When I’m working on embedded systems (running Linux), one of the most common task is waiting for data on the serial line.

Sometimes the read must be a blocking read, more often I need to do a non blocking read over the serial line.

Let’s start with an axample:

Imagine the EmbSys has to receive commands from an external device, and then it must execute some jobs depending on the commands received, in this case the blocking read is very useful since the EmbSys has to work only after he has received a command.

In this case I normally something like the following:

open_serial()

wait_for_command()

switch (received_command)

{

    Com1: do some stuff;

    Com2: do some other stuff;

    Com(n): do some other stuff;

}

Running on Linux, this pseudo code becomes:




/*+----------------------------------------------------------------+
  | SYSTEM INCLUDES                                                |
  +----------------------------------------------------------------+*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <termio.h>
#include <pthread.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>

int readString(int fd, char *buffer, unsigned int length)
{
    int    sRetVal;
    int    i;

    memset(buffer, 0, length);
    for(i=0; i<length; i++)
    {
        sRetVal = read(fd, &buffer[i], 1);
        if(sRetVal==-1)
        {
            perror("reading on fd");
            return(-2); /* reading error */
        }
    }
    return(i);
}

int main(int argc, char **argv)
{
    char string[10];
    int serialFd;
    struct termio tbuf;
    struct termios sOldTermio;
    int retval = 0;
    /* Open the serial line */
    serialFd = open("/dev/ttyS0", O_RDWR | O_NDELAY | O_NONBLOCK);
    if (serialFd == -1)
    {
        exit(EXIT_FAILURE);
    }
    /* get the current tty setting */
    if (tcgetattr(serialFd, &sOldTermio) != 0)
    {
        close(serialFd);
        exit(EXIT_FAILURE);
    }

    /* elimina il flag no_delay */
    if (fcntl(serialFd,F_SETFL,fcntl(serialFd,F_GETFL,0) & ~FNDELAY) != 0)
    {
        exit(EXIT_FAILURE);
    }

    if (ioctl(serialFd,TCGETA,&tbuf)==-1)
    {
        exit(EXIT_FAILURE);
    }
    tbuf.c_cflag = (tbuf.c_cflag & (~CBAUD)) | B115200;
    tbuf.c_lflag &= ~ICANON;
    tbuf.c_lflag &= ~ECHO;
    tbuf.c_iflag &= ~(INLCR | ICRNL | IUCLC | ISTRIP | IXON | BRKINT);
    tbuf.c_oflag &= ~OPOST;
    tbuf.c_lflag &= ~ISIG;

    tbuf.c_cc[4] = 0;  /* min */
    tbuf.c_cc[5] = 64; /* time */
    if (ioctl(serialFd,TCSETAF,&tbuf)==-1)
    {
        exit(EXIT_FAILURE);
    }
    retval = readString(serialFd, string, 10);
    if(retval<10)
    {
        printf("Error while reading\n");
    }
    else
    {
        printf("string  completely received\n");
        printf("Received string: %s \tlength = %d\n", string, retval);
    }
}

Second Example:

If the EmbSys asks for data to a device (let’s say a temperature sensor) over a serial line, and then the sensor has to reply, in this case I use the non blocking read over the serial for two reasons:

– if the external device is broken the EmbSys will never receive an answer, and it will be blocked forever.

– If the data I asked for are very important, if I use a non blocking I/O I can decide how to go on if I haven’t received the response during a predetermined interval of time.

In this case I normally something like the following:

open_serial()

non_block_read()

if timeout

    print The device is not working fine

else

    do_something with received data

 

Running on Linux, this pseudo code becomes:

  1 /*+----------------------------------------------------------------+
  2   | SYSTEM INCLUDES                                                |
  3   +----------------------------------------------------------------+*/
  4 #include <stdio.h>
  5 #include <stdlib.h>
  6 #include <unistd.h>
  7 #include <termios.h>
  8 #include <termio.h>
  9 #include <pthread.h>
 10 #include <string.h>
 11 #include <sys/time.h>
 12 #include <sys/types.h>
 13 #include <sys/stat.h>
 14 #include <sys/ioctl.h>
 15 #include <fcntl.h>
 16 
 17 int nonBlockingRead(int fd, char *buffer, unsigned int length, unsigned int timeout)
 18 {
 19     fd_set rfds;
 20     struct timeval tv;
 21     int    retval;
 22     int    sRetVal;
 23     int    i;
 24 
 25     FD_ZERO(&rfds);
 26     FD_SET(fd, &rfds);
 27 
 28     tv.tv_sec     =  timeout/1000000;
 29     tv.tv_usec    =  timeout%1000000;
 30 
 31     memset(buffer, 0, length);
 32     for(i=0; i<length; i++)
 33     {
 34         retval=select(fd+1, &rfds, NULL, NULL, &tv);
 35         if(retval==-1)
 36         {
 37             perror("select");
 38             return(-1);
 39         }
 40         else if(retval)
 41         {
 42             sRetVal = read(fd, &buffer[i], 1);
 43             if(sRetVal==-1)
 44             {
 45                 perror("reading on fd");
 46                 return(-2); /* reading error */
 47             }
 48         }
 49         else
 50         {
 51             /* timeout */
 52             break;
 53         }
 54     }
 55     return(i);
 56 }
 57 
 58 int main(int argc, char **argv)
 59 {
 60     char string[10];
 61     int serialFd;
 62     struct termio tbuf;
 63     struct termios sOldTermio;
 64     int retval = 0;
 65     /* Open the serial line */
 66     serialFd = open("/dev/ttyS0", O_RDWR | O_NDELAY | O_NONBLOCK);
 67     if (serialFd == -1)
 68     {
 69         exit(EXIT_FAILURE);
 70     }
 71     /* get the current tty setting */
 72     if (tcgetattr(serialFd, &sOldTermio) != 0)
 73     {
 74         close(serialFd);
 75         exit(EXIT_FAILURE);
 76     }
 77 
 78     /* elimina il flag no_delay */
 79     if (fcntl(serialFd,F_SETFL,fcntl(serialFd,F_GETFL,0) & ~FNDELAY) != 0)
 80     {
 81         exit(EXIT_FAILURE);
 82     }
 83 
 84     if (ioctl(serialFd,TCGETA,&tbuf)==-1)
 85     {
 86         exit(EXIT_FAILURE);
 87     }
 88     tbuf.c_cflag = (tbuf.c_cflag & (~CBAUD)) | B115200;
 89     tbuf.c_lflag &= ~ICANON;
 90     tbuf.c_lflag &= ~ECHO;
 91     tbuf.c_iflag &= ~(INLCR | ICRNL | IUCLC | ISTRIP | IXON | BRKINT);
 92     tbuf.c_oflag &= ~OPOST;
 93     tbuf.c_lflag &= ~ISIG;
 94 
 95     tbuf.c_cc[4] = 0;  /* min */
 96     tbuf.c_cc[5] = 64; /* time */
 97     if (ioctl(serialFd,TCSETAF,&tbuf)==-1)
 98     {
 99         exit(EXIT_FAILURE);
100     }
101     retval = nonBlockingRead(serialFd, string, 10, 1000000);
102     if(retval<0)
103     {
104         printf("Error while reading\n");
105     }
106     else
107     {
108         if(retval<10)
109             printf("string not completely received\n");
110         else
111             printf("string  completely received\n");
112         printf("Received string: %s \tlength = %d\n", string, retval);
113     }
114 }

that’s all.

Gg1