htz
htz

Reputation: 1037

How to read binary protocol from InputStream?

I try to read a binary protocol from my serial port in Java. I use the RXTX library to read from the serial port.

The protocol is the SiRF binary protocol for GPS receivers. The structure is:

Although this structure is quite simple I am not able to read the data correctly. I tried two different ways:

  1. I wrote a thread, which reads from the InputStream and compares the input with 0xA0. If this matches, then the thread reads the next byte and compares to 0xA2. If this matches again, it reads the next two bytes and computes the payload length. After that, it reads the payload with length n bytes. At last, it reads the last two bytes and compares them to 0xB0 and 0xB3. When everything matches, it creates a hex string from the input and saves it to a queue. Here is my code:

    try {
        byte[] size = new byte[2];
        byte[] checkSum = new byte[2];
        byte[] packet;
        int bytesRead = -1;
        while (in.available() > 0) {
            if (getInput() == 0xA0) {
                if (getInput() == 0xA2) {
                    System.out.print("160,162,");
                    bytesRead = in.read(size);
                    int payLoadLength = (size[0] << 8) | (size[1] & 0xFF);
                    byte[] payload = new byte[payLoadLength];
                    bytesRead = in.read(payload);
                    bytesRead = in.read(checkSum);
                    if (getInput() == 0xB0) {
                        if (getInput() == 0xB3) {
                    System.out.println("out");
                    packet = new byte[2 + 2 + payLoadLength + 2 + 2];
                    packet[0] = (byte) START_1;
                    packet[1] = (byte) START_2;
                    packet[2] = size[0];
                    packet[3] = size[1];
                    for (int i = 0; i < payload.length; i++) {
                        packet[i + 4] = payload[i];
                    }
                    packet[packet.length - 4] = checkSum[0];
                    packet[packet.length - 3] = checkSum[1];
                    packet[packet.length - 2] = (byte) END_1;
                    packet[packet.length - 1] = (byte) END_2;
                    StringBuffer hexString = new StringBuffer();
                    int[] output = new int[packet.length];
                    for (int i = 0; i < packet.length; i++) {
                        output[i] = 0xFF & packet[i];
                        hexString.append(Integer.toHexString(output[i]));
                    }
                    TransportMessage tm = new TransportMessage(hexString.toString());
                    boolean b = queue.offer(tm);
                    System.out.println(hexString.toString().toUpperCase());
                    if(!b)
                        System.err.println("TransportMessageQueue voll!");
                }
            } else {
                System.out.println();
            }
        }
    } 
        }
    } catch (IOException e) {
        System.out.println(e);
    }
    

    getInput() just reads a byte from the InputStream.

  2. I used the SerialPortEventListener from the RXTX library. Everytime something happens on the serial port, a SerialPortEvent is fired and I read the data in a similar way as in the first method.

For debugging reasons I added several System.out.println() calls to show what exactly is read by the threads and it seems that I am missing nearly the half of all data frames sended via the InputStream.

My question now is: What is the best way to read such a protocol from an InputStream? In my opinion I have to get my reading efforts synchronized with the data from the serial port, but I have no idea how to do that in Java.

Upvotes: 1

Views: 1748

Answers (1)

thedayofcondor
thedayofcondor

Reputation: 3876

If you are using RXTX you can set a listener which will notify you asyncronously when data is available:

serial.notifyOnDataAvailable(true);
serial.addEventListener(this);

public void serialEvent(final SerialPortEvent arg0) {
    try {
        if (arg0.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
            //read from the input stream;
        }
    } catch (final Exception e) {
        e.printStackTrace();
    }
}

In general, when you read from the input stream you should abstract the concept of "packet of data" and not use the "available" function - just read enough from the packet to be able to determine its length, and then just use the "read(byte[])" to read a full packet into a buffer. (on a more advanced note, the "read" can read fewer bytes than needed so it should be used in a loop).

Upvotes: 1

Related Questions