# GPS-Maus auslesen



## scheidti (21. Jul 2009)

Hi,

ich habe mir eine Bluetooth GPS-Maus (Royaltek rbt-2100) gekauft und wollte die Daten mit Java auslesen. Ich wollte die RXTX Api verwenden. Ich habe allerdings Probleme die Daten korrekt byteweise einzulesen. Zwischen den richtigen Daten befinden sich Sonderzeichen. Hier ein Bild dazu:




Und so sollte es korrekt aussehen (in Putty):




Vielleicht kann mir hier jemand helfen. Hier mein Quellcode: 

```
import java.io.*;
import java.util.*;
import gnu.io.*;

public class Test implements SerialPortEventListener {

	static CommPortIdentifier portId;
	static Enumeration portList;
	InputStream inputStream;
	SerialPort serialPort;
	
	public static void main(String[] args) {
		boolean portFound = false;
		String defaultPort = "";
		
		String osName = System.getProperty("os.name","").toLowerCase();
		if(osName.startsWith("windows")) defaultPort = "COM5";
		else { System.out.println("Program only for MS Windows."); System.exit(0); }
		System.out.println("Default Port: "+defaultPort);
		
		portList = CommPortIdentifier.getPortIdentifiers();
		while(portList.hasMoreElements()) {
			portId = (CommPortIdentifier)portList.nextElement();
			if(portId.getPortType()==CommPortIdentifier.PORT_SERIAL) {
				if(portId.getName().equals(defaultPort)) {
					System.out.println("Found Port:"+defaultPort);
					portFound = true;
					Test reader = new Test();
				}
			}
		}
		if(!portFound) System.out.println("Port "+defaultPort+" not found.");
	}
	
	public Test() {
		try {
			serialPort = (SerialPort)portId.open("Test", 2000);
		} catch (PortInUseException e) { System.out.println("Port is used."); System.exit(0); }
		
		try {
			inputStream = serialPort.getInputStream();
		} catch (IOException e) { System.out.println(e.toString()); System.exit(0); }
		
		try {
			serialPort.addEventListener(this);
		} catch (TooManyListenersException e) { System.out.println(e.toString()); System.exit(0); }
		
		serialPort.notifyOnDataAvailable(true);
		
		try {
			serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
		} catch (UnsupportedCommOperationException e) { System.out.println(e.toString()); System.exit(0); }
	}
	
	public void serialEvent(SerialPortEvent event) {
		String s = new String();
		switch(event.getEventType()) {
			case SerialPortEvent.BI: 
			case SerialPortEvent.OE:
			case SerialPortEvent.FE:
			case SerialPortEvent.PE:
			case SerialPortEvent.CD:
			case SerialPortEvent.CTS:
			case SerialPortEvent.DSR:
			case SerialPortEvent.RI:
			case SerialPortEvent.OUTPUT_BUFFER_EMPTY: break;
			case SerialPortEvent.DATA_AVAILABLE:
				byte[] readBuffer = new byte[200];
				try {				
					while(inputStream.available()>0) {
						int numBytes = inputStream.read(readBuffer);
					}
					String result  = new String(readBuffer);
		            System.out.println(result);
				} catch (IOException e) { System.out.println(e.toString()); System.exit(0); }
				
				break;
		}
	}
}
```

Später möchte ich die einzelnen Daten zeilenweise als String auswerten können. Wenn jemand dafür eine Idee hat, könnte er diese bitte posten.


Mfg
scheidti


----------



## scheidti (21. Jul 2009)

Ich habe jetzt eine Möglichkeit gefunden:

```
public void serialEvent(SerialPortEvent event) {
		String s = new String();
		switch(event.getEventType()) {
			case SerialPortEvent.BI: 
			case SerialPortEvent.OE:
			case SerialPortEvent.FE:
			case SerialPortEvent.PE:
			case SerialPortEvent.CD:
			case SerialPortEvent.CTS:
			case SerialPortEvent.DSR:
			case SerialPortEvent.RI:
			case SerialPortEvent.OUTPUT_BUFFER_EMPTY: break;
			case SerialPortEvent.DATA_AVAILABLE:
				byte[] readBuffer = new byte[200];
				try {				
					while(inputStream.available()>0) {
						char c = (char)inputStream.read();
						int x = c;
						if(x<256) { System.out.print(c); }
					}
				} catch (IOException e) { System.out.println(e.toString()); System.exit(0); }
				
				break;
		}
	}
```

Um aber die einzelnen Daten als String zu bekommen, wollte ich einen InputStreamReader und einen BufferedReader benutzen. Hier habe ich jetzt wieder ein Problem.


```
try {
			inputStream = serialPort.getInputStream();
			in = new BufferedReader(new InputStreamReader(inputStream));
		} catch (IOException e) { System.out.println(e.toString()); System.exit(0); }
```


```
public void serialEvent(SerialPortEvent event) {
		String s = new String();
		switch(event.getEventType()) {
			case SerialPortEvent.BI: 
			case SerialPortEvent.OE:
			case SerialPortEvent.FE:
			case SerialPortEvent.PE:
			case SerialPortEvent.CD:
			case SerialPortEvent.CTS:
			case SerialPortEvent.DSR:
			case SerialPortEvent.RI:
			case SerialPortEvent.OUTPUT_BUFFER_EMPTY: break;
			case SerialPortEvent.DATA_AVAILABLE:
				try {				
					while(true) {
						String msg = in.readLine();
						System.out.println(msg);
					}
				} catch (IOException e) { System.out.println(e.toString()); System.exit(0); }
				
				break;
		}
	}
```

So habe ich meinen Quellcode bis jetzt geändert. Ich bekomme jedoch folgende Fehlermeldung:
Es werden erst die richtigen Daten ausgegeben und dann eine Exception:
java.io.IOException: Underlying input stream returned zero bytes

Wie kann ich das beseitigen?


----------



## tuxedo (22. Jul 2009)

Wann und wo kommt diese Exception? Stacktrace?


----------



## HoaX (22. Jul 2009)

scheidti hat gesagt.:


> [JAVA=68]
> byte[] readBuffer = new byte[200];
> try {
> while(inputStream.available()>0) {
> ...


Dein Problem liegt hier.
a) Die While-Schliefe ist gefährlich. Wenn 300Bytes verfügbar sind, dann liest du 200, wirfst sie weg und liest die nächsten 100 und machst nur mit denen etwas.
b) new String(readBuffer): Hier wird ein String mit 200Bytes erstellt, auch wenn du garkeine 200 Bytes eingelesen hast, darum die komischen Zeichen in deiner Ausgabe.

Richtig wäre:
[JAVA=68]
				byte[] readBuffer = new byte[200];
				try {				
					int numBytes = inputStream.read(readBuffer);

					String result  = new String(readBuffer, 0, numBytes);
					System.out.println(result);
				} catch (IOException e) { System.out.println(e.toString()); System.exit(0); }
[/code]


----------



## HoaX (22. Jul 2009)

Dein While(true) im DataAvailable ist böse, damit blockierst du den Empfang.
Ich schätze daher kommt dann auch die Exception. Im ersten Durchlauf liest du die verfügbaren Bytes. Dann wartest du aber nicht auf das nächste DataAvailable-Event sondern liest einfach weiter bevor rxtx die Möglichkeit hatte überhaupt Daten nachzuladen. Rxtx merkt das und meckert.


----------



## scheidti (22. Jul 2009)

HoaX hat gesagt.:


> Dein While(true) im DataAvailable ist böse, damit blockierst du den Empfang.
> Ich schätze daher kommt dann auch die Exception. Im ersten Durchlauf liest du die verfügbaren Bytes. Dann wartest du aber nicht auf das nächste DataAvailable-Event sondern liest einfach weiter bevor rxtx die Möglichkeit hatte überhaupt Daten nachzuladen. Rxtx merkt das und meckert.



Das mir der while-Schleife hab ich mir schon gedacht. Ich hatte es ohne Schleife versucht und mit while(in.ready()). Beides führt zur gleichen Exception.




tuxedo hat gesagt.:


> Wann und wo kommt diese Exception? Stacktrace?



Hier der Stacktrace:

```
java.io.IOException: Underlying input stream returned zero bytes
	at sun.nio.cs.StreamDecoder.readBytes(Unknown Source)
	at sun.nio.cs.StreamDecoder.implRead(Unknown Source)
	at sun.nio.cs.StreamDecoder.read(Unknown Source)
	at java.io.InputStreamReader.read(Unknown Source)
	at java.io.BufferedReader.fill(Unknown Source)
	at java.io.BufferedReader.readLine(Unknown Source)
	at java.io.BufferedReader.readLine(Unknown Source)
	at Test2.serialEvent(Test2.java:72)
	at gnu.io.RXTXPort.sendEvent(RXTXPort.java:732)
	at gnu.io.RXTXPort.eventLoop(Native Method)
	at gnu.io.RXTXPort$MonitorThread.run(RXTXPort.java:1575)
```
Die Exception kommt nachdem einige Datensätze korrekt ausgegeben wurden. Das Problem liegt dann wohl hier:

```
try {				
					while(in.ready()) {
						String msg = in.readLine();
						System.out.println(msg);
					}
				} catch (IOException e) { System.out.println(e.toString()); e.printStackTrace(); System.exit(0); }
```


----------



## scheidti (22. Jul 2009)

Ich habe jetzt mal die while-Schleife komplett rausgenommen und einfach mit Thread.sleep() 1ne Sekunde vorher gewartet bis ich die Daten einlese.

```
case SerialPortEvent.DATA_AVAILABLE:
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {}
				try {				
						String msg = in.readLine();
						System.out.println(msg);
				} catch (IOException e) { System.out.println(e.toString()); e.printStackTrace(); System.exit(0); }
				
				break;
```
Das scheint zu funktionieren, kann aber keine elegante Lösung sein. Da muss es doch noch etwas anderes geben. Wenn ich halt mit ready() vom BufferedReader prüfe, ob der Reader bereit ist, gibt dieser zu jeder Zeit true zurück. (Ist ja eigentlich auch logisch, denn es wird ja das Event DATA_AVAILABLE ausgelöst) Die Exception, dass keine Daten im Buffer sind wird immer noch ausgelöst.

Kennt noch jemand eine andere Möglichkeit, wie ich mein Problem lösen könnte?


----------



## HoaX (23. Jul 2009)

Das Problem ist dass readLine() mehr Bytes zu lesen versucht als InputStream#available() zurückliefert. Was dann passiert habe ich oben erläutert.


----------



## tuxedo (23. Jul 2009)

Hatte in meiner Studienzeit mal ein ähnliches Projekt laufen.

Hier ein Auszug aus meinem damaligen Code (nicht perfekt, aber funktioniert...):


```
/**
	 * Öffnet die Verbindung zu einem echten GPS-Empfänger
	 * 
	 * @param portIdentifier
	 */
	private void openRealCommunication(String portIdentifier) {
		try {
			//_log.info("getting com-port");
			portID = CommPortIdentifier.getPortIdentifier(portIdentifier);

			//_log.info("registering program");
			rs232 = (SerialPort) portID.open("WAYTRACKING rev"+Config.BUILD, 1);
			
			//_log.info("program is now registered with com-port");

			//_log.info("setting port parameters");
			rs232.setSerialPortParams(Config.COM_BAUDRATE, Config.COM_DATABITS,	Config.COM_STOPBITS, Config.COM_PARITY);

			// Signalisiert wenn neue Daten anliegen, eigtl für uns nicht nötig
			// da wir blockierend lesen. Macht im Non-Bocking-IO Sinn:
			// [url=http://www.eecs.harvard.edu/~mdw/proj/java-nbio/]NBIO: Java Non-blocking I/O Library[/url]
			
			rs232.notifyOnDataAvailable(true);

			_log.info("getting rx-stream from com-port");
			inputStream = rs232.getInputStream();
			
			char c;
			String tempRxString = "";
				
			while (!interrupted()) {
				

				// Lese ein zeichen 'c' aus dem Empfangsstream
				c = (char) inputStream.read();
				
				// Das Zeichen ist nur dann gültig wenn es in die ASCII-Tabelle (0..255) passt
				if (c < 256) {	

					/*
					 *  Wenn es den int-Wert 10 hat handelt es sich um ein Linefeed.
					 *  Ein Linefeed ist sogesehen ein Zeilenumbrch und trennt somit
					 *  die einzelnen GPS-Datensätze voneinander. 
					 *  Nur wenn es KEIN Zeilenumbruch-Zeichen ist wird es an einen
					 *  temporäeren Sring angehängt. 
					 */
					if (c!=10) {
						tempRxString+=c;	// hänge das nächste Zeichen an.
					}
					/*
					 * Hat es dann irgend wann mal den Wert 10 ist der GPS-Datemsatz
					 * komplett und kann geparsed werden.
					 * Danach muss der Temp-String zurückgesetzt werden und die 
					 * Geschchte geht von vorne los.
					 */
					else {
						if (!suspend){
							NMEA0183 nmea = new NMEA0183(tempRxString.trim()); 
							// Nur in den Puffer schreiben wenn der Datensatz okay ist ...
							if (nmea.CRC_OKAY) gpsBuffer.add(nmea);
							
							tempRxString="";
						}
					}
				}
				
			}

		} catch (UnsupportedCommOperationException e) {
			// TODO Exception abfangen !!!
			e.printStackTrace();
		} catch (NoSuchPortException e) {
			// TODO Exception abfangen !!!
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Exception abfangen !!!
			e.printStackTrace();
		} catch (PortInUseException e) {
			// TODO Exception abfangen !!!
			e.printStackTrace();
		} catch (RingBufferOverflowException e) {
			e.printStackTrace();
		} catch (NMEASentenceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
```

Die einzelnen rxtx Elemente müsstest du wiedererkennen. "portIdentifier" definiert den Com-POrt. Also z.B. "COM3". Das ganze lief in einem eigenen Thread und hat nix anderes gemacht wie Datensätze gelesen und sie "bereit gestellt" (siehe weiter unten in dieser Erklärung)

tempRxString hat am Ende die komplette Zeile eines NMEA Datensatzes enthalten. Diese "raw" Zeile hab ich dann in ein NMEA-Container-Objekt gesteckt in dem die Zeile dann geparst wurde und letztenendes ein passendes Objekt mit passenden Getter-Methoden zum Anfragen der Daten des Datensatzes rausfiel... Das NMEA Container-Objekt hab ich dann in einem Ring-Puffer gesteckt und in einem weiteren Thread, der sich um die Abarbeitung der Daten gekümmert hat wieder ausgelesen. Schnittstelle zwischen den beiden Threads war also dieser Ring-Puffer.

- Alex


----------



## scheidti (23. Jul 2009)

Hi,

danke für eure Hilfe. Ich werd wohl mit tuxedos variante arbeiten.


mfg
scheidti


----------



## tuxedo (23. Jul 2009)

Wie gesagt: Optimal ist es nicht da immer nur Zeichen für Zeichen gelesen wird. "Besser" wäre es mehr zu lesen und dynamisch die Zeilen zurecht zu schneiden.

Aber da bei GPS kein riesen Datenaufkommen vorhanden ist fällt das wohl weniger ins Gewicht.

- Alex


----------



## toffi947 (27. Jul 2009)




----------



## KTR (29. Mai 2010)

Hallo,

ich bin Student der Wirtschaftsinformatik und hätte eine Frage zum Programm, das scheidti geschrieben hat. Ich habe es in meine eclipse Umgebung eingebunden. Beim Ausführen des Programms hängt dieses sich jedoch auf. Dies geschieht dadurch das eine Maske geöffnet wird, in der ich dann einen COM-Port auswählen kann. Das klicken auf den Save-Button führt jedoch zu keinem Ergebnis.

Ich sollte vielleicht noch hinzufügen, dass ich in dem Gebiet der Java Programmierung nicht allzu sehr bewandert bin.

Vielen Dank
Lukas


----------



## mabuhay (29. Mai 2010)

Naja, soviel ich sehe ist der hier zu findende Code gar kein Gui und wird so per copy-paste wahrscheinlich auch nicht funktionieren. Hast du denn schon ein Gui programmiert und den hier zu findenden Code dort eingebunden? Dann liegt es an deinem Button, welcher mit keiner Aktion verknüpft ist...


----------



## toffi947 (30. Mai 2010)

```
import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import gnu.io.*;

public class Test implements SerialPortEventListener {

	static CommPortIdentifier portId;
	static Enumeration portList;
	InputStream inputStream;
	SerialPort serialPort;
	private int numBytes;
	public static String s;
	static ArrayList<String> al;


	public static void main(String[] args) {
		al = new ArrayList();
		boolean portFound = false;
		String defaultPort = "";
	

		String osName = System.getProperty("os.name", "").toLowerCase();
		if (osName.startsWith("windows"))
			defaultPort = "COM1";
		else {
			System.out.println("Program only for MS Windows.");
			System.exit(0);
		}
		System.out.println("Default Port: " + defaultPort);

		portList = CommPortIdentifier.getPortIdentifiers();
		while (portList.hasMoreElements()) {
			portId = (CommPortIdentifier) portList.nextElement();
			if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
				if (portId.getName().equals(defaultPort)) {
					System.out.println("Found Port:" + defaultPort);
					portFound = true;
					Test reader = new Test();
				}
			}
		}
		if (!portFound)
			System.out.println("Port " + defaultPort + " not found.");
	}

	public Test() {
		try {
			serialPort = (SerialPort) portId.open("Test", 2000);
		} catch (PortInUseException e) {
			System.out.println("Port is used.");
			System.exit(0);
		}

		try {
			inputStream = serialPort.getInputStream();
		} catch (IOException e) {
			System.out.println(e.toString());
			System.exit(0);
		}

		try {
			serialPort.addEventListener(this);
		} catch (TooManyListenersException e) {
			System.out.println(e.toString());
			System.exit(0);
		}

		serialPort.notifyOnDataAvailable(true);

		try {
			serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8,
					SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
		} catch (UnsupportedCommOperationException e) {
			System.out.println(e.toString());
			System.exit(0);
		}
	}

	public void serialEvent(SerialPortEvent event) {

		switch (event.getEventType()) {
		case SerialPortEvent.BI:
		case SerialPortEvent.OE:
		case SerialPortEvent.FE:
		case SerialPortEvent.PE:
		case SerialPortEvent.CD:
		case SerialPortEvent.CTS:
		case SerialPortEvent.DSR:
		case SerialPortEvent.RI:
		case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
			break;
		case SerialPortEvent.DATA_AVAILABLE:
			byte[] readBuffer = new byte[200];
			try {
				while (inputStream.available() > 0) {
					numBytes = inputStream.read(readBuffer);
				}
                String s = new String(readBuffer);
				/*
				Pattern p = Pattern.compile("[0-9]");
				Matcher m = p.matcher(result);

				while (m.find()) {
					s = result.substring(m.start(), m.end());
					//System.out.print(s);
					 * */
				
					//al.add(s);
					System.out.print(s);
					
				
				
			

			} catch (IOException e) {
				System.out.print(e.toString());
				System.exit(0); 
			}

			break;
		}
		
		
	
	}

}
```



> Ich hatte mal das gleiche Problem.Weis aber nicht mehr genau woran es lag.Kopiere mal den Code. Und die rxtxSerial.dll und die rxtx.paralell.dll in in  das Verzeichnis reinziehen.Wenn du mir deine E-Mail gibtst kann ich dir auch noch die Bibliotheken schicken


----------

