简体   繁体   中英

C serial read does not find carriage return “\r”

I am battling now for a few days to Initialize a ELM237 OBD2 Adapter via Serial. The device i am connecting to sends a response that always ends with a carriage return. I can Send commands without problems. The response is also send by the device as expected. I am trying to read from the serial port until i read the carriage return. 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.

Here is what i receive on my Serial Monitor :

[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..> 

This is the Output of my programm :

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

Code:

#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.

The output from your program does confirm what @JonathanLeffler commented on. The received carriage returns are being translated to newlines, which are terminating the canonical read() requests. You're seeing somewhat reasonable results inspite of an incomplete termios configuration by your program.

Your program does configure the termios attributes using the preferred method.
But if you require canonical mode (ie read lines), then your program should explicitly configure that mode, rather than leave it to chance (as it does now).

options.c_lflag |= ICANON;                        /* Canonical mode*/
options.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);

You probably should disable echoing, so that you do not receive back what you sent.

Since you expect the line to terminate with \\r rather than the conventional \\n , you must also specify that.

options.c_cc[VEOL] = '\r';
options.c_iflag &= ~(INLCR | IGNCR | ICRNL);

Disable both carriage return and newline translations, and definitely do not ignore carriage returns.


Note that your code does disable hardware flow-control, but leaves software flow-control (Xon/Xoff) to chance.

options.c_iflag &= ~(IXON | IXOFF | IXANY);   /* no SW flowcontrol */
options.c_iflag &= ~(INPCK| IUCLC | IMAXBEL);
options.c_oflag &= ~OPOST;

Signal generation is also not configured either way.


ADDENDUM

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) Echo has been turned off, so the read() will not pick up the "ATZ\\r" that was sent unless the connected device echoes the command back. That's unlikely because such devices are typically configured to not echo when controlled by a computer program instead of a human.

(2) The printf() is displaying the entire contents of the buffer , which includes the EOL character such as the carriage return. Outputting this received \\r combined with the extraneous whitespace (between the string specifier and newline) is what clobbers the earlier printf() output.

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

A carriage return by itself (ie without a line feed) is rarely used (in Linux) unless you intend to overwrite the existing line. You need to be aware of the consequences when displaying solitary carriage returns.

One simple solution would be to switch to Linux line termination, and translate carriage returns to newlines (ie enable ICRNL instead of disabling it). And change your search for \\r to \\n .

Actually the concatenated reads into the buffer and search for carriage return are all superfluous when (blocking) canonical input is properly configured. There is no need for a read loop because a single canonical read() request is assured of returning with a line of input.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM