解析来自串口的完整消息
parsing complete messages from serial port
我正在尝试通过串行端口从我的 GPS 读取完整的消息。
我要查找的消息开头为:
0xB5 0x62 0x02 0x13
于是我就这样从串口读取
while (running !=0)
{
int n = read (fd, input_buffer, sizeof input_buffer);
for (int i=0; i<BUFFER_SIZE; i++)
{
if (input_buffer[i]==0xB5 && input_buffer[i+1]== 0x62 && input_buffer[i+2]== 0x02 && input_buffer[i+3]== 0x13 && i<(BUFFER_SIZE-1) )
{
// process the message.
}
}
我遇到的问题是我需要获得完整的消息。一条消息的一半可能在一次迭代中位于缓冲区中。另一半可能会在下一次迭代中出现。
有人建议从完整的消息中释放缓冲区。然后我将缓冲区中的其余数据移动到缓冲区的开头。
我该怎么做或以其他方式确保我收到每条完整的选定消息?
编辑//
我想要一个特定的 class 和 ID。但我也可以读入长度
您可以将阅读分为三个部分。查找消息的开头。然后得到长度。然后阅读邮件的其余部分。
// Should probably clear these in case data left over from a previous read
input_buffer[0] = input_buffer[1] = 0;
// First make sure first char is 0xB5
do {
n = read(fd, input_buffer, 1);
} while (0xB5 != input_buffer[0]);
// Check for 2nd sync char
n = read(fd, &input_buffer[1], 1);
if (input_buffer[1] != 0x62) {
// Error
return;
}
// Read up to LENGTH
n = read(fd, &input_buffer[2], 4);
// Parse length
//int length = *((int *)&input_buffer[4]);
// Since I don't know what size an int is on your system, this way is better
int length = input_buffer[4] | (input_buffer[5] << 8);
// Read rest of message
n = read(fd, &input_buffer[6], length);
// input_buffer should now have a complete message
你应该添加错误检查...
要尽量减少多次 read() 系统调用的小字节数开销,请在代码中使用中间缓冲区。
read() 应该处于阻塞模式以避免 return 零字节代码。
#define BLEN 1024
unsigned char rbuf[BLEN];
unsigned char *rp = &rbuf[BLEN];
int bufcnt = 0;
static unsigned char getbyte(void)
{
if ((rp - rbuf) >= bufcnt) {
/* buffer needs refill */
bufcnt = read(fd, rbuf, BLEN);
if (bufcnt <= 0) {
/* report error, then abort */
}
rp = rbuf;
}
return *rp++;
}
有关串行终端的正确 termios 初始化代码,请参阅 this answer。您应该将 VMIN 参数增加到更接近 BLEN 值的值。
现在您可以一次一个字节方便地访问接收到的数据,而性能损失最小。
#define MLEN 1024 /* choose appropriate value for message protocol */
unsigned char mesg[MLEN];
while (1) {
while (getbyte() != 0xB5)
/* hunt for 1st sync */ ;
retry_sync:
if ((sync = getbyte()) != 0x62) {
if (sync == 0xB5)
goto retry_sync;
else
continue; /* restart sync hunt */
}
class = getbyte();
id = getbyte();
length = getbyte();
length += getbyte() << 8;
if (length > MLEN) {
/* report error, then restart sync hunt */
continue;
}
for (i = 0; i < length; i++) {
mesg[i] = getbyte();
/* accumulate checksum */
}
chka = getbyte();
chkb = getbyte();
if ( /* valid checksum */ )
break; /* verified message */
/* report error, and restart sync hunt */
}
/* process the message */
switch (class) {
case 0x02:
if (id == 0x13) {
...
...
我正在尝试通过串行端口从我的 GPS 读取完整的消息。
我要查找的消息开头为:
0xB5 0x62 0x02 0x13
于是我就这样从串口读取
while (running !=0)
{
int n = read (fd, input_buffer, sizeof input_buffer);
for (int i=0; i<BUFFER_SIZE; i++)
{
if (input_buffer[i]==0xB5 && input_buffer[i+1]== 0x62 && input_buffer[i+2]== 0x02 && input_buffer[i+3]== 0x13 && i<(BUFFER_SIZE-1) )
{
// process the message.
}
}
我遇到的问题是我需要获得完整的消息。一条消息的一半可能在一次迭代中位于缓冲区中。另一半可能会在下一次迭代中出现。
有人建议从完整的消息中释放缓冲区。然后我将缓冲区中的其余数据移动到缓冲区的开头。
我该怎么做或以其他方式确保我收到每条完整的选定消息?
编辑//
我想要一个特定的 class 和 ID。但我也可以读入长度
您可以将阅读分为三个部分。查找消息的开头。然后得到长度。然后阅读邮件的其余部分。
// Should probably clear these in case data left over from a previous read
input_buffer[0] = input_buffer[1] = 0;
// First make sure first char is 0xB5
do {
n = read(fd, input_buffer, 1);
} while (0xB5 != input_buffer[0]);
// Check for 2nd sync char
n = read(fd, &input_buffer[1], 1);
if (input_buffer[1] != 0x62) {
// Error
return;
}
// Read up to LENGTH
n = read(fd, &input_buffer[2], 4);
// Parse length
//int length = *((int *)&input_buffer[4]);
// Since I don't know what size an int is on your system, this way is better
int length = input_buffer[4] | (input_buffer[5] << 8);
// Read rest of message
n = read(fd, &input_buffer[6], length);
// input_buffer should now have a complete message
你应该添加错误检查...
要尽量减少多次 read() 系统调用的小字节数开销,请在代码中使用中间缓冲区。
read() 应该处于阻塞模式以避免 return 零字节代码。
#define BLEN 1024
unsigned char rbuf[BLEN];
unsigned char *rp = &rbuf[BLEN];
int bufcnt = 0;
static unsigned char getbyte(void)
{
if ((rp - rbuf) >= bufcnt) {
/* buffer needs refill */
bufcnt = read(fd, rbuf, BLEN);
if (bufcnt <= 0) {
/* report error, then abort */
}
rp = rbuf;
}
return *rp++;
}
有关串行终端的正确 termios 初始化代码,请参阅 this answer。您应该将 VMIN 参数增加到更接近 BLEN 值的值。
现在您可以一次一个字节方便地访问接收到的数据,而性能损失最小。
#define MLEN 1024 /* choose appropriate value for message protocol */
unsigned char mesg[MLEN];
while (1) {
while (getbyte() != 0xB5)
/* hunt for 1st sync */ ;
retry_sync:
if ((sync = getbyte()) != 0x62) {
if (sync == 0xB5)
goto retry_sync;
else
continue; /* restart sync hunt */
}
class = getbyte();
id = getbyte();
length = getbyte();
length += getbyte() << 8;
if (length > MLEN) {
/* report error, then restart sync hunt */
continue;
}
for (i = 0; i < length; i++) {
mesg[i] = getbyte();
/* accumulate checksum */
}
chka = getbyte();
chkb = getbyte();
if ( /* valid checksum */ )
break; /* verified message */
/* report error, and restart sync hunt */
}
/* process the message */
switch (class) {
case 0x02:
if (id == 0x13) {
...
...