Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
747 views
in Technique[技术] by (71.8m points)

c++ - How to read from serial port like picocom on Linux?

I have a gps module that sends data (NMEA sentence) every 1 seconds to the serial port. I've been trying to read it from a c++ program.

When reading the serial port with picocom, data is displayed in a clean way, each line has a NMEA sentence).

Output from picocom

The result of my program is close but lines are sometimes mixed.

Output from my program

This is my code:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <fcntl.h> 
#include <errno.h> 
#include <termios.h> 
#include <unistd.h> 

int main(){

    struct termios tty;
    memset(&tty, 0, sizeof tty);

    int serial_port = open("/dev/ttyUSB0", O_RDWR);

    // Check for errors
    if (serial_port < 0) {
        printf("Error %i from open: %s
", errno, strerror(errno));
    }

        // Read in existing settings, and handle any error
    if(tcgetattr(serial_port, &tty) != 0) {
        printf("Error %i from tcgetattr: %s
", errno, strerror(errno));
    }

    tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common)
    tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common)
    tty.c_cflag |= CS8; // 8 bits per byte (most common)
    tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common)
    tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1)
    tty.c_lflag &= ~ICANON;
    tty.c_lflag &= ~ECHO; // Disable echo
    tty.c_lflag &= ~ECHOE; // Disable erasure
    tty.c_lflag &= ~ECHONL; // Disable new-line echo
    tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP
    tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes
    tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
    tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
    tty.c_cc[VTIME] = 10;   
    tty.c_cc[VMIN] = 0;
    // Set in/out baud rate to be 9600
    cfsetispeed(&tty, B9600);
    cfsetospeed(&tty, B9600);

    // Save tty settings, also checking for error
    if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
        printf("Error %i from tcsetattr: %s
", errno, strerror(errno));
    }

    // Allocate memory for read buffer, set size according to your needs
    char read_buf [24];
    memset(&read_buf, '', sizeof(read_buf));

    while(1){
        int n = read(serial_port, &read_buf, sizeof(read_buf));
        std::cout << read_buf ;
    }

    return 0;
}

How does picocom manage to display data correctly? Is is due to my buffer size or maybe VTIME and VMIN flags ?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Your are getting "framing" errors.

You cannot rely on read() to always get exactly one NMEA sentence from beginning to end.

You need to add the data read to the end of a buffer, then detect the start and end of each NMEA sentence in the buffer, removing each detected sentence from the beginning of the buffer as it is found.

Like this:

FOREVER
  read some data and add to end of buffer
  if start of buffer does not have start of NMEA sentence
    find start of first NMEA sentence in buffer
    if no sentence start found
      CONTINUE
    delete from begining of buffer to start of first sentence
  find end of first NMEA sentence in buffer
  if no sentence end in buffer
    CONTINUE
  remove first sentence from buffer and pass to processing

It is important, if you expect a NMEA application to work reliably in the real world, to handle framing errors. This sort of thing:

         received                                       output
$GPRMC,,V,,,,,,,,,N*53
                                                $GPRMC,,V,,,,,,,,,N*53
$GPVTG,,,,,,,,N*30
                                                $GPVTG,,,,,,,,N*30
$GPRMC,,V,,,,,,,,,N*53$GPVTG,,,,,,,,N*30
                                                $GPRMC,,V,,,,,,,,,N*53
                                                $GPVTG,,,,,,,,N*30
$GPRMC,,V,,,
                                                ----
,,,,,,N*53
                                                $GPRMC,,V,,,,,,,,,N*53

The code to do this is available at https://gist.github.com/JamesBremner/291e12672d93a73d2b39e62317070b7f


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...