Da die Zeit knapp wird, suchten wir im Internet nach einfacheren Lösungen um Klagenfurt zu modellieren. Wenige Minuten später fanden wir auch schon eine passende Lösung. Dazu brauchten wir ein einfaches Programm (3DVia Printscreen) welches alle 3d-Gebäude von Google Earth in eine 3DXML Datei speichern kann. Aber leichter gesagt als getan.
 
Zuerst versuchten wir es auf einem etwas älteren Laptop, doch schon nach kurzer Zeit merkten wir, dass etwas nicht stimmte. Denn bei dem Tutorial dauerte dieser Vorgang nur ca. 10 Sekunden. Also installierten wir die benötigten Programme auf einem stärkeren Laptop, welchen wir auch für den Assetto Corsa Editor verwenden. Nach mehreren Versuchen meisterten wir auch dieses Problem aber das nächste ließ nicht auf sich warten.
 
Als wir endlich die fertige 3DXML Datei auf unserem Desktop vorgefunden hatten, merkten wir, dass weder Sketchup noch Blender dieses Dateiformat öffnen konnte. Also baten wir Google um Hilfe, doch auch die Suchmaschine konnte uns keine brauchbaren Ergebnisse liefern. Also entschieden wir uns doch, Klagenfurt per Hand zu modellieren.

 
 

Because we are slowly running out of time, we searched for easier ways to design Klagenfurt. A few minutes later we came up with a solution. For this we just needed a program which is able to save all 3D buildings of Google Earth into a 3DXML file. But easier said than done.
 
First we tried it out with an older notebook, but it was too slow. That’s why we tried it on a newer notebook which we also use for the Assetto Corsa Editor later. After several attempts we managed to save the 3DXML file, but there we were already standing in front of a new problem:
 
We could not open the file in SketchUp or Blender. So it was completely useless for us and we decided to design Klagenfurt by hand.

Es ist ein schöner Sommertag mit gefühlten 40 Grad Celsius. Perfekte Bedingungen um im kühlen Keller den Fahrsimulator zu testen, dachten sich unsere Gäste aus London. Wir waren sehr knapp in der Zeit, haben unsere Strecke durch Klagenfurt aber rechtzeitig fahrbereit gemacht. Deshalb konnten die Schüler aus London, ihre erste Fahrt in einem 500 PS Boliden durch Klagenfurt machen. Außerdem machten wir einige Tests mit dem Ergebnis, dass es ganz zu unseren Gunsten nicht mehr möglich ist, durch Wände zu fahren.

Im Moment fehlen noch einige Gebäude und man legt den Weg von den City Arkaden zum Hauptbahnhof in wenigen Sekunden zurück, doch es ist uns gelungen einen ersten Eindruck zu vermitteln. In der nächsten Woche werden wir weitere Gebäude hinzufügen und unsere Strecke auf ein realistisches Maß vergrößern.

 

Visit from London

It’s a beautiful summer day in what felt like 40 degrees. The perfect weather to test the driving simulator in the cool cellar. We finished our race course through Klagenfurt right in time and our guests from London were able to make their first ride in a 500 hp race car. Moreover we made some tests with the result: it’s not possible anymore to drive through walls.
At the moment many buildings are missing and the way from the City Arkaden to the rail station is too short, but we were able to show them the base frame of our race course. Next week we are going to add more buildings and make our track larger.

Heute präsentieren wir euch unseren Workflow und danach die ersten Probleme, mit denen wir konfrontiert wurden.

.
Workflow: Zunächst wird die Rennstrecke im Sketch Up Editor erstellt. Anschließend werden verschiedene Szeneobjekte und Texturen hinzugefügt. Sobald das Grundgerüst der Strecke erstellt wurde, exportieren wir das 3D Modell in den ksEditor, der genau nach Assetto Corsas Wünschen angepasst ist. Danach werden die Texturgrößen und Shader angepasst.
Doch nun zum weniger erfreulichen Teil unserer Arbeit.

 

Probleme: Meistens passen die Texturgrößen des exportierten 3D Modells nicht im ksEditor. Das ist noch recht einfach zu lösen, da man die Größen sehr einfach anpassen kann. Oft passiert es, dass unser Rennwagen an einer falschen Stelle oder ohne Reifen startet. Das liegt dann meistens an der falschen Benennung der Ordner und Objekte oder daran, dass der Startpunkt der Strecke nicht die Koordinaten (0/0/0) besitzt, ist aber noch einfach zu korrigieren. Schwieriger wird es, wenn der Editor die x-, y- & z-Achse des 3D Modells falsch benennt und unsere Rennstrecke plötzlich auf dem Kopf steht.

 

Es ist Mittwoch und schon morgen kommt eine Schülergruppe aus England, um sich unseren Fahrsimulator anzusehen. Hoffentlich haben wir unsere Teststrecke bis dahin fertig gestellt, es bleibt spannend!

 

Today we present you our workflow and the first problems we had to deal with while working on our project.

 

Workflow: First we create a race course in the Sketch Up Editor. Then we add various objects and textures. After that we export the base frame of our 3D model in the ksEditor, which is made for Assetto Corsa. The next step is to adapt the size of the objects and textures.
However, we already had to solve several problems.

 

Problems: Most of the time the size of the objects doesn’t fit. But we are able to adapt the size in the ksEditor and it’s pretty easy to change. It happens many times that our car doesn’t spawn at the right start point or our car has no tires. However, this is a problem which can be easily fixed too, because we named the files wrong or the start point hasn’t the coordinates (0/0/0). But it gets pretty difficult if the ksEditor doesn’t recognize the x-, y- & z-axis of the 3D model, so that our race course is turned upside down.

 

It’s already Wednesday and tomorrow a school group visits us to test the driving simulator. Hopefully we finish our race course until tomorrow.

 

 

Sehr geehrte Damen und Herren,

unsere Namen sind: Stefan Schönhöffer (Peraugymnasium Villach), Daniel Hagelmayer (Borg Klagenfurt), Harald Tributsch (HTL Mössingerstraße) und Fabrizio Makula (BG Tanzenberg). Am 13.7 2015 starteten wir unser Ferialpraktikum an der Alpen-Adria-Universität Klagenfurt im Lakesidepark.

In diesem Projekt versuchen wir eine 3D-Verkehrslandschaft zu modellieren und diese anschließend in einem 20.000 Euro Fahrsimulator zu befahren. Nun ist eine Woche vergangen, in dieser Zeit haben wir uns bereits mit dem Programm Sketch-Up vertraut gemacht und zeichneten eine kleine Teststrecke. Außerdem haben wir 3D-Objekte mit Texturen versehen, damit unsere Strecke realistisch wirkt. Um diese Strecke im Fahrsimulator befahren zu können, müssen wir diese nur noch in den Assetto-Corsa Editor importieren und die Shader anpassen. Aber unser Ziel ist noch lange nicht erreicht, denn wir wollen eine Rennstrecke durch die Stadt Klagenfurt bauen. Während der Fahrt wird man die wichtigsten Gebäude, unter anderem auch die Alpen-Adria-Universität, die detailgetreu im Maßstab 1:1 nachgebaut wurde, sehen.

©Fabrizio Makula

 

Ladies and Gentlemen,

our names are: Stefan Schönhöffer (Peraugymnasium Villach), Daniel Hagelmayer (Borg Klagenfurt), Harald Tributsch (HTL Mössingerstraße) and Fabrizio Makula (BG Tanzenberg). On the 13th July 2015 we started our holiday work placement at the Alpen-Adria University in Klagenfurt.

In this project we are trying to build a 3D landscape in which we are able to drive with a driving simulator. Now a week passed by and we already made progress. We learned how to work with a 3D program called Sketch-Up and made our first course. In order to drive on the course we have to import it in another 3D program called Assetto-Corsa Editor. Nevertheless, our goal is to build a race course through the city of Klagenfurt. Further we will add important buildings and the University of Klagenfurt to the landscape.

 

 

WAS? WIE? WARUM?

In der nachfolgenden Dokumentation werden wir versuchen Ihnen einen kurzen Überblick über unser Projekt zu geben. Wir beschäftigten uns im Grunde mit der Kommunikation zwischen einem Auto und einem Android Smartphone. Unser Ziel war es relevante Daten aus einem Auto mit Hilfe eines Arduino und eines LCD-Displays darzustellen. Im weiteren Verlauf wurde das LCD-Display durch eine Android App ersetzt, welche mit einer Bluetooth Verbindung angesteuert wurde. Fahrzeuge verfügen in der heutigen Zeit über ein großes Repertoire an computergestützter Hilfsmittel. Alles ist darauf ausgelegt dem Fahrer/ der Fahrerin das Fahren so angenehm und sicher wie möglich zu machen. Systeme wie ABS, ESP, ASR und viele mehr benötigen einen hohen technischen Aufwand. Diese Dinge sind für uns selbstverständlich und kaum mehr wegzudenken. Um technische Daten wie die Motorauslastung, Motortemperatur, Motordrehzahl und die Geschwindigkeit auf ein externes Gerät auslesen zu können, muss eine Verbindung mit der Diagnosebuchse im Auto bestehen.

Bilder




Dokumentation

 

LCD – Display


Im ersten Versuch wurden die Daten / Informationen auf ein LCD – Display ausgelesen. Um dies jedoch zu ermöglichen musste zuerst die Pinbelegung vom Arduino – Board zum LCD – Display neu angeordnet werden um eine korrekte Anzeige zu ermöglichen. Für die Entwicklung der Software wurde die „Arduino IDE“ heran gezogen. Die Kommunikation erfolgte vom Auto zum Arduino über das CANdiy – Shield. Bei diesem jedoch, brannte der Chip in den ersten Testversuchen durch, wahrscheinlich wegen eines Kurzschlusses. Blöderweise hatten wir kein weiteres CANdiy – Shield und mussten uns deshalb einen neuen bzw. anderen Lösungsweg überlegen.

Android App


Die Hauptaufgabe dieser App besteht darin die wichtigsten Daten (die aus dem Auto ausgelesen werden) auf einer grafischen Benutzeroberfläche darzustellen. Weiters wurde eine Funktion hinzugefügt die es dem Benutzer ermöglicht gewisse Daten auszuwählen die er anzeigen lassen will. Hierfür benötigen wir eine Bluetooth Verbindung zu einem Android Smartphone, welche über eine Android Smartphone, welche über eine Java-Applikation am Laptop, aufgebaut wird.




Hier sämtlicher Source Code:


Sourcecode

Mit diesem Programm wurde der erste Testlauf durchgeführt.

Zuvor mussten jedoch einige Änderungen vorgenommen werden. Sowohl das CANdiy Shield, als auch das LCD-Display verwenden die selben Pins am Arduino Board. Wir beseitigten dieses Problem durch das Umlöten einiger Pins.

Dieser Testversuch lief jedoch leider nicht so ab wie wir es geplant hatten. Aufgrund eines Kurzschlusses brannte der Chip am CANdiy Shield durch und es war keine Kommunikation mehr mit dem Auto möglich.

Hier unser GUI-Design:

Und hier unser Source Code:

#include 
#include 
 
ObdInterface obd;
ObdMessage msg;
 
LiquidCrystal lcd(11,9,4,5,6,3);
 
int modeHorizontal = 0;
int modeVertical = -1;
 
String drivingMenu[][3] = {
                            {"Speed", " km/h","0"},
                            {"Engine RPM", " RPM","1"},
                            {"Engine Temp.", " °C","0"},
                            {"Turbo. Temp. 1", " °C","0"},
                            {"Turbo. Temp. 2", " °C","0"},
                            {"Turbo. RPM", " RPM","0"}
                          };
int drivingMenuPids[] = {0x0d,0x0c,0x05,0x75,0x76,0x74};
boolean drivingMenuPidsSupported[] = {false,false,false,false,false,false};
 
String generalMenu[][3] = {
                            {"Codes del. since", " km","0"},
                            {"Fuel", " %","1"},
                          };
int generalMenuPids[] = {0x31,0x2F};
boolean generalMenuPidsSupported[] = {false,false};
 
void setup()
{
  lcd.begin(16,2);
  lcd.print("Welcome!");
  lcd.setCursor(0,1);
  lcd.print("Starting...");
 
  Serial.begin(9600);
  Serial.println("Probing Bus settings...");
  //probing bus settings
  if (probe(false,false))
  {
    obd.setSlow(false);
    obd.setExtended(false);
    Serial.println("Bus settings found: false false");
  }
  else
  {
    if (probe(true,false))
    {
      obd.setSlow(true);
      obd.setExtended(false);
      Serial.println("Bus settings found: true false");
    }
    else
    {
      if (probe(false,true))
      {
        obd.setSlow(false);
        obd.setExtended(true);
        Serial.println("Bus settings found: false true");
      }
      else
      {
        if (probe(true,true))
        {
          obd.setSlow(true);
          obd.setExtended(true);
          Serial.println("Bus settings found: true true");
        }
        else
        {
          lcd.clear();
          lcd.setCursor(0,0);
          lcd.print("No Connection!");
        }
      }
    }
  }
 
  obd.begin();
 
  //check which pids are supported
  for (int i = 0;i<sizeof(drivingMenuPidsSupported);i++)
  {
    boolean supported;
    obd.isPidSupported(drivingMenuPids[i],supported);
    if (supported)
      drivingMenuPidsSupported[i] = true;
  }
  for (int i = 0;i<sizeof(generalMenuPidsSupported);i++)
  {
    boolean supported;
    obd.isPidSupported(generalMenuPids[i],supported);
    if (supported)
      generalMenuPidsSupported[i] = true;
  }
}
 
void loop()
{
  lcd.clear();
  if (analogRead(A0)<20)    //right button
  {
    if (modeHorizontal <2)       modeHorizontal++;     else       modeHorizontal = 0;     modeVertical = -1;   }   if (analogRead(A0)>380 && analogRead(A0)<430)    //right button   {     if (modeHorizontal >0)
      modeHorizontal--;
    else
      modeHorizontal = 2;
    modeVertical = -1;
  }
  switch (modeHorizontal)
  {
    case 0:    //Driving values
      if (analogRead(A0)>220&&analogRead(A0)<300)    //down button
      {
        if (modeVertical<sizeof(drivingMenuPidsSupported)-1)         {           modeVertical++;           while (!drivingMenuPidsSupported[modeVertical])           {             if (modeVertical>=sizeof(drivingMenuPidsSupported))
              modeVertical = 0;
            modeVertical++;
          }
        }
        else
        {
          modeVertical=0;
        }
      }
      else if (analogRead(A0)<150&&analogRead(A0)>80)  //up button
      {
        if (modeVertical>0)
        {        
          modeVertical--;
          while(!drivingMenuPidsSupported[modeVertical])
          {
            if (modeVertical>0)
              modeVertical--;
            else
            {
              modeVertical = -1;
              break;
            }
 
          }
        }
        else
        {
          modeVertical = -1;
        }
      }
      if (modeVertical == -1)
      {
        lcd.setCursor(0,0);
        lcd.print("Driving values");
        lcd.setCursor(0,1);
        lcd.print("Press Down...");
      }
      else
      {
        lcd.setCursor(0,0);
        lcd.print(drivingMenu[modeVertical][0]);
        lcd.setCursor(0,1);
 
        if (generalMenu[modeVertical][2] == "0")
        {
          word value = 0;
          Serial.println("Calling " + drivingMenu[modeVertical][0]);
          if (!obd.getPidAsInteger(drivingMenuPids[modeVertical],value))
           value = 666;
           lcd.print(value);
           lcd.print(drivingMenu[modeVertical][1]);
        }
        else if (drivingMenu[modeVertical][2] = "1")
        {
//          Serial.println("Calling " + drivingMenu[modeVertical][0]);
          float value = 0;
          if (!obd.getPidAsFloat(drivingMenuPids[modeVertical],0.0f,16383.75f,value))
            value = 666;
          lcd.print(value);
          lcd.print(drivingMenu[modeVertical][1]);
        }
      }
    break;
 
    case 1:  //general infos
    if (analogRead(A0)>220&&analogRead(A0)<300)    //down button     {         if (modeVertical >= sizeof(generalMenuPidsSupported)-1)
          modeVertical = 0;
        else
          modeVertical++;
        while (!generalMenuPidsSupported[modeVertical])
        {
          if (modeVertical>=sizeof(generalMenuPidsSupported)-1)    
          {
            modeVertical = 0;
          }
          else
          {
            modeVertical++;
          }
        }
    }
    else if (analogRead(A0)<150&&analogRead(A0)>80)  //up button
    {
            if (modeVertical == 0)
        modeVertical = -1;
      else
      {
      if (modeVertical>0)
      {        
        modeVertical--;
        while(!generalMenuPidsSupported[modeVertical])
        {
          if (modeVertical>0)
            modeVertical--;
          else
          {
            modeVertical = -1;
          }
        }
      }
      }
    }
    if (modeVertical == -1)
    {
      lcd.setCursor(0,0);
      lcd.print("General infos");
      lcd.setCursor(0,1);
      lcd.print("Press Down...");
    }
    else
    {
      lcd.setCursor(0,0);
      lcd.print(generalMenu[modeVertical][0]);
      lcd.setCursor(0,1);
      if (generalMenu[modeVertical][2] == "0")
      {
        word value = 0;
        //Serial.println("Calling " + generalMenu[modeVertical][0]);
        if (!obd.getPidAsInteger(generalMenuPids[modeVertical],value))
         value = 666;
         lcd.print(value);
         lcd.print(generalMenu[modeVertical][1]);
      }
      else if (generalMenu[modeVertical][2] = "1")
      {
//        Serial.println("Calling " + generalMenu[modeVertical][0]);
        float value = 0;
        if (!obd.getPidAsFloat(generalMenuPids[modeVertical],0.0f,16383.75f,value))
          value = 666;
        lcd.print(value);
        lcd.print(generalMenu[modeVertical][1]);
      }
    }
    break;
 
    case 2:  //errors
    if (modeVertical == -1)
    {
      lcd.setCursor(0,0);
      lcd.print("Errors");
      lcd.setCursor(0,1);
      lcd.print("Press Down...");
    }
    break;
  }
  delay(125);
}
 
boolean probe(boolean slow, boolean extended) {
  ObdInterface obd;
  boolean dummy;
 
  obd.setSlow(slow);
  obd.setExtended(extended);
 
  obd.begin();
  boolean result = obd.isPidSupported(1, dummy);
  obd.end();
 
  delay(250);
 
  return result;
}

Die Ansteuerung für das LCD-Display, auf der später Informationen wie Motorauslastung und Turbodruck angezeigt werden, ist fertig. Zum Test haben wir ein kleines Spiel für das 16*2 Display programmiert.

Wenn jemand Lust hat es auszuprobieren, hier ist der Sourcecode:

 

 

/*
Autoren: Daniel Wuggenig & Michelle Kilzer
Datum: 09.07.2014 11:35
Beschreibung: Minispiel bei dem man Gegenständen ausweichen muss.
Blöcke spawnen random und kommen von links nach rechts.
Zwischen den Blöcken ist mindestens 2 Blöcke nichts.
Kommt man bei einem Block an, hat man verloren.
*/
 
#include <LiquidCrystal.h>  //Bibliothek für Display Ansteuerung
 
LiquidCrystal lcd(8,9,4,5,6,7);  //Pinbelegung Display
 
boolean brick[16][2];    //2D-Array das speichert, ob sich an jeder Position ein Block befindet oder nicht. True=ein Block, False = kein Block
 
boolean player = true;  //True = Player ist in der oberen Reihe, False = Player ist in der unteren Reihe
 
void setup() {
	lcd.begin(16, 2);  //16*2 Display initialisieren
	lcd.print("Starting...");  //Start auf Display ausgeben
 
	for (int i = 0;i<16;i++)  //ganzes 2D-Array auf false setzen (kein Block am Anfang)
	{
		for (int j = 0;j<2;j++)
		{
			brick[i][j] = false;
		}
	}
}
int modTime = 100;    //Gibt die Anzahl der Durchläufe an
boolean lost = false;  //False = nicht verloren, True = verloren (Spielende)
 
void loop() {
	if (!lost)  //Überprüfen ob das Spiel aus ist
	{
		modTime++;  //modTime um eins erhöhen, da neuer Durchlauf
		delay((1/((float)modTime/100))*250);  //delay abhängig wie lange das Spiel bereits läuft
		if (modTime%2 == 0)  //in jeden 2. Durchlauf werden die Blöcke eine Position nach rechts verschoben
		{
			for (int i = 15;i>=0;i--)
			{
				for (int j = 0;j<2;j++)
				{
					brick[i][j] = brick[i-1][j];  //Blöcke nach rechts verschieben
				}
			}
			brick[0][0] = false;  //in der ersten Spalte sind keine Blöcke, da sie gerade nach rechts verschoben wurden
			brick[0][1] = false;
		}
		if (modTime%6==0)  //Alle 6 Durchläufe wird ein neuer Block gespawnt
		{
			int rN = random(2);  //zufällige Zahl, die angibt ob der Block in der oberen oder unteren Zeile landet.
			brick[0][rN] = true;  //erzeugt den Block
		}
		lcd.clear();    //Display wird gecleared, damit keine Übereste von alten Blöcken übrig bleiben
		for (int i = 0;i<16;i++)  // Array wird durchgegangen, wenn true wird ein block gezeichnet
		{
			for (int j = 0;j<2;j++)
			{
				lcd.setCursor(i,j);
				if (brick[i][j])
					lcd.print((char)219);  //Block wird gezeichnet (█)
			}
		}
		if (analogRead(A0)<110&&analogRead(A0)>90)  //Wenn der obere Knopf gedrückt wird
			player = true;    //Player wird auf die obere Zeile gesetzt
		else if (analogRead(A0)>240 && analogRead(A0)<270)  //Wenn der untere Knopf gedrückt wird
			player = false;  //Player wird auf die untere Zeile gesetzt
		if (player)  //Spieler zeichnen
		{
			lcd.setCursor(13,0);
			lcd.print((char)181);
			if (brick[13][0])  //Wenn an der Spielerposition ein Block ist, ist das Spiel aus
			{
				lost = true;
				lcd.clear();
			}
		}
		else
		{
 
			lcd.setCursor(13,1);  //Gleich wie oben nur für untere Zeile
			lcd.print((char)181);
			if (brick[13][1])
			{
				lost = true;
				lcd.clear();
			}
		}
	}
	else    //Spiel verloren
	{
		lcd.setCursor(0,0);
		lcd.print("Highscore:");    //Highscore ausgeben
		lcd.print(((modTime-100)/6)-4);  //1 Punkt für jeden Block, -4 weil am Anfang keine Blöcke sind
	}
}

Hauptpost – Klick!

 

Changelog:

  • Es liegen jetzt Treiber für sowohl den NXT, als auch den RFID Reader bei.
  • Die Programme wurden überarbeitet, um den NXT responsiver zu machen.
  • Bugfixes

Download v5.0

 

Die neuen Codes:

Java PC-Programm:

//Written by Peter Por
//Last edited: 30.07.13
 
package nxtrfid;
 
import lejos.pc.comm.*;
import java.io.*;
 
 
public class NXTRFID {	
 
  public static void main(String[] args) throws IOException, InterruptedException 
  {
	  String nxtName = "NXT";   //Name of the specific NXT brick; "NXT" is default
	  int COMPort = 8;      //The COM-Port of the RFID Reader	
	  String RFIDScanLocation = "RRFIDScan_v.2.0.1.exe";    //The Location of the ".exe" file for the RFID Reader
	  String Music = "vlc/vlc.exe --play-and-exit --novideo Music.mp3";     //Commandline promt opening VLC and the sound file
	  String InputLocation = "NXTRFID.conf";    //Location of the config file
	  int keypressOld = 9999;   //the last read RFID tag's ID will be saved here
 
	  //You may change the location of the conf file by sending a command line argument
	if(args.length!=0)
	{
		InputLocation = args[0];
	}
 
	//Read the config file
	try 
	{
		BufferedReader in = new BufferedReader(new FileReader(InputLocation));
		String line1 = in.readLine();
		String line2 = in.readLine();
		String line3 = in.readLine();
		String line4 = in.readLine();
 
		nxtName = line1;
		COMPort = Integer.parseInt(line2);
		RFIDScanLocation = line3;
		Music = line4;
 
		in.close();
	} 
	catch (IOException err) 
	{
		err.printStackTrace();
	}
 
    //Necessary declarations for establishing the connection
    NXTInfo[] nxts;
    NXTConnector connect = new NXTConnector();
 
    nxts = connect.search(nxtName,null,NXTCommFactory.BLUETOOTH);    //Searches for nearby NXTs
 
    if(nxts.length == 0)
    { //The NXT was not in range
      System.out.print(nxtName + " not found!");
    }
    else{
      if(!connect.connectTo(nxts[0].name,nxts[0].deviceAddress,NXTCommFactory.BLUETOOTH))
      { //Connection failed
        System.out.println("Connecting to " + nxtName + " failed!");
      }
      else
      { //now there is a connection
        System.out.println("Successfully connected to " + nxtName + " !");
        DataOutputStream dos = new DataOutputStream(connect.getOutputStream());
 
        while(true){
         try
         {
          Process p = Runtime.getRuntime().exec(RFIDScanLocation + " " + COMPort);   //This declaration starts the ".exe" file for the RFID Reader; The number following specifies the COM port
          p.waitFor();    //Waits until the ".exe" has finished
          dos.writeInt(p.exitValue());    //Writes the exitValue of the RFIDScan program to the NXT
          dos.flush();
 
          //Starts VLC and opens the sound file - 3 is the Code for "Dance"
          if(p.exitValue()==3 && p.exitValue() != keypressOld)
          {
        	  Thread.sleep(10);
        	  try
        	  {
        		 Runtime.getRuntime().exec(Music);
        	  }
        	  catch(Exception ex)
        	  {
  		        System.out.println("Error with playing sound.");
  		        ex.printStackTrace();
  		      }
          }
 
          if(p.exitValue()==-1)   // -1 is the code for "Terminate"
          {
        	  System.out.println("The Program will now shut down.");
        	  break;
          }
          if(p.exitValue()==-2)   // -2 is the Error-Code
          {
        	  System.out.println("Error reading the RFID Tag.");
        	  break;
          }
 
          keypressOld=p.exitValue();
         }
         catch(Exception err)
         {
        	 System.out.println("An unknown Error occured.");
        	 break;
         }
        }// end of loop       
 
    	dos.close();    //Closes the DataOutputStream
        connect.close();    //Closes the Connection to the NXT
        System.out.println("Program was terminated successfully.");
      }
     }
 
    }// end of main
   } // end of class Main

C++ PC-Programm (RFID):

// RFIDScan.cpp : Defines the entry point for the console application.
// Written by Andreas Mieke
// Date: 30.07.13
 
#define VERSION "3"
//#define DEBUG		//Uncomment this to see what is scanned from RFIDScan.conf and what is the TagID on the scanner
#define MAX_TAGS (20)	//Defines the maximum number of RFID tags to be scanned
 
#include "std_inc.h"
#include "comm.h"
#include "protocol.h"
#include "std_readwrite.h"
 
//General purpose buffer
char buffer[1000];
char code[17];
int tagCounter = 0;
int tagCounterMax = 0;
char tagIDs[17][MAX_TAGS];
int tagReturns[MAX_TAGS];
 
//This function closes the reader and communication handle
//If not connected the two variables are set to zero
void CloseComm() {
    //If the reader handle is set, close the reader
    if(reader_handle) {
        RDR_CloseReader(reader_handle);
        reader_handle = NULL;
    }
 
    //If the COM handle is set, close the COM port
    if(comm_handle) {
        RDR_CloseComm(comm_handle);
        comm_handle = NULL;
    }
}
 
bool OpenComm() {
    char input_buffer[50], detect_buffer[256];
 
     //Close any possible open connection
    CloseComm();
 
    comm_handle = RDR_OpenComm(com_port, 1, NULL);
 
    if(comm_handle == 0) { //zero indicates error while opening the com port
        printf("\n\nError opening reader(com).\n");
 
        return false;
    }
 
    //After sucessfully opening the com port, try to open the reader
 
    //First detect the station ID of the reader
    RDR_DetectReader(comm_handle, detect_buffer);
 
    //Open the reader with the first detected station ID
    reader_handle = RDR_OpenReader(comm_handle, detect_buffer[0], 0);
 
    if(reader_handle == 0) { //zero indicates error while opening reader
        //Since the com port is allready open, close it on error contition
        CloseComm();
 
        printf("\n\nError opening reader.\nPress any key to continue...");
        getch();
 
        return false;
    }
 
    //Stop the maybe activated continous read (should only be done after opening the reader)
    //RDR_EmptyCommRcvBuffer is not needed because this is implicit done with RDR_AbortContinuousRead
    RDR_AbortContinuousRead(comm_handle);
    return true;
}
 
//This function expalains the standard read/write sequence for a standard MIFARE card [ASCII]
//If the default key is not valid you have to change the line in the source code below
void DoASCIIReadWrite() {
    //The first action is to select the card
    //The select command has no data
    RDR_SendCommandGetData(reader_handle, "select", "", buffer);
	sprintf(code, "%s", buffer);
	strcpy(buffer,"");
}
 
//Main programm entry point
int main(int args, char* argv[]) {
	char* filepath;
	if(args < 2)
	{
		printf("[ERROR]\tPlease provide the right COM port <name_of_file.exe {portnumer} [path_to_config]>\n");
		return -2;
	}
	printf("RFIDScan Version: %s; Coded by Andreas Mieke\n\n[JOB]\t\tLoading config file...\n\n",VERSION);
	if(argv[2] != NULL)
		filepath = argv[2];
	else
		filepath = "RFIDScan.conf";
	FILE *configFile = fopen(filepath,"r");
	if(configFile != NULL)
	{
		fgets(buffer, sizeof(buffer), configFile);
		tagCounterMax = atoi(buffer);
		if(tagCounterMax > MAX_TAGS)
		{
			printf("[ERROR]\tYour specification of tags has too much elements! Max: %d",MAX_TAGS);
			return -3;
		}
		if(tagCounterMax < 1)
		{
			printf("[WARNING]\tFailed to read the config file (maybe because encoding is not ANSI?)");
			return -6;
		}
		for(; tagCounter < tagCounterMax; tagCounter++)
		{
			fscanf(configFile,"%s%d", tagIDs[tagCounter], &tagReturns[tagCounter]);
#ifdef DEBUG
			printf("[DEBUG]\t%s : %d\n", tagIDs[tagCounter], tagReturns[tagCounter]);
#endif
		}
		fclose(configFile);
		printf("[SUCCESS]\tConfig file successfully loaded. Program started.\n\n");
	}
	else
	{
		printf("[FATAL ERROR]\tConfig file \"%s\" could not be opened!\n", filepath);
		return -4;
	}
	sprintf(com_port,"COM%d",atoi(argv[1])); //Sets the content of argv[1] as COM port
 
	if(OpenComm() == false) //Opens the connection to the RFID reader (comm.cpp)
	{
		printf("[FATAL ERROR]\tCould not connect to the RFID Reader!\n");
		return -5;
	}
 
	do { //Continues until a valid ID was scanned.
		do { //Tries as long to scan a Tag as it doesn't find one.
			DoASCIIReadWrite(); // (std_readwrite.cpp)
		} while ((strlen(code) == 0) || (strlen(code) == 1)); //Strlen of 0 or 1 indicates error
 
#ifdef DEBUG
		printf("[DEBUG] %s\n",code); //Prints the HEX-Code (TAGID) to the screen
#endif
 
		for(tagCounter = 0; tagCounter < tagCounterMax; tagCounter++)
		{
			if(strcmp(code,tagIDs[tagCounter]) == 0)
			{
#ifdef DEBUG
				printf("%s return %d\n",tagIDs[tagCounter],tagReturns[tagCounter]);
#endif
				CloseComm(); //Closes connection to Reader (comm.cpp)
				return tagReturns[tagCounter];
			}
#ifdef DEBUG
			else
			{
				printf("no match\n");
			}
#endif
		}
		strcpy(code,"");
	} while(true);
}
//EOF

Java NXT-Programm:

//Written by Peter Por
//Last edited: 30.07.13     
 
import java.io.IOException;
import lejos.nxt.*;
import lejos.nxt.comm.*;
import java.io.*;
import javax.bluetooth.*;
import lejos.util.Delay;
import lejos.nxt.UltrasonicSensor;
 
 
 
public class BluetoothNXT_v5 {
 
  //globally accessable variables
  public static int keypress = 0, keypressOld = 9999;
  public static boolean mayDriveForward=true, mayDriveBackward = true, clawsOpen=false;
 
  public static void main(String [] args) throws Exception
  {
    String connected = "Connected";
    String waiting = "Waiting...";
    String closing = "Closing...";
 
    //Starting the Thread which controls the ultrasonic sensors
    Indicators ussf = new Indicators();
    ussf.setDaemon(true);
    ussf.start();
 
    //Waiting for, and establishing, the bluetooth connection
    LCD.drawString(waiting,0,0);
    LCD.refresh();
    BTConnection btc = Bluetooth.waitForConnection();
    LCD.clear();
    LCD.drawString(connected,0,0);
    LCD.refresh();
    Sound.beepSequenceUp();
    DataInputStream dis = btc.openDataInputStream();
    DataOutputStream dos = btc.openDataOutputStream();
    ////////////////////////////////////////////////////////////////////////////  
    mayDriveForward=true;
    mayDriveBackward=true;
 
 
    while(true)
    {//start loop
 
      try{//waiting for the order from the PC program
        keypress=dis.readInt();
      }
      catch(IOException e)
      {
        LCD.drawString("Error:",0,0);
        LCD.drawString("Datastream Invlaid.",0,1);
        LCD.refresh();
        break;
      }
      Thread.sleep(100);
 
 
      //Forward
      if (keypressOld!=8 && keypress ==8 && mayDriveForward) 
      {
        Motor.A.setSpeed(1200);
        Motor.C.setSpeed(1200);
        Motor.A.forward();
        Motor.C.forward();
        mayDriveBackward=true;
      } // end of if
      else
      //Backward
      if (keypressOld!=2 && keypress ==2 && mayDriveBackward) 
      {
        Motor.A.setSpeed(1200);
        Motor.C.setSpeed(1200);
        Motor.A.backward();
        Motor.C.backward();
        mayDriveForward=true;
      } // end of if
      else
      //Turn right
      if (keypressOld!=6 && keypress ==6) 
      {
        Motor.A.setSpeed(150);
        Motor.C.setSpeed(150);
        Motor.A.forward();
        Motor.C.backward();
        mayDriveForward=true;
        mayDriveBackward=true;
      } // end of if
      else
      //Turn left
      if (keypressOld!=4 && keypress ==4) 
      {         
        Motor.A.setSpeed(150);
        Motor.C.setSpeed(150);
        Motor.A.backward();
        Motor.C.forward();
        mayDriveForward=true;
        mayDriveBackward=true;
      } // end of if
      else
      //Stop
      if (keypress ==5) 
      {
        if (keypressOld==8||keypressOld==2)
        SoftStop();
        Motor.A.stop();
        Motor.C.stop();
      } // end of if
      else
      //Close Claws
      if (keypressOld!=keypress && clawsOpen && keypress==9) 
      {
        if (keypressOld==8||keypressOld==2)
        {SoftStop();}
        Motor.A.stop();
        Motor.C.stop();
        Motor.B.setSpeed(100);
        Motor.B.backward();
        Thread.sleep(1000);
        Motor.B.stop();
        clawsOpen=false;
      } // end of if
      else
      //Dance like nobody's watching
      if (keypressOld!=3 && keypress==3) 
      {
        SoftStop();
        Motor.A.stop();
        Motor.C.stop();
        if (!clawsOpen) 
        {                               
          Motor.B.setSpeed(100);
          Motor.B.forward();
          Thread.sleep(1000);
          Motor.B.stop();
          clawsOpen=true;
        }
        Dance.startDance();
      } // end of if  
      //Open claws
      else
      if (true) 
      {
        if (keypressOld!=keypress && !clawsOpen && keypress==7) 
        {
          if (keypressOld==8||keypressOld==2)
          SoftStop();
          Motor.A.stop();
          Motor.C.stop();
          Motor.B.setSpeed(100);
          Motor.B.forward();
          Thread.sleep(1000);
          Motor.B.stop();
          clawsOpen=true;
        } // end of if   
        mayDriveForward=true;
      }      
      keypressOld=keypress;
    }//end loop
 
 
    //Terminating the program
 
    //closes claws in case they were open
    if (clawsOpen) 
    {                               
      SoftStop();
      Motor.A.stop();
      Motor.C.stop();
      Motor.B.setSpeed(100);
      Motor.B.backward();
      Thread.sleep(1000);
      Motor.B.stop();
      clawsOpen=false;
    }
 
    //closes the bluetooth connection
    dis.close();
    dos.close();
    Thread.sleep(90); // wait for data to drain
    LCD.clear();
    LCD.drawString(closing,0,0);
    LCD.refresh();
    btc.close();
    LCD.clear();
  }
 
 
  //Due to a hardware problem regarding the NXT's motors, this method was necessary in order to stop both motors at the same time
  static void SoftStop()
  {
    int i = 1200;
    while(i>0)
    {
      Motor.A.setSpeed(i);
      Motor.C.setSpeed(i);
      Delay.msDelay(10);
      i=i-100;
    }
  }
}
 
 
//Ultrasonic-Thread
class Indicators extends Thread 
{
  //Declaring the sensor-ports
  UltrasonicSensor sonicF = new UltrasonicSensor(SensorPort.S1);
  UltrasonicSensor sonicB = new UltrasonicSensor(SensorPort.S2);
  int safetyDistanceF=30, safetyDistanceB=27;
 
  //Workerthread
  public void run() 
  {
    while (true) 
    {
      //If the claws are open, the safety-distance is smaller
      if (BluetoothNXT_v5.clawsOpen)
      safetyDistanceF=23;
      else
      safetyDistanceF=30;
 
 
      try
      {
        if (sonicF.getDistance()<safetyDistanceF && BluetoothNXT_v5.keypress==8) 
        {//The NXT is currently driving forwards and faces an obstacle
          Motor.B.stop();
          BluetoothNXT_v5.SoftStop();
          Motor.A.stop();
          Motor.C.stop();       
          BluetoothNXT_v5.mayDriveForward = false;                  
        }
      }      
      catch(Exception err)
      { }
 
 
      try
      {
        if (sonicB.getDistance()<safetyDistanceB && BluetoothNXT_v5.keypress==2) 
        {//The NXT is currently driving backwards and faces an obstacle
          Motor.B.stop();
          BluetoothNXT_v5.SoftStop();
          Motor.A.stop();
          Motor.C.stop();     
          BluetoothNXT_v5.mayDriveBackward = false;                  
        }
      }      
      catch(Exception err)
      { }     
    }     
  }
}
 
 
class Dance {
 
  public static void startDance()
  {
    try{
      //Declaring the sensor-ports
      UltrasonicSensor sonicF = new UltrasonicSensor(SensorPort.S1);
      UltrasonicSensor sonicB = new UltrasonicSensor(SensorPort.S2);
      //Getting the current time
      long startTime = System.currentTimeMillis();
      Delay.msDelay(20);
      long endTime = System.currentTimeMillis();
 
      //The program may only run for 29seconds (30seconds is the duration of the music we chose, the last second is used to close the claws and beep)
      while (endTime-startTime < 29000) 
      { 
        //Set the motor-speed to a random number between 20 and 400
        Motor.A.setSpeed(myRandom(100,400));
        Motor.C.setSpeed(myRandom(100,400));
 
        //If the NXT is facing an obstacle, the the motors are only allowed to turn backward
        if (sonicF.getDistance()<50)
        { 
          Motor.A.backward();
          Motor.C.backward();   
          Delay.msDelay(100);
        }
        //If the NXT's back is facing an obstacle, the the motors are only allowed to turn forward
        else if (sonicB.getDistance()<40) 
        {
          Motor.A.forward();
          Motor.C.forward();
          Delay.msDelay(100);   
        }
        //If there is no obstacle in the way, the directions are chosen randomly
        else
        {
          if (myRandom(0,1)==0) 
          {                   
            Motor.A.forward();
          } // end of if
          else
          {              
            Motor.A.backward();
          }
 
          if (myRandom(0,1)==0) 
          {                   
            Motor.C.forward();
          } // end of if
          else
          {              
            Motor.C.backward();
          }
        }
        //Perform the action for a duration of 100 to 500 milliseconds
        Delay.msDelay(myRandom(100,400));
 
        //reading the current time
        endTime = System.currentTimeMillis();
      } // end of while
 
      //Stopping, closing the claws and beeping
      BluetoothNXT_v5.SoftStop();
      Motor.A.stop();
      Motor.C.stop();
      Motor.B.setSpeed(100);
      Motor.B.backward();
      Thread.sleep(1000);
      Motor.B.stop();
      BluetoothNXT_v5.clawsOpen=false;
      Sound.twoBeeps();
    }
    catch(Exception err) {}
  }
 
  //Random number generator
  static int myRandom(int low, int high) 
  {
    high++;
    return (int) (Math.random() * (high - low) + low);
  }
}//EOF
Praktikaten: Andreas Mieke, Peter Por
Betreuer: Patrik Grausberg

Am 29.07.2013 fand die Abschlusspräsentation der Ferialpraktika 2013 statt.

Jedes Team bekam seinen eigenen Stand, sowie eine Pinnwand für das jeweilige Plakat. Wir konnten unseren NXT-Roboter vorführen und bekamen fast nur positives Feedback!
Anschließend ging es dann zum Sommerfest, wo uns ein überwältigendes Grillbuffet erwartete.

Unser Poster können Sie hier sehen.

 

Ein paar der Bilder von dem Event:

The DAO alternates between driving forwards and backwards every x seconds.
If there is an obstacle in range of one of its four ultrasonic sensors, DAO stops, waits till it is removed, and then proceeds driving along the predefined path.

This program was written due to the DAO being supposed to make an appearence at the “Wiener Forschungsfest 2013″. There it will have to interact with other traffic participants, notice them, and avoid crashs.

Download

Read the rest of this entry »