2

I'm trying to read NMEA message in Linux. But I can't get a completely message:

54.441,V,,,,,0.00,0.00,010720,,,N*42
$GPVTG,0.00,T,,M,0.00,N,0.00,K,N*32
$GPGGA,020954.441,,,,,0,0,,,M,,M,,*43
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,1,1,00*79
$GLGSV,1,1,00*65
$GPGLL,,,,,020954.441,V,N*71
$GP

The first line and last line is the one message but it have been splited. I thing, It's cause by sleep 1 second. And It's not right at all. I think I should use interrupt serial.

My idea is when data in come, interrupt serial will run a function which read serial and handle it. After that, system will sleep until next message. I searched some material but It's doesn't help.

This is my new code and it's not working:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <errno.h>
#include <termios.h>

void signal_handler_IO ();  

int fd;
int connected;
struct termios termAttr;
struct sigaction saio;

int main(int argc, char *argv[])
{
     fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
     if (fd == -1)
     {
        perror("open_port: Unable to open port\n");
        exit(1);
     }
     saio.sa_handler = signal_handler_IO;
     saio.sa_flags = 0;
     saio.sa_restorer = NULL; 
     sigaction(SIGIO,&saio,NULL);

     fcntl(fd, F_SETFL, FNDELAY);
     fcntl(fd, F_SETOWN, getpid());
     fcntl(fd, F_SETFL,  O_ASYNC );

     tcgetattr(fd,&termAttr);       
     cfsetispeed(&termAttr,B9600);  
     cfsetospeed(&termAttr,B9600);  
     termAttr.c_cflag &= ~PARENB;   
     termAttr.c_cflag &= ~CSTOPB;   
     termAttr.c_cflag &= ~CSIZE;
     termAttr.c_cflag |= CS8;         
     termAttr.c_cflag |= (CLOCAL | CREAD); 
     termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); 
     termAttr.c_iflag &= ~(IXON | IXOFF | IXANY); 
     termAttr.c_oflag &= ~OPOST; 
     tcsetattr(fd,TCSANOW,&termAttr);
     printf("UART1 configured....\n");

     while(1){
         sleep(1);
     }
     close(fd);
     exit(0);   
          
}

void signal_handler_IO ()
{
    FILE *csv;
    char buff [1024];
    int n = read(fd, &buff, sizeof(buff));
    char * token = strtok(buff, ",");
    csv=fopen("csvfile.csv","w");
    while( token != NULL ) {
      fprintf(csv,"%s\n",token);
      token = strtok(NULL, ","); 
    }
    fclose(csv);
}

What should I do now ?

ToanVnET
  • 105
  • 4
  • 14
  • 1
    `strtok(buff, ",")` expects `buff` to contain a _string_ (_null character_ terminated array of characters). Yet `read(fd, &buff, sizeof(buff));` does not certainly form a _string_. Try using `fgets()` to read a _line_ of data into a _string_. – chux - Reinstate Monica Jul 01 '20 at 03:05
  • Did you mean read data from serial by fgets() ? Can you write a specify code in my case ? I not familiar with fget(). And also try with: `fgets (buff ,sizeof(buff), fd);`. But I get: `Serial_Interrupt.c: In function ‘signal_handler_IO’: Serial_Interrupt.c:67:32: warning: passing argument 3 of ‘fgets’ makes pointer from integer without a cast [-Wint-conversion] fgets (buff ,sizeof(buff), fd); expected ‘FILE * restrict {aka struct _IO_FILE * restrict}’ but argument is of type ‘int’ extern char *fgets (char *__restrict __s, int __n, FILE *__restrict __stream)` – ToanVnET Jul 01 '20 at 03:20
  • 1
    Look to `fopen()` rather than `open()`. `fgets()` take a `FILE *`, not an `int`. – chux - Reinstate Monica Jul 01 '20 at 03:35
  • I update new code and result above. I compile without error but it's not working properly. – ToanVnET Jul 01 '20 at 03:49
  • 1
    "it's not working properly." is too vague. Good luck. – chux - Reinstate Monica Jul 01 '20 at 04:02
  • If I say like vague. You also can see the post. I updated to result on there. – ToanVnET Jul 01 '20 at 04:08
  • It's working rightnow. Thank you for your help @chux-ReinstateMonica. Can you make an answer and I will mark it as correctly. – ToanVnET Jul 01 '20 at 04:13
  • 2
    You probably shouldn't use a signal handler in this manner. `fopen` is _not_ signal-safe. It does a `malloc` internally, which is not signal-safe. And, why do `fopen` on a device that you already did `open` on? Better to put `select` and `read` in your loop in `main` and dispense with the signal handler altogether. And you can use the `FIONREAD` ioctl if necessary. Signals are _not_ like interrupts as I think you're viewing them, based on your code. – Craig Estey Jul 01 '20 at 04:18
  • Hi @CraigEstey, thank you for your positive response. Did you mean I should use read() function like my old code ? And actually, I have never working with `select` before. But I very interesting with your idea. And I hope you can show me more the way which apply on my case. – ToanVnET Jul 01 '20 at 04:25
  • 1
    Your code to read lines of text from a serial terminal makes no sense. You do not need a signal handler or use non-canonical mode or use non-blocking I/O. See https://stackoverflow.com/questions/57152937/canonical-mode-linux-serial-port/57155531#57155531 – sawdust Jul 01 '20 at 05:04
  • hi @sawdust, thank you for your response. If I don't use signal handler and interrupt, I will miss a brief of data as I mentioned above. I will try follow you idea and response soon. – ToanVnET Jul 01 '20 at 05:09
  • 1
    You need to read the linked answer. There's a link within to [serial drivers](http://www.linux.it/~rubini/docs/serial/serial.html) article that points out that your concepts are incorrect. The kernel driver will capture and buffer all the data that the UART receives. Your userspace program is merely fetching data from a system buffer, and is completely asynchronous with actual data reception. – sawdust Jul 01 '20 at 05:17
  • Thanks @sawdust, you make it more clearly now. Yes, I'm reading it. – ToanVnET Jul 01 '20 at 05:33
  • 1
    Does this answer your question? [How to read from serial port like picocom on Linux?](https://stackoverflow.com/questions/56757505/how-to-read-from-serial-port-like-picocom-on-linux) – sawdust Jul 01 '20 at 06:33
  • I just take a rest after lunch. Now. I'm reading the rest part. I will response soon. – ToanVnET Jul 01 '20 at 06:38
  • Thanks @sawdust, all of this is out of my knowledge. Your [answer](https://stackoverflow.com/questions/57152937/canonical-mode-linux-serial-port/57155531#57155531) is so good. – ToanVnET Jul 01 '20 at 07:09
  • @sawdust, I will use your code. Thanks with all respect ! – ToanVnET Jul 01 '20 at 07:24
  • Hi @sawdust, I have an issue. I'm using your solution and my program is focus on receive message from serial. I great but in case, serial not transfer anything, my program will stuck at read serial. Do you have any idea in this case ? I think interrupt is good way to solve that. But I quite vague about serial interrupt. I mean my program will run and run, if serial data in come, it will switch to handle it and then return to handle other stuffs. Thank you so much. (Should I create a new question for this). – ToanVnET Jul 02 '20 at 10:28
  • 1
    *"serial not transfer anything, my program will stuck at read serial"* -- That is the way sequential processing works. What do you want to do when there is no received data to process? *"Interrupt"* is not the solution. Threads is one solution, i.e. use what the OS offers, not some bare-metal hack. – sawdust Jul 03 '20 at 17:15
  • Thank you so much for advise. I'm reading about threads. – ToanVnET Jul 04 '20 at 01:57

2 Answers2

1

NMEA message are lines, ending with a '\n'.

Change read() to fgets() (open using fopen()) and read as a line into a string for later strtok() processing.

See also @Craig Estey ideas.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Reading from a serial terminal can utilize a line discipline (i.e. canonical mode) to retrieve lines of text, so using streams and **fopen()** is not necessary/advised. – sawdust Jul 01 '20 at 04:52
  • @sawdust Good idea. `fopen()/fgets()` is one of many options. Perhaps post an answer that expresses your idea. – chux - Reinstate Monica Jul 01 '20 at 04:54
1

This is prefaced by my top comment.

thank you for your positive response. Did you mean I should use read() function like my old code ? And actually, I have never working with select before. But I very interesting with your idea. And I hope you can show me more the way which apply on my case.

Okay, here's a simple [and untested] version that does not use a signal handler. And, I'm using poll instead of select [they are similar] because it's easier to use.

Note that you opened the TTY device file with O_NDELAY, so the read call is non-blocking.

Also note that the open device will not produce an EOF condition either the way you did it or the way I'm doing it.

So, you'll have to have code that looks for a line that signifies the last line (e.g. $GP).

Or, after an initial wait the first time in the loop, a subsequent timeout should indicate no more input

Anyway, here's the code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <errno.h>
#include <termios.h>
#if 1
#include <poll.h>
#endif

void signal_handler_IO();               /* definition of signal handler */

int fd;
struct termios termAttr;
struct sigaction saio;
struct pollfd fdpoll;

int
main(int argc, char *argv[])
{
    int timout;
    FILE *fout = NULL;
    int buf_has_GP = 0;
    int lastchr = -1;
    int curchr;
    int err;
    int rlen;
    int idx;
    char buf[1000];

    fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd == -1) {
        perror("open_port: Unable to open port\n");
        exit(1);
    }

#if 0
    saio.sa_handler = signal_handler_IO;
    saio.sa_flags = 0;
    saio.sa_restorer = NULL;
    sigaction(SIGIO, &saio, NULL);
#endif

    fcntl(fd, F_SETFL, FNDELAY);
    fcntl(fd, F_SETOWN, getpid());
    fcntl(fd, F_SETFL, O_ASYNC);

    tcgetattr(fd, &termAttr);
    cfsetispeed(&termAttr, B9600);
    cfsetospeed(&termAttr, B9600);
    termAttr.c_cflag &= ~PARENB;
    termAttr.c_cflag &= ~CSTOPB;
    termAttr.c_cflag &= ~CSIZE;
    termAttr.c_cflag |= CS8;
    termAttr.c_cflag |= (CLOCAL | CREAD);
    termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    termAttr.c_iflag &= ~(IXON | IXOFF | IXANY);
    termAttr.c_oflag &= ~OPOST;
    tcsetattr(fd, TCSANOW, &termAttr);
    printf("UART1 configured....\n");

    fout = fopen("csvfile.csv","w");

    fdpoll.fd = fd;
    fdpoll.events = POLLIN;
    timout = 10000;

    while (1) {
        err = poll(&fdpoll,1,timout);

        // timeout
        if (err == 0)
            break;

        // error
        if (err < 0) {
            fprintf(stderr,"error -- %s\n",strerror(errno));
            break;
        }

        // err will always be _one_ because poll's second arg is 1

        while (1) {
            rlen = read(fd,buf,sizeof(buf));
            if (rlen <= 0)
                break;

            fwrite(buf,1,rlen,fout);

            // need to check buf looking for last line (e.g. $GP)
            // to know when to stop
            // since read is _not_ line oriented we have to check for G followed
            // by P [and they may or may not occur in the same read call]
            // FIXME -- this is quite crude -- just to illustrate
            for (idx = 0;  idx < rlen;  ++idx) {
                curchr = buf[idx];
                buf_has_GP = ((lastchr == 'G') && (curchr == 'P'));
                if (buf_has_GP)
                    break;
                lastchr = curchr;
            }

            if (buf_has_GP)
                break;
        }

        if (buf_has_GP)
            break;

        timout = 1000;

#if 0
        sleep(1);
#endif
    }

    close(fd);
    fclose(fout);

    exit(0);
}

void
signal_handler_IO()
{
    FILE *csv;
    FILE *ff;
    char buff[1024];

    ff = fopen("/dev/ttyUSB0", "r");
    // int n = read(fd, &buff, sizeof(buff));
    fgets(buff, sizeof(buff), ff);
    char *token = strtok(buff, ",");

    csv = fopen("csvfile.csv", "w");
    while (token != NULL) {
        fprintf(csv, "%s\n", token);
        token = strtok(NULL, ",");
    }
    sleep(0.2);
    fclose(csv);
}

UPDATE:

Thank you so much for spend your time for me. I compiled it without error. Unfortunately, I get nothing from output and empty file.

This may have been because of the simple/crude EOF string detection code. I think it could have stopped prematurely. I've added more robust checking.

I've also added debug printing (if -d is given).

Because I don't have access to a real ttyUSB device, I've added test code using a PTY [pseudo TTY]. To use this, put the sample NMEA data into a file (e.g. input.txt) and add -pinput.txt to the arguments.

This was how I was able to debug the general program flow.

I've turned off any unnecessary fcntl options.

If, after trying this, you still have issues, you may wish to test your device interface with a terminal program (e.g. minicom) to verify that the remote device is, indeed, sending data.

If minicom produces output, but your program doesn't, you may have to modify some of the termios options.

Some usbtty/uart devices need RTS/CTS [I've actually used such a device for work]. minicom has a config option to deal with this.

In the program [although I suspect it's off by default], you may need to disable RTS/CTS hardware so that the port doesn't get hung up. And/or ensure that XON/XOFF flow control is disabled.

Or, the remote device needs RTS/CTS support [you have to somehow force the remote device to see CTS high]. Although unlikely, this might have to be done in the cable itself.

Anyway, here's the updated code:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <errno.h>
#include <termios.h>
#if 1
#include <poll.h>
#include <pty.h>
#include <sys/wait.h>
#include <time.h>
#endif

#ifndef RAWOUT
#define RAWOUT      1
#endif

void signal_handler_IO();               /* definition of signal handler */

const char *ttydev = "/dev/ttyUSB0";
int fd;

int opt_d;                              // 1=debug print
char *opt_pty;                          // PTY input file
int ptypid;
#define PTYSLP      1

FILE *fout = NULL;

struct termios termAttr;
struct sigaction saio;
struct pollfd fdpoll;

int linelen;
char linebuf[1000];

#define SHOWPOLL(_msk) \
    if (events & _msk) \
        bp += sprintf(bp,"/" #_msk)

typedef long long tsc_t;

tsc_t
tscget(void)
{
    struct timespec ts;
    tsc_t tsc;
    static tsc_t tsczero = 0;

    clock_gettime(CLOCK_REALTIME,&ts);

    tsc = ts.tv_sec;
    tsc *= 1000000000;
    tsc += ts.tv_nsec;

    if (tsczero == 0)
        tsczero = tsc;

    tsc -= tsczero;

    return tsc;
}

double
tscsec(tsc_t tsc)
{
    double sec;

    sec = tsc;
    sec /= 1e9;

    return sec;
}

void
tscprt(void)
{
    tsc_t tsc;

    tsc = tscget();

    printf("%.9f ",tscsec(tsc));
}

#define dbgprt(_fmt...) \
    do { \
        if (! opt_d) \
            break; \
        int sverr = errno; \
        tscprt(); \
        printf(_fmt); \
        errno = sverr; \
    } while (0)

// dopty -- generate pseudo TTY test device
void
dopty(void)
{
    int fdm;
    int fdtxt;
    int rlen;
    int wlen;
    int off;
    char buf[1000];

#if 0
    fdm = open("/dev/pts/ptmx",O_RDWR | O_NDELAY);
#else
    fdm = getpt();
#endif
    if (fdm < 0) {
        perror("dopty/open");
        exit(1);
    }
    dbgprt("dopty: GETPT fdm=%d\n",fdm);

    ttydev = ptsname(fdm);
    dbgprt("dopty: PTSNAME ttydev=%s\n",ttydev);

    grantpt(fdm);
    unlockpt(fdm);

    dbgprt("dopty: INPUT opt_pty=%s\n",opt_pty);

    do {
        ptypid = fork();

        if (ptypid != 0) {
            close(fdm);
            break;
        }

        // open sample test data file
        fdtxt = open(opt_pty,O_RDONLY);
        if (fdtxt < 0) {
            perror("dopty/open");
            exit(1);
        }

        sleep(PTYSLP);

        while (1) {
            rlen = read(fdtxt,buf,sizeof(buf));
            if (rlen <= 0)
                break;
            dbgprt("dopty: READ rlen=%d\n",rlen);

            for (off = 0;  off < rlen;  off += wlen) {
                wlen = rlen - off;
                wlen = write(fdm,&buf[off],wlen);
                dbgprt("dopty: WRITE wlen=%d\n",wlen);
            }
        }

        sleep(PTYSLP);

        dbgprt("dopty: CLOSE\n");
        close(fdtxt);
        close(fdm);

        sleep(PTYSLP);

        dbgprt("dopty: EXIT\n");
        exit(0);
    } while (0);
}

char *
showpoll(short events)
{
    char *bp;
    static char buf[1000];

    bp = buf;
    bp += sprintf(bp,"%4.4X",events);

    SHOWPOLL(POLLIN);
    SHOWPOLL(POLLPRI);
    SHOWPOLL(POLLOUT);
    SHOWPOLL(POLLRDHUP);
    SHOWPOLL(POLLERR);
    SHOWPOLL(POLLHUP);

    return buf;
}

// lineadd -- add character to line buffer
void
lineadd(int chr)
{
    char *bp;
    char buf[10];

    if (opt_d) {
        bp = buf;
        *bp = 0;
        if ((chr >= 0x20) && (chr <= 0x7E))
            bp += sprintf(bp," '%c'",chr);
        dbgprt("showchr: CHR chr=%2.2X%s\n",chr,buf);
    }

    linebuf[linelen++] = chr;
    linebuf[linelen] = 0;
}

// eoftst -- decide if current line is the last line
int
eoftst(void)
{
    static char *eofstr = "$GP\n";
    static int eoflen = 0;
    int stopflg = 0;

    if (eoflen == 0)
        eoflen = strlen(eofstr);

    stopflg = ((linelen == eoflen) && (memcmp(linebuf,eofstr,eoflen) == 0));

    dbgprt("eoftst: %s\n",stopflg ? "STOP" : "CONT");

    return stopflg;
}

int
main(int argc, char **argv)
{
    int timout;
    int buf_has_eof = 0;
    int curchr;
    int err;
    int rlen;
    int idx;
    char buf[1000];

    --argc;
    ++argv;

    setlinebuf(stdout);
    setlinebuf(stderr);

    for (;  argc > 0;  --argc, ++argv) {
        char *cp = *argv;

        if (*cp != '-')
            break;

        cp += 2;
        switch (cp[-1]) {
        case 'd':
            opt_d = ! opt_d;
            break;

        case 'p':
            opt_pty = (*cp != 0) ? cp : "input.txt";
            break;
        }
    }

    do {
        // create test device
        if (opt_pty != NULL) {
            dopty();
            break;
        }

        if (argc > 0) {
            ttydev = *argv;
            --argc;
            ++argv;
        }
    } while (0);

    dbgprt("main: TTYDEV ttydev=%s\n",ttydev);
    fd = open(ttydev, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd == -1) {
        perror("open_port: Unable to open port\n");
        exit(1);
    }

#if 0
    saio.sa_handler = signal_handler_IO;
    saio.sa_flags = 0;
    saio.sa_restorer = NULL;
    sigaction(SIGIO, &saio, NULL);
#endif

    // not needed unless doing signal handler
#if 0
    fcntl(fd, F_SETFL, FNDELAY);
    fcntl(fd, F_SETOWN, getpid());
    fcntl(fd, F_SETFL, O_ASYNC);
#endif

#if 1
    tcgetattr(fd, &termAttr);
#endif

#if 1
    cfsetispeed(&termAttr, B9600);
    cfsetospeed(&termAttr, B9600);
#endif

    // force immediate return from device read if no chars available
#if 1
    dbgprt("main: CC VMIN=%d VTIME=%d\n",
        termAttr.c_cc[VMIN],termAttr.c_cc[VTIME]);
    termAttr.c_cc[VMIN] = 0;
    termAttr.c_cc[VTIME] = 0;
#endif

    termAttr.c_cflag &= ~PARENB;
    termAttr.c_cflag &= ~CSTOPB;
    termAttr.c_cflag &= ~CSIZE;
    termAttr.c_cflag |= CS8;
    termAttr.c_cflag |= (CLOCAL | CREAD);

    // FIXME -- you may need to handle this
#if 1
    termAttr.c_cflag &= ~CRTSCTS;
#else
    termAttr.c_cflag |= CRTSCTS;
#endif

    termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    termAttr.c_iflag &= ~(IXON | IXOFF | IXANY);
    termAttr.c_oflag &= ~OPOST;

#if 1
    tcsetattr(fd, TCSANOW, &termAttr);
#endif
    printf("UART1 configured....\n");

    // open output file
    fout = fopen("csvfile.csv","w");
    if (fout == NULL) {
        perror("main/fopen");
        exit(1);
    }

    fdpoll.fd = fd;
    fdpoll.events = POLLIN;
    fdpoll.revents = 0;

    // set initial timeout of 10 seconds
    timout = 10000;

    // NOTE: iter is just for testing to prevent infinite looping if failure to
    // read or match the EOF string
    for (int iter = 1;  iter < 10;  ++iter) {
        dbgprt("main: POLL iter=%d events=%s timout=%d\n",
            iter,showpoll(fdpoll.events),timout);
        err = poll(&fdpoll,1,timout);

        dbgprt("main: POLL revents=%s err=%d\n",showpoll(fdpoll.revents),err);

        // timeout
        if (err == 0)
            break;

        // error
        if (err < 0) {
            fprintf(stderr,"error -- %s\n",strerror(errno));
            break;
        }

        // err will always be _one_ because poll's second arg is 1

        // process all data in current chunk
        while (1) {
            rlen = read(fd,buf,sizeof(buf));
            dbgprt("main: READ iter=%d rlen=%d\n",iter,rlen);
            if (rlen <= 0)
                break;

            // send data to output file
#if RAWOUT
            fwrite(buf,1,rlen,fout);
#endif

            // need to check buf looking for last line (e.g. $GP)
            // to know when to stop
            // since read is _not_ line oriented we have to check for G followed
            // by P [and they may or may not occur in the same read call]
            // FIXME -- this is quite crude -- just to illustrate
            for (idx = 0;  idx < rlen;  ++idx) {
                curchr = buf[idx];

                // add to line buffer
                lineadd(curchr);

                // wait for newline
                if (curchr != '\n')
                    continue;

                // decide if this is the last line of the current NMEA message
                buf_has_eof = eoftst();

#if (! RAWOUT)
                // do processing on line buffer ...
#endif

                // reset line buffer index/length for next line
                linelen = 0;

                if (buf_has_eof)
                    break;
            }

            if (buf_has_eof)
                break;
        }

        if (buf_has_eof)
            break;

        // set 1 second timeout for subsequent reads
        timout = 1000;

#if 0
        sleep(1);
#endif
    }

    close(fd);
    fclose(fout);

    // reap any child processes [only if doing PTY mode]
    while (opt_pty != NULL) {
        pid_t pid = wait(NULL);
        dbgprt("main: WAIT pid=%d\n",pid);
        if (pid <= 0)
            break;
    }

    exit(0);
}

void
signal_handler_IO()
{
    FILE *csv;
    FILE *ff;
    char buff[1024];

    ff = fopen("/dev/ttyUSB0", "r");
    // int n = read(fd, &buff, sizeof(buff));
    fgets(buff, sizeof(buff), ff);
    char *token = strtok(buff, ",");

    csv = fopen("csvfile.csv", "w");
    while (token != NULL) {
        fprintf(csv, "%s\n", token);
        token = strtok(NULL, ",");
    }
    sleep(0.2);
    fclose(csv);
}
Craig Estey
  • 30,627
  • 4
  • 24
  • 48
  • The comment in your code *"since read is _not_ line oriented ..."* is wrong. The serial terminal using the termios interface supports canonical reading of text lines. Study the [termios man page](https://man7.org/linux/man-pages/man3/termios.3.html). – sawdust Jul 01 '20 at 05:10
  • Thank you so much for spend your time for me. I compiled it without error. Unfortunately, I get nothing from output and empty file. – ToanVnET Jul 01 '20 at 05:11
  • [UPDATE] This is output: 'UART1 configured.... I/O possible' – ToanVnET Jul 01 '20 at 07:02
  • @sawdust Um, _no_ ... The app is using noncanonical mode (i.e. `ICANON` is turned off), so it produces raw input. And, even with canonical processing, there is no guarantee that `read` will return all bytes in a single read (e.g. `read(fd,buf,1)`), so you still have to process things byte at a time. – Craig Estey Jul 01 '20 at 17:22