我尝试从Java中的串口读取二进制协议。我使用RXTX library从串口读取。
该协议是用于GPS接收器的SiRF二进制协议。结构是:
虽然这种结构非常简单,但我无法正确读取数据。我尝试了两种不同的方式:
我写了一个线程,它从InputStream
读取并将输入与0xA0进行比较。如果匹配,则线程读取下一个字节并与0xA2进行比较。如果再次匹配,则读取接下来的两个字节并计算有效负载长度。之后,它读取长度为n个字节的有效载荷。最后,它读取最后两个字节并将它们与0xB0和0xB3进行比较。当一切都匹配时,它会从输入中创建一个十六进制字符串并将其保存到队列中。这是我的代码:
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()
只是从InputStream
。
我使用了RXTX库中的SerialPortEventListener
。每次在串口上发生某些事情时,都会触发SerialPortEvent,我会以与第一种方法类似的方式读取数据。
出于调试原因,我添加了几个System.out.println()
调用来显示线程读取的内容,而且我似乎错过了通过InputStream
发送的所有数据帧的近一半。
我现在的问题是:从InputStream
阅读此类协议的最佳方法是什么?在我看来,我必须让我的阅读工作与串口数据同步,但我不知道如何用Java做到这一点。
答案 0 :(得分:1)
如果您使用的是RXTX,您可以设置一个监听器,当数据可用时,该监听器将异步通知您:
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();
}
}
通常,当您从输入流中读取时,您应该抽象出&#34;数据包的概念&#34;而不是使用&#34;可用&#34;函数 - 只需从数据包中读取足以确定其长度,然后只需使用&#34; read(byte [])&#34;将完整数据包读入缓冲区。 (在更高级的说明中,&#34;读取&#34;可以读取比所需更少的字节,因此应该在循环中使用它。)