Zieldefinition

Der Mindstorms NXT Roboter soll, abhängig davon, welcher RFID Tag an den Scanner gehalten wird, eine andere Operation ausführen. Ist dieser Schritt erst einmal geschafft, darf weiters Kreativität einfließen.
Die Arbeit soll übersichtlich dokumentiert werden.
 

1. Update!

2. Update!

 

Bilder

Videos



 

Dokumentation

Das erste Problem bestand darin, dass wir den NXT (und die Bluetooth Schnittstelle) per Java programmieren wollten, der RFID Sensor allerdings nur über C-Librarys verfügt.
Um dieses Hindernis zu umgehen, hatten wir mehrere Lösungsansätze:

  • Die fertige Java Bluetooth Fernsteuerung nochmals in C schreiben. Hierfür fehlten uns allerdings die Bluetooth Bibliotheken.
  • Die C-Librarys des RFID Sensors mit Hilfe eines Java/C-Wrappers in Java implementieren. Hierfür standen uns das JNI (Java Native Interface) oder JNA (Java Native Access) zur Verfügung. Der Vorteil von JNA ist, dass wir hiermit, bereits existierende DLLs einbinden können, und diese nicht, wie bei JNI, selbst erstellen müssen.
  • Das getrennte Programmieren des Readers in C, und des NXTs in Java.

Wir haben uns letzten Endes für die dritte Option entschieden.
Auch bei dieser Herangehensweise konnten wir wieder zwischen zwei Verfahren wählen:
Das C-Programm für den RFID-Scanner könnte das Java-Programm aufrufen, oder umgekehrt.
Problematisch ist bei ersterem Ansatz, dass der Bluetooth-Kommunikationskanal immer geöffnet bleiben muss, dies aber schwer umzusetzen ist, wenn das Java-Programm nach jedem Befehl wieder beendet wird.

Daher haben wir uns dazu entschlossen, die Programme nach folgendem Blockschaltbild zu entwickeln:

Flowchart PC

Den entsprechenden Java-Code (wird am PC ausgeführt) können Sie hier sehen:
//Written by Peter Por and Andreas Mieke
//Last edited: 11.07.13
 
import lejos.pc.comm.*;
import java.io.*;
import java.awt.*;
 
public class NXTRFID {	
 
  public static void main(String[] args) throws IOException, InterruptedException {
	String nxtName = "NXT";    //Name of the specific NXT brick; "NXT" is default
	String RFIDScanLocation = "C:\\Users\\Peter\\Desktop\\RFIDScan_v1.2";    //The Location of the ".exe" file for the RFID Reader
	int COMPort = 8;    //The COM-Port of the RFID Reader
 
    //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 + " nicht gefunden!");
    }
    else{
      if(!connect.connectTo(nxts[0].name,nxts[0].deviceAddress,NXTCommFactory.BLUETOOTH))
      { //Connection failed
        System.out.print("Verbindungsaufbau zu " + nxtName + " fehlgeschlagen!");
      }
      else
      { //now there is a connection
        System.out.println("Verbindung zu " + nxtName + " aufgebaut!");
        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();
 
          if(p.exitValue()==3)  //Start playing music if the NXT is supposed to dance
          {
          	Desktop d = Desktop.getDesktop();
              File f = new File("C:/Users/Peter/Desktop/Musik.mp3");
              d.open(f); 
          }
 
          if(p.exitValue()==-1)   // -1 is the Error Code
          {
        	  System.out.println("Ein nicht bekannter RFID Tag wurde eingelesen.");
        	  break;
          }
         }
         catch(Exception err)
         {
        	 System.out.println("Fehler beim Einlesen des RFID Tags.");
          break;
         }
        }// end of loop       
 
    	dos.close();    //Closes the DataOutputStream
        connect.close();    //Closes the Connection to the NXT
        System.out.println("Programm beendet.");
      }
     }
 
    }// end of main 
   } // end of class Main
Der C-Code für den RFID Reader:
// RFIDScan.cpp : Defines the entry point for the console application.
// Written by Andreas Mieke
// Date: 10.07.13
 
#define VERSION "1.2"
 
#include "std_inc.h"
#include "comm.h"
#include "protocol.h"
#include "std_readwrite.h"
 
//General purpose buffer
char buffer[1000];
char code[17];
void *comm_handle = NULL;
void *reader_handle = NULL;
 
//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[]) {
	printf("RFIDScan Version: %s; Coded by Andreas Mieke\n\tStarting up...\n\n",VERSION);
	if(args < 2)
	{
		printf("Please provide the right COM port ");
		return -1;
	}
	sprintf(com_port,"COM%d",atoi(argv[1])); //Sets the content of argv[1] as COM port
 
	do { //Continues until a valid ID was scanned.
		if(OpenComm() == false) //Opens the connection to the RFID reader (comm.cpp)
			return -1;
 
		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
 
		printf("%s\n",code); //Prints the HEX-Code (TAGID) to the screen
		CloseComm(); //Closes connection to Reader (comm.cpp)
 
		if(strcmp(code,"E00401500A95A9FF") == 0)		//Forward code
			return 8;
		else if(strcmp(code,"E00401500A957E17") == 0)	//Backward code
			return 2;
		else if(strcmp(code,"E00401500A95AA39") == 0)	//Turn left code
			return 4;
		else if(strcmp(code,"E00401500A957E55") == 0)	//Turn right code
			return 6;
		else if(strcmp(code,"E00401500A95AA79") == 0)	//Stop code
			return 5;
		else if(strcmp(code,"E00401500A957DDB") == 0)	//Claws open code
			return 7;
		else if(strcmp(code,"E00401500A9572E4") == 0)	//Claws close code
			return 9;
		else if(strcmp(code,"E00401500A954A94") == 0)	//Dance code
			return 3;
		else if(strcmp(code,"E00401500A957E95") == 0)	//Terminate code
			return -1;
		else
			strcpy(code,"");
	} while(true);
}
//EOF
Das Grundgerüst des Programms, das am NXT ausgeführt wird, ist ebenfalls sehr simpel:

Bevor wir Ihnen an dieser Stelle den NXT-Code präsentieren, wollen wir Ihnen noch etwas über die Bonus-Features erzählen, die wir unserem NXT gegeben haben:
Wir haben den Roboter vorne und hinten mit je einem Ultraschall-Sensor ausgestattet. Wenn der NXT nun zu nahe an ein Objekt fährt, bleibt er stehen und man kann den jeweiligen Befehl (vorwärts/rückwärts) erst wieder benutzen, wenn der NXT “freie Sicht” hat. Alle anderen Fahrtrichtungen sind jedoch weiterhin freigegeben.
Des Weiteren haben wir, zusätzlich zu den nun folgenden Funktionalitäten, auch einen Tanz-Befehl einprogrammiert. Außerdem haben wir ihn mit Klauen, die er öffnen und schließen kann, ausgestattet.

Der Roboter kennt folgende Befehle:

  • Vorwärts
  • Rückwärts
  • Links drehen
  • Rechts drehen
  • Stopp
  • Klauen öffnen
  • Klauen schließen
  • Tanzen
Nun können Sie sich hier den betreffenden Java-Code (wird am NXT ausgeführt) zu Gemüte führen:
//Written by Peter Por
//Last edited: 11.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_v4 {
 
  //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 using 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-ID 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_v4.clawsOpen)
      safetyDistanceF=23;
      else
      safetyDistanceF=30;
 
 
      try
      {
        if (sonicF.getDistance()<safetyDistanceF && BluetoothNXT_v4.keypress==8) 
        {//The NXT is currently driving forwards and faces an obstacle
          Motor.B.stop();
          BluetoothNXT_v4.SoftStop();
          Motor.A.stop();
          Motor.C.stop();       
          BluetoothNXT_v4.mayDriveForward = false;                  
        }
      }      
      catch(Exception err)
      { }
 
 
      try
      {
        if (sonicB.getDistance()<safetyDistanceB && BluetoothNXT_v4.keypress==2) 
        {//The NXT is currently driving backwards and faces an obstacle
          Motor.B.stop();
          BluetoothNXT_v4.SoftStop();
          Motor.A.stop();
          Motor.C.stop();     
          BluetoothNXT_v4.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(), 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_v4.SoftStop();
      Motor.A.stop();
      Motor.C.stop();
      Motor.B.setSpeed(100);
      Motor.B.backward();
      Thread.sleep(1000);
      Motor.B.stop();
      BluetoothNXT_v4.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);
  }
}//End of file
Praktikaten: Andreas Mieke, Peter Por
Betreuer: Patrik Grausberg

Leave a Reply