C串口读取未发现回车 "\r"

标签 c serial-port carriage-return

我现在正在努力几天以通过串行初始化 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);
}

最佳答案

I can See in my Serial Monitor that the command is sent properly and i get a full Message including carriage return back . But my code does not recognize the carriage return.

您程序的输出确实证实了@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;

信号生成也没有以任何方式配置。


附录

I only have one weird issue left . My Message is 20 bytes Long when i do a printf on nbytes it tells me that i have received all 20 bytes . but when i do a printf on buffer it cuts off the first characters instead of printing ATZ...ELM327 v1.5 it only prints M327 v1.5

(1) 回声已关闭,因此 read() 不会接收发送的“ATZ\r”除非连接的设备回声命令回来。这不太可能,因为此类设备通常配置为在由计算机程序而不是人控制时不回声。

(2) printf() 正在显示buffer 的全部内容,其中包括回车等EOL 字符。输出这个接收到的 \rextraneous 空格(在字符串说明符和换行符之间)是破坏早期 printf() 输出的原因。

printf("current buffer %s \n",buffer);
                         ^

回车本身(即没有换行符)很少使用(在 Linux 中)除非你打算覆盖现有的行。您需要了解显示单独回车的后果。

一个简单的解决方案是切换到 Linux 行终止,并将回车符转换为换行符(即启用 ICRNL 而不是禁用它)。并将搜索 \r 更改为 \n

实际上,当正确配置(阻塞)规范输入时,串联读取缓冲区和搜索回车都是多余的。不需要读取循环,因为单个规范的 read() 请求可以确保返回一行输入。

关于C串口读取未发现回车 "\r",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51430495/

相关文章:

c - PIC C编译器RS232传输代码

c - 静态结构初始化 C

c - 同时标记匹配和转义匹配外的特殊字符

linux - 如何在 Linux 中创建一个将数据代理到真实设备的虚拟 io 设备?

python - 如何修复TypeError : an integer is required in python 3. 4、pyserial 2.7虚拟串口

python - sys.stdout.write 和\r 回车不工作

Java:如何通过忽略 "\n"逐行读取文件

mysql_errno 始终返回 0

c# - 如何在串口的DataReceived事件处理器中显示读取到的数据

windows - 为什么 Windows 使用 CR LF?