c - C串口读取找不到回车“\r”
问题描述
我现在正在努力通过串行初始化 ELM237 OBD2 适配器几天。我要连接的设备发送一个总是以回车结束的响应。我可以毫无问题地发送命令。响应也按预期由设备发送。我正在尝试从串行端口读取,直到我读取回车符。我可以在我的串行监视器中看到该命令已正确发送,并且我收到一条完整的消息,包括回车。但是我的代码无法识别回车。
这是我在串行监视器上收到的内容:
[19/07/2018 21:08:43]
Written data
41 54 5a 0d
ATZ.
[19/07/2018 21:08:43] Read data
41 54 5a 0d
ATZ.
[19/07/2018 21:08:44] Read data
0d 0d 45 4c 4d 33 32 37 20 76 31 2e 35 0d 0d 3e
..ELM327 v1.5..>
这是我的程序的输出:
Serial Port Open Succesfully
Request ATZ Reset ELM Adapter
current buffer ATZ
current buffer ATZ
current buffer ATZ
current buffer ATZ
ELM327 v1.5
current buffer ATZ
ELM327 v1.5
代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <sys/ioctl.h>
int SerialConnection();
int serial_fd;
int main(int argc, char* argv[])
{
char buffer[255]; /* Input buffer */
char *bufptr; /* Current char in buffer */
int nbytes; /* Number of bytes read */
if(argc != 2)
{
printf( "Usage: %s <Serial Port>\n",argv[0]);
exit(1);
}
serial_fd = SerialConnection(argv[1]);//Open serial port
if(serial_fd < 1)
{
printf( "Serial Port Open Failure\n");
exit(1);
}
printf( "Serial Port Open Succesfully\n");
while(1)
{
memset(buffer, 0, 255); //clear buffer
int ELMInit = 1;
switch(ELMInit)
{
case 1:
printf( "Request ATZ Reset ELM Adapter\n");
write(serial_fd, "ATZ\r", 4);
bufptr = buffer;
while ((nbytes = read(serial_fd, bufptr, buffer + sizeof(buffer) - bufptr - 1)) > 0)
{
printf("current buffer %s \n",buffer);
bufptr += nbytes;
if (bufptr[-1] == '\r')
{
printf("carriage return found %s \n",buffer);
if(strstr(buffer, "ELM327") != NULL)
{
printf("success %s\n",buffer);
}
break;
}
*bufptr = '\0';
}
break;
default:
break;
}
}
}
int SerialConnection(char *serial_port)
{
int fd;
struct termios options;
fd = open(serial_port, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
return -1;
}
else
{
fcntl(fd, F_SETFL, 0);
tcgetattr(fd, &options);
cfsetispeed(&options, B38400);
cfsetospeed(&options, B38400);
options.c_cflag &= ~PARENB; /* Mask the character size to 8 bits, no parity */
options.c_cflag &= ~CSTOPB; /*1 Stop bit */
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8; /* Select 8 data bits */
options.c_cflag &= ~CRTSCTS; /* Disable hardware flow control */
//options.c_lflag |= ICANON; /* Canonical mode*/
//options.c_lflag |= ~(ICANON | ECHO | ECHOE); /* Enable data to be processed as Canonical input */
options.c_cflag |= (CLOCAL | CREAD); /* Enable the receiver and set local mode */
tcsetattr(fd, TCSANOW, &options); /* Set the new options for the port */
}
return (fd);
}
解决方案
我可以在我的串行监视器中看到该命令已正确发送,并且我收到一条完整的消息,包括回车。但是我的代码无法识别回车。
您程序的输出确实证实了@JonathanLeffler 的评论。接收到的回车被转换为换行符,从而终止规范的read()请求。尽管您的程序的 termios 配置不完整,但您会看到一些合理的结果。
您的程序确实使用首选方法配置了 termios 属性。
但是,如果您需要规范模式(即读取行),那么您的程序应该显式配置该模式,而不是让它碰运气(就像现在一样)。
options.c_lflag |= ICANON; /* Canonical mode*/
options.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);
您可能应该禁用回显,这样您就不会收到您发送的内容。
由于您希望该行以\r
而不是传统的 终止\n
,因此您还必须指定它。
options.c_cc[VEOL] = '\r';
options.c_iflag &= ~(INLCR | IGNCR | ICRNL);
禁用回车和换行翻译,绝对不要忽略回车。
请注意,您的代码确实禁用了硬件流控制,但将软件流控制(Xon/Xoff)留给了机会。
options.c_iflag &= ~(IXON | IXOFF | IXANY); /* no SW flowcontrol */
options.c_iflag &= ~(INPCK| IUCLC | IMAXBEL);
options.c_oflag &= ~OPOST;
信号生成也未配置任何一种方式。
附录
我只剩下一个奇怪的问题了。当我在 nbytes 上执行 printf 时,我的消息长度为 20 个字节,它告诉我我已收到所有 20 个字节。但是当我在缓冲区上执行 printf 时,它会切断第一个字符而不是打印 ATZ...ELM327 v1.5 它只打印 M327 v1.5
(1) 回显已关闭,因此read()不会拾取已发送的“ATZ\r”,除非连接的设备回显命令。这不太可能,因为此类设备通常配置为在由计算机程序而不是人类控制时不回显。
(2) printf()正在显示 的全部内容buffer
,其中包括 EOL 字符,例如回车符。输出这个接收到\r
的加上无关的空格(在字符串说明符和换行符之间)是破坏早期printf()输出的原因。
printf("current buffer %s \n",buffer);
^
除非您打算覆盖现有行,否则很少使用回车本身(即没有换行)(在 Linux 中)。在显示单独的回车时,您需要注意后果。
一种简单的解决方案是切换到 Linux 行终止,并将回车转换为换行符(即启用 ICRNL 而不是禁用它)。并将您的搜索更改\r
为\n
。
实际上,当正确配置(阻塞)规范输入时,连接到缓冲区的读取和搜索回车都是多余的。不需要读取循环,因为单个规范的read()请求可以确保返回一行输入。
推荐阅读
- node.js - 如何将对象值保存在变量中
- javascript - 打开新窗口并在新窗口中添加本地存储值
- kotlin - 如何做 livedata.postValue(any()) 的 coVerifyOrder - 它返回 io.mockk.MockKException:匹配模拟签名失败
- r - GGPlot2 中带有子组的森林图
- php - Ajax $_POST 发送到 php 时未设置
- c - 在C中,关闭程序后再次打开该程序然后如何存储该初始值
- amazon-web-services - 没有端口号,域无法工作
- java - 数组长度不同?
- ios - NSNotificationCenter 可以在不同框架之间进行通信吗?
- cmake - 不要展开 CMake 列表变量