We help IT Professionals succeed at work.

How to improve serial communication

Rocco Galati
Rocco Galati used Ask the Experts™
on
I'm using the code proposted by Sarabande in one of my previous questions Read and Write on Serial Port under Ubuntu and C/C++ and it works very well even if I found out that sometimes the code stucks immediately before the while() loop without throwing any errors, it just get stuck and nothing happens. Then I run again the code and it works fine.

The device connected to the serial port is a digital scale and it works in this way when it receives
READ<CR><LF>

Open in new window

:
01ST,GS, 0.0,kg<CR><LF>
where
01       code to use only for 485 communications
ST             scale status:
            US - measurement not stable
            ST - measurement stable
            OL - overload weight
            UL - underload weight
            TL - scale non balanced
,              ASCII 044
GS Tipo di dato di peso (2 chars)
,              ASCII 044
0.0             weight
,              ASCII 044
kg             measurement unit (2 caratteri)
<CR><LF> end of packet ASCII 013 e ASCII 010

Why the serial read gets stuck sometimes?
Is there any way to detect if the while() loop is not working in order to skip the operation?
Can it be related to the parameters of the serial port?

This is my current code:
char buf[80] = {'\0' }; 
int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}

 const char *portname = "/dev/ttyS4";
    int fd;
    int wlen;
    printf("Opening the connection on serial port\n");
    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
        printf("Error opening %s: %s\n", portname, strerror(errno));

    } else {
    printf("Bilancia presente\n");
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    //set_mincount(fd, 0);                /* set to pure timed read */

    /* simple output */
    printf("Sending the command READ\n");
    wlen = write(fd, "READ\r\n", 6);
    if (wlen != 6) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    tcdrain(fd);    /* delay for output */


        int rdlen = 0, total = 0, timeoutcnt = 0;
      //*************** SOMETIMES IT GETS STUCK BEFORE THE WHILE *********************//
        while ((rdlen = read(fd, &buf[total], sizeof(buf) - total - 1)) >= 0 && total + rdlen < (int)sizeof(buf))  {
            if (rdlen == 0) {
              //  printf("Timeout from read\n");
                if (++timeoutcnt > 100)
                       break;
                continue;
            }
           // printf("%s", buf);
            total += rdlen;
            if (total > 1 && strcmp(&buf[total-2], "\r\n") == 0)
                  break;
        }
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (total + rdlen >= (int)sizeof(buf)) {
           printf("message too long for buffer: total:%d rdlen:%d: %s\n", total, rdlen, buf);
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
        }  else {
            printf("Success. total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }
    }

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Dr. KlahnPrincipal Software Engineer

Commented:
Try sending the device a single <CR> before entering the loop, wait 100 milliseconds, then flush the buffer.  If the device was hung on an incomplete previous command this may clear the issue.
Rocco GalatiR&D Engineer

Author

Commented:
Should I send the <CR> before the first write function and after that and before the while()?

I thought to add it before the first write() function in this way:

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

double get_double(const char *str)
{
    /* First skip non-digit characters */
    /* Special case to handle negative numbers and the `+` sign */
    while (*str && !(isdigit(*str) || ((*str == '-' || *str == '+') && isdigit(*(str + 1)))))
        str++;

    /* The parse to a double */
    return strtod(str, NULL);
}

char buf[80] = {'\0' };  // buffer is zero'ed

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}


int main()
{
    const char *portname = "/dev/ttyS4";
    int fd;
    int wlen;
    printf("Opening the connection on serial port\n");
    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
        printf("Error opening %s: %s\n", portname, strerror(errno));
        return -1;
    }
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    //set_mincount(fd, 0);                /* set to pure timed read */

    /*************** SEND <CR> ***************/
    printf("Sending only CR\n");
    wlen = write(fd, "\r\n", 2);
    if (wlen != 2) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    tcdrain(fd);    /* delay for output */

    sleep(100);
    // fflush(buf);
    /*************** SEND <CR> ***************/

    /* simple output */
    printf("Sending the command READ\n");
    wlen = write(fd, "READ\r\n", 6);
    if (wlen != 6) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    tcdrain(fd);    /* delay for output */


   	
        int rdlen = 0, total = 0, timeoutcnt = 0;
	printf("Entering the while loop..\n");
        while ((rdlen = read(fd, &buf[total], sizeof(buf) - total - 1)) >= 0 && total + rdlen < (int)sizeof(buf))  {
            if (rdlen == 0) { 
              //  printf("Timeout from read\n"); 
                if (++timeoutcnt > 100)
                       break;
                continue;
            }
           // printf("%s", buf);
            total += rdlen;
            if (total > 1 && strcmp(&buf[total-2], "\r\n") == 0)
                  break;
        } 
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (total + rdlen >= (int)sizeof(buf)) {
           printf("message too long for buffer: total:%d rdlen:%d: %s\n", total, rdlen, buf);
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
        }  else {            
            printf("Success. total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }


printf("Stringa: %s and number: %f\n", buf, get_double(buf));  

}

Open in new window


even if I cannot fflush the buf because it is not a FILE stream. How can i do that?
nociSoftware Engineer
Distinguished Expert 2018

Commented:
Readflushing can be done by reading with a timeout and not error out on timeout,.
If the timeout occurs the buffer is empty. (timeout should be short 100ms should be sufficient)
Rocco GalatiR&D Engineer

Author

Commented:
Can you provide me an example of how to do this, please?
Duncan RoeSoftware Developer

Commented:
Leave ICANON on - you're not doing per-character processing. Turn on IGNCR: that will give you a line with a terminating newline (like reading from a file).
To flush everything before the initial write(), do tcflush(fd, TCIOFLUSH);
Rocco GalatiR&D Engineer

Author

Commented:
Aren't ICANON and IGNCR already ON in my code?

tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);

Open in new window


About the flush, do you mean just in this way?

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

double get_double(const char *str)
{
    /* First skip non-digit characters */
    /* Special case to handle negative numbers and the `+` sign */
    while (*str && !(isdigit(*str) || ((*str == '-' || *str == '+') && isdigit(*(str + 1)))))
        str++;

    /* The parse to a double */
    return strtod(str, NULL);
}

char buf[80] = {'\0' };  // buffer is zero'ed

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}


int main()
{
    const char *portname = "/dev/ttyS4";
    int fd;
    int wlen;
    printf("Opening the connection on serial port\n");
    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
        printf("Error opening %s: %s\n", portname, strerror(errno));
        return -1;
    }
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    //set_mincount(fd, 0);                /* set to pure timed read */

    tcflush(fd, TCIOFLUSH);

    /* simple output */
    printf("Sending the command READ\n");
    wlen = write(fd, "READ\r\n", 6);
    if (wlen != 6) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    tcdrain(fd);    /* delay for output */


   	
        int rdlen = 0, total = 0, timeoutcnt = 0;
	printf("Entering the while loop..\n");
        while ((rdlen = read(fd, &buf[total], sizeof(buf) - total - 1)) >= 0 && total + rdlen < (int)sizeof(buf))  {
            if (rdlen == 0) { 
              //  printf("Timeout from read\n"); 
                if (++timeoutcnt > 100)
                       break;
                continue;
            }
           // printf("%s", buf);
            total += rdlen;
            if (total > 1 && strcmp(&buf[total-2], "\r\n") == 0)
                  break;
        } 
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (total + rdlen >= (int)sizeof(buf)) {
           printf("message too long for buffer: total:%d rdlen:%d: %s\n", total, rdlen, buf);
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
        }  else {            
            printf("Success. total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }


printf("Stringa: %s and number: %f\n", buf, get_double(buf));  

}

Open in new window

nociSoftware Engineer
Distinguished Expert 2018

Commented:
No they are masked off... This is to turn on:  also break should be ignored.
 
tty.c_lflag |= ICANON;
tty.c_iflag |= (IGNCR | IGNBRK);

Open in new window

Rocco GalatiR&D Engineer

Author

Commented:
what is the purpose of IGNCR ? should I have to change the string I send now? or is it related to what the device answers after the write function?
nociSoftware Engineer
Distinguished Expert 2018

Commented:
IGNCR has no effect on sending..
It means that with reading the data CR is not handled in a special way.
It also will not terminate the IO. I am not sure if you want to ignore that, you have to test with and without.
If you are reading character by character (read(fd,&buf, 1) ) then you probably want IGNCR otherwise mask the bit.
Duncan RoeSoftware Developer

Commented:
IGNCR means a received CR is thrown away.
Your call to tcflush() looks OK, but I would check the return code and report any error.
With ICANON now turned on, I think you can safely expect to read a whole line at a time.
On line 113, only test for "\n" - with IGNCR you will not see "\r" (IGNCR == IGNore Carriage Return).
Rocco GalatiR&D Engineer

Author

Commented:
I'm sorry for the delay in my answer, but I had several problems with my internet connection.

How can I check the return code?

I tried to use the
tcflush()

Open in new window

and these configuration flags:

tty.c_lflag |= ICANON;
tty.c_iflag |= (IGNCR | IGNBRK);

Open in new window


so my code looks like this:

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

double get_double(const char *str)
{
    /* First skip non-digit characters */
    /* Special case to handle negative numbers and the `+` sign */
    while (*str && !(isdigit(*str) || ((*str == '-' || *str == '+') && isdigit(*(str + 1)))))
        str++;

    /* The parse to a double */
    return strtod(str, NULL);
}

char buf[80] = {'\0' };  // buffer is zero'ed

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(BRKINT | PARMRK | ISTRIP | INLCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;
    tty.c_lflag |= ICANON;
    tty.c_iflag |= (IGNCR | IGNBRK);

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}


int main()
{
    const char *portname = "/dev/ttyS4";
    int fd;
    int wlen;
    printf("Opening the connection on serial port\n");
    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
        printf("Error opening %s: %s\n", portname, strerror(errno));

    } else {
    printf("Bilancia presente\n");
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    //set_mincount(fd, 0);                /* set to pure timed read */

    tcflush(fd, TCIOFLUSH);

    /* simple output */
    printf("Sending the command READ\n");
    wlen = write(fd, "READ\r\n", 6);
    if (wlen != 6) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }

    tcdrain(fd);    /* delay for output */


        int rdlen = 0, total = 0, timeoutcnt = 0;
        printf("valore prima: %d\n", rdlen);

        while ((rdlen = read(fd, &buf[total], sizeof(buf) - total - 1)) >= 0 && total + rdlen < (int)sizeof(buf))  {
            printf("valore %d\n", rdlen);
            if (rdlen == 0) {
                printf("Timeout from read\n");
                if (++timeoutcnt > 100)
                       break;
                continue;
            }
           // printf("%s", buf);
            total += rdlen;
            if (total > 1 && strcmp(&buf[total-2], "\r\n") == 0)
                  break;
        }
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (total + rdlen >= (int)sizeof(buf)) {
           printf("message too long for buffer: total:%d rdlen:%d: %s\n", total, rdlen, buf);
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
        }  else {
            printf("Success. total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }
    }


printf("Stringa: %s and number: %f\n", buf, get_double(buf));  

}

Open in new window


but the code still blocks and only prints:

Opening the connection on serial port
Bilancia presente
Sending the command READ
valore prima: 0
valore 18

Open in new window


and then nothing happens.
Shouldn't the code exit if the READ function doesn't work correctly? Why does the program hang instead?


EDIT: I realized that sometimes I get "ERR04" as answer instead of
01ST,GS,     0.0,kg<CR><LF>

Open in new window

Duncan RoeSoftware Developer

Commented:
You are getting 18 characters, which looks good.
At line 118, you are still testing for "\r\n" although you have turned on IGNCR.
Your best bet is to run your program under the gdb debugger, set a breakpoint at line 118, and see what the data looks like.
FWIW, my guess at the required line 118 is:
if (total > 1 && buf[total-1] == '\n')

Open in new window

but do check for yourself. Be aware, buf will not be nul-terminated
nociSoftware Engineer
Distinguished Expert 2018

Commented:
man tcflush  is your friend.... it tells how this function should be called and its declaration and return value:
0 = success
-1 = error, check errno for actual error.
Rocco GalatiR&D Engineer

Author

Commented:
This is what I get when I run the program and I use cat the serial port in another window:

cat /dev/ttyS4
ST,GS,   -2.30,kg
ST,GS,   -2.30,kg
ERR04
ST,GS,   -2.30,kg
ST,GS,   -2.30,kg
ST,GS,   -2.30,kg

Open in new window


This is my current code with the correction on line 118 and the returning value for tcflush:

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

double get_double(const char *str)
{
    /* First skip non-digit characters */
    /* Special case to handle negative numbers and the `+` sign */
    while (*str && !(isdigit(*str) || ((*str == '-' || *str == '+') && isdigit(*(str + 1)))))
        str++;

    /* The parse to a double */
    return strtod(str, NULL);
}

char buf[80] = {'\0' };  // buffer is zero'ed

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(BRKINT | PARMRK | ISTRIP | INLCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;
    tty.c_lflag |= ICANON;
    tty.c_iflag |= (IGNCR | IGNBRK);

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}


int main()
{

    const char *portname = "/dev/ttyS4";
    int fd;
    int wlen; 
    int ret=0;

    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
        printf("Error opening %s: %s\n", portname, strerror(errno));

    } else {
    printf("Bilancia presente\n");
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    //set_mincount(fd, 0);                /* set to pure timed read */

    ret = tcflush(fd, TCIOFLUSH);
    printf("return value: %d\n", ret);			
    /* simple output */
    printf("Sending the command READ\n");
    wlen = write(fd, "READ\r\n", 6);
    if (wlen != 6) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }

    tcdrain(fd);    /* delay for output */


        int rdlen = 0, total = 0, timeoutcnt = 0;
        printf("valore prima: %d\n", rdlen);

        while ((rdlen = read(fd, &buf[total], sizeof(buf) - total - 1)) >= 0 && total + rdlen < (int)sizeof(buf))  {
            printf("valore %d\n", rdlen);
            if (rdlen == 0) {
                printf("Timeout from read\n");
                if (++timeoutcnt > 100)
                       break;
                continue;
            }
            printf("%s", buf);
            total += rdlen;
            if (total > 1 && strcmp(&buf[total-1], "\n") == 0)
                  break;
        }
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (total + rdlen >= (int)sizeof(buf)) {
           printf("message too long for buffer: total:%d rdlen:%d: %s\n", total, rdlen, buf);
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
        }  else {
            printf("Success. total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }
    }


printf("Stringa: %s and number: %f\n", buf, get_double(buf));  

}

Open in new window


The problem is that the code never reaches the end of the program and get stuck on "valore prima:"

./test 
Bilancia presente
return value: 0
Sending the command READ
valore prima: 0

Open in new window


only 1 time on 10 it returned this:

Bilancia presente
return value: 0
Sending the command READ
valore prima: 0
valore 6
ERR04
Success. total:6 rdlen:6  buf:ERR04

Stringa: ERR04
 and number: 4.000000

Open in new window

Duncan RoeSoftware Developer

Commented:
Line 120 (was 118) is still wrong. You cannot use strcmp here - buf is not nul-terminated. Do it the way I showed you before.
And get into the debugger.
Rocco GalatiR&D Engineer

Author

Commented:
I modified the code as you suggested and now it works sometimes and other times it get stuck again.
Sometimes, the serial device answers after receiving the READ command, but I can't get the answer from my code (I see it in the other terminal).

This is the output:

robodyne@robodyne-isola1:~$ ./test 
Bilancia presente
return value: 0
Sending the command READ
valore prima: 0
valore 18
ST,GS,   -2.37,kg
Success. total:18 rdlen:18  buf:ST,GS,   -2.37,kg

Stringa: ST,GS,   -2.37,kg
 and number: -2.370000
robodyne@robodyne-isola1:~$ ./test 
Bilancia presente
return value: 0
Sending the command READ
valore prima: 0
^C  
robodyne@robodyne-isola1:~$ ./test 
Bilancia presente
return value: 0
Sending the command READ
valore prima: 0
^C

Open in new window


from the other terminal, I try to sniff the serial port and I noticed that sometimes the device answers with the result while other times it seems that it doesn't receive any command string.

I can't understand why it happens!

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

double get_double(const char *str)
{
    /* First skip non-digit characters */
    /* Special case to handle negative numbers and the `+` sign */
    while (*str && !(isdigit(*str) || ((*str == '-' || *str == '+') && isdigit(*(str + 1)))))
        str++;

    /* The parse to a double */
    return strtod(str, NULL);
}

char buf[80] = {'\0' };  // buffer is zero'ed

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(BRKINT | PARMRK | ISTRIP | INLCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;
    tty.c_lflag |= ICANON;
    tty.c_iflag |= (IGNCR | IGNBRK);

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}


int main()
{

    const char *portname = "/dev/ttyS4";
    int fd;
    int wlen; 
    int ret=0;

    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
        printf("Error opening %s: %s\n", portname, strerror(errno));

    } else {
    printf("Bilancia presente\n");
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    //set_mincount(fd, 0);                /* set to pure timed read */

    ret = tcflush(fd, TCIOFLUSH);
    printf("return value: %d\n", ret);			
    /* simple output */
    printf("Sending the command READ\n");
    wlen = write(fd, "READ\r\n", 6);
    if (wlen != 6) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }

    tcdrain(fd);    /* delay for output */


        int rdlen = 0, total = 0, timeoutcnt = 0;
        printf("valore prima: %d\n", rdlen);

        while ((rdlen = read(fd, &buf[total], sizeof(buf) - total - 1)) >= 0 && total + rdlen < (int)sizeof(buf))  {
            printf("valore %d\n", rdlen);
            if (rdlen == 0) {
                printf("Timeout from read\n");
                if (++timeoutcnt > 100)
                       break;
                continue;
            }
            printf("%s", buf);
            total += rdlen;
            if (total > 1 && buf[total-1] == '\n')
                  break;
        }
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (total + rdlen >= (int)sizeof(buf)) {
           printf("message too long for buffer: total:%d rdlen:%d: %s\n", total, rdlen, buf);
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
        }  else {
            printf("Success. total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }
    }


printf("Stringa: %s and number: %f\n", buf, get_double(buf));  

}

Open in new window

nociSoftware Engineer
Distinguished Expert 2018

Commented:
Try to send a "\n"  on startup.. the device might need some activation.
opening/closing a port might give some hardware pulses depends on hardware init etc.

When timeout occurs try to send a new command. Just waiting will not magically restart the balance.
Rocco GalatiR&D Engineer

Author

Commented:
Same problem, I tried to send a "\n" then the flush and then to send the real command, but it gets stuck the same sometimes:

./test 
Bilancia presente
return value1: 0
Sending a pulse
return value: 0
Sending the command READ
valore prima: 0
valore 18
ST,GS,   -2.37,kg
Success. total:18 rdlen:18  buf:ST,GS,   -2.37,kg

Stringa: ST,GS,   -2.37,kg
 and number: -2.370000
robodyne@robodyne-isola1:~$ ./test 
Bilancia presente
return value1: 0
Sending a pulse
return value: 0
Sending the command READ
valore prima: 0

Open in new window


and this is the code:

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

double get_double(const char *str)
{
    /* First skip non-digit characters */
    /* Special case to handle negative numbers and the `+` sign */
    while (*str && !(isdigit(*str) || ((*str == '-' || *str == '+') && isdigit(*(str + 1)))))
        str++;

    /* The parse to a double */
    return strtod(str, NULL);
}

char buf[80] = {'\0' };  // buffer is zero'ed

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(BRKINT | PARMRK | ISTRIP | INLCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;
    tty.c_lflag |= ICANON;
    tty.c_iflag |= (IGNCR | IGNBRK);

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}


int main()
{

    const char *portname = "/dev/ttyS4";
    int fd;
    int wlen; 
    int ret=0;

    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
        printf("Error opening %s: %s\n", portname, strerror(errno));

    } else {
    printf("Bilancia presente\n");
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    //set_mincount(fd, 0);                /* set to pure timed read */

    ret = tcflush(fd, TCIOFLUSH);
    printf("return value1: %d\n", ret);			
    /* simple output */
    printf("Sending a pulse\n");
    wlen = write(fd, "\n", 1);
    if (wlen != 1) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }


    ret = tcflush(fd, TCIOFLUSH);
    printf("return value: %d\n", ret);			
    /* simple output */
    printf("Sending the command READ\n");
    wlen = write(fd, "READ\r\n", 6);
    if (wlen != 6) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }

    tcdrain(fd);    /* delay for output */


        int rdlen = 0, total = 0, timeoutcnt = 0;
        printf("valore prima: %d\n", rdlen);

        while ((rdlen = read(fd, &buf[total], sizeof(buf) - total - 1)) >= 0 && total + rdlen < (int)sizeof(buf))  {
            printf("valore %d\n", rdlen);
            if (rdlen == 0) {
                printf("Timeout from read\n");
                if (++timeoutcnt > 100)
                       break;
                continue;
            }
            printf("%s", buf);
            total += rdlen;
            if (total > 1 && buf[total-1] == '\n')
                  break;
        }
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (total + rdlen >= (int)sizeof(buf)) {
           printf("message too long for buffer: total:%d rdlen:%d: %s\n", total, rdlen, buf);
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
        }  else {
            printf("Success. total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }
    }


printf("Stringa: %s and number: %f\n", buf, get_double(buf));  

}

Open in new window


Since sometimes it works, I was thinking to repeat the writing and reading until I get a correct value from the device, but I do not know if it is possible to implement since at the moment it get stucks and there is not possibility to recovery the program.

I was thinking something like this:

1. Send READ
2. try to read the answer for the first time
3. answer is not good, send READ again
4. answer is correct, exit
nociSoftware Engineer
Distinguished Expert 2018

Commented:
Effectively you have no timeout... as the read will patiently wait a long time.

There are two solution ins Unix/Linux (c-runtime) style:
1) use a setitimer()/ sigaction() pair.  
    here you start a oneshot timer just before the read, when the read complete you cancel the time, if the time expires you need to backtrack some steps using setjmp/longjmp.....

2) use non-block  file descriptor.   the
open("/dev/ttySbla", O_READ|O_WRITE|O_NONBLOCK)...
Now read & write will return EWOULDBLOCK when no data is available in stead of waiting for data.
using select() you can "wait, with a specified timeout"  for IO to be received.. upon receipt you get have the opportunity to read the next few characters.
 
   timeval max_time;
    fd_set readfds;
    int ready;
    int current;
    ....
     fd=open("/dev/ttyS0",O_RDWR | O_NOCTTY |O_NONBLOCK )
     max_time.tv_sec = 10;   /* 10 seconds */
     max_time.tv_usec = 0;  /* 0 micro-seconds */
     ...

     where read is now:
    ...
/* loop from Here for Complete commands... */
     current = 0;
     max_time.tv_sec = 10;   /* 10 seconds */
     max_time.tv_usec = 0;  /* 0 micro-seconds */
/* loopfrom here during partial IO [ incomplete buffers ]*/
     FD_ZERO(&readfds);
     FD_SET(fd, &readfds);
     ready=select(fd+1, &readfds, NULL, NULL, &max_time);
     if (ready > 0) {
          /* a data block is ready, now read... */
          n = read(fd, &buffer[current], sizeof(buffer) - current);
          /* n<0 ==> error */
          current += n;
          .......
...          if io = complete (\n received) then data ready... if incomplete ensure partial loop.
...          parse data if intelligible...
      } else if (ready == 0) {
          /* no data received before timeout */
...          ignore received parts and start from top?

     } else /* ready < 0 */
...          inspect errno;
     }

Open in new window


checkout man 2 select, man 2 open.

Linux/unix etc. are basicly a synchronous OS. meaning there is no elegant way of doing asynch IO  and in the years about 8 different methods have been developped for doing it on program level.   Varaiant for all Unix OS's, several have been ported to others for compaibility. Linux probably can support most methods.

(in comparison OpenVMS is an almost complete Asynchronous OS, where for most functions an Asynchronous & synchronous variant exist. (The synchronous function is basicly an Async one with a wait tacked on).
Rocco GalatiR&D Engineer

Author

Commented:
Thank you for your advice, I'm trying to study your pseudo-code in order to integrate it with my code (even if it is not easy for me).

In the meanwhile, I was reading about VTIME and VMIN even if I cannot use them if I use non-blocking mode.

However, in my code, I have this configuration:

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(BRKINT | PARMRK | ISTRIP | INLCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;
    tty.c_lflag |= ICANON;
    tty.c_iflag |= (IGNCR | IGNBRK);

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}

Open in new window


does this affect your solution based on timeout?
nociSoftware Engineer
Distinguished Expert 2018

Commented:
But you setup ICANON:   tty.c_lflag |= ICANON;
then those (VMIN, VTIME) are not applicable...


If VMIN/VTIME are used then no need fr select etc.
Rocco GalatiR&D Engineer

Author

Commented:
What do you suggest?

1. use VMIN/VTIME by setting VIM=18, VTIME=10 without ICANON and leave the code as it is

2. use the select function as you proposed in the previous post

?

Moreover, I'm trying to figure out how your pseudo code works, because I can't understand how the loop should be implemented.
 struct timeval max_time;
    fd_set readfds;
    int ready;
    int current;

    fd = open(portname, O_RDWR | O_NOCTTY | O_NONBLOCK);
    max_time.tv_sec = 10;   /* 10 seconds */
    max_time.tv_usec = 0;  /* 0 micro-seconds */
    current = 0;
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    //set_mincount(fd, 0);                /* set to pure timed read */

   
    ret = tcflush(fd, TCIOFLUSH);
    printf("return value: %d\n", ret);			
    /* simple output */
    printf("Sending the command READ\n");
    wlen = write(fd, "READ\r\n", 6);
    if (wlen != 6) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }

    tcdrain(fd);    /* delay for output */


        int rdlen = 0, total = 0, timeoutcnt = 0;
        printf("valore prima: %d\n", rdlen);

        while ((rdlen = read(fd, &buf[total], sizeof(buf) - total - 1)) >= 0 && total + rdlen < (int)sizeof(buf))  {
            printf("valore %d\n", rdlen);
            if (rdlen == 0) {
                printf("Timeout from read\n");
                if (++timeoutcnt > 100)
                       break;
                continue;
            }
            printf("%s", buf);
            total += rdlen;
            if (total > 1 && buf[total-1] == '\n')
                  break;
        }
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (total + rdlen >= (int)sizeof(buf)) {
           printf("message too long for buffer: total:%d rdlen:%d: %s\n", total, rdlen, buf);
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
        }  else {
            printf("Success. total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }

Open in new window

nociSoftware Engineer
Distinguished Expert 2018

Commented:
Start with VMIN/VTIME as that requires a lot less code changes
Wrapping your head around select() etc. might need more time to get all things corrected.  (for me that is the preferred way, as i used that a lot ...)
Rocco GalatiR&D Engineer

Author

Commented:
I tried to set VMIN e VTIME, it seems to work a little bit better, but sometime it happens that it still get stuck like you can see in this sample output:

robodyne@robodyne-isola1:~$ ./test 
Bilancia presente
return value: 0
Sending the command READ
valore prima: 0

valore 8
ST,GS,  
valore 8
ST,GS,    0.00,k
valore 2
ST,GS,    0.00,kg
Success. total:18 rdlen:2  buf:ST,GS,    0.00,kg

Stringa: ST,GS,    0.00,kg
 and number: 0.000000
robodyne@robodyne-isola1:~$ ./test 
Bilancia presente
return value: 0
Sending the command READ
valore prima: 0

valore 8
ST,GS,  
valore 8
ST,GS,    0.00,k
valore 2
ST,GS,    0.00,kg
Success. total:18 rdlen:2  buf:ST,GS,    0.00,kg

Stringa: ST,GS,    0.00,kg
 and number: 0.000000
robodyne@robodyne-isola1:~$ ./test 
Bilancia presente
return value: 0
Sending the command READ
valore prima: 0
^C

Open in new window


This is the code:
#include <errno.h>
#include <fcntl.h> 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <ctype.h>

double get_double(const char *str)
{
    /* First skip non-digit characters */
    /* Special case to handle negative numbers and the `+` sign */
    while (*str && !(isdigit(*str) || ((*str == '-' || *str == '+') && isdigit(*(str + 1)))))
        str++;

    /* The parse to a double */
    return strtod(str, NULL);
}

char buf[80] = {'\0' };  // buffer is zero'ed

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(BRKINT | PARMRK | ISTRIP | INLCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ISIG | IEXTEN | ICANON);
    tty.c_oflag &= ~OPOST;
    //tty.c_lflag |= ICANON;
    tty.c_iflag |= (IGNCR | IGNBRK);

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = 19;
    tty.c_cc[VTIME] = 20;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}


int main()
{

    const char *portname = "/dev/ttyS4";
    int fd;
    int wlen; 
    int ret=0;

    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
        printf("Error opening %s: %s\n", portname, strerror(errno));

    } else {
    printf("Bilancia presente\n");
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    //set_mincount(fd, 0);                /* set to pure timed read */

    ret = tcflush(fd, TCIOFLUSH);
    printf("return value: %d\n", ret);			
    /* simple output */
    printf("Sending the command READ\n");
    wlen = write(fd, "READ\r\n", 6);
    if (wlen != 6) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }

    tcdrain(fd);    /* delay for output */


        int rdlen = 0, total = 0, timeoutcnt = 0;
        printf("valore prima: %d\n", rdlen);

        while ((rdlen = read(fd, &buf[total], sizeof(buf) - total - 1)) >= 0 && total + rdlen < (int)sizeof(buf))  {
            printf("\nvalore %d\n", rdlen);
            if (rdlen == 0) {
                printf("Timeout from read\n");
                if (++timeoutcnt > 100)
                       break;
                continue;
            }
            printf("%s", buf);
            total += rdlen;
            if (total > 1 && buf[total-1] == '\n')
                  break;
        }
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (total + rdlen >= (int)sizeof(buf)) {
           printf("message too long for buffer: total:%d rdlen:%d: %s\n", total, rdlen, buf);
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
        }  else {
            printf("Success. total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }
    }


printf("Stringa: %s and number: %f\n", buf, get_double(buf));  

}

Open in new window


This is the output from putty
ERR04
ST,GS,    0.00,kg
ERR04
ERR04
ERR04
ST,GS,    0.00,kg
ERR04
ST,GS,    0.00,kg
ERR04
ST,GS,    0.00,kg
ERR04
ST,GS,    0.00,kg
ST,GS,    0.00,kg
ST,GS,    0.00,kg
ERR04
ERR04
ERR04
ERR04
ERR04
ST,GS,    0.00,kg
ERR04
ST,GS,    0.00,kg
ST,GS,    0.00,kg
ST,GS,    0.00,kg

Open in new window


The digital scale works in this way, when it receives
READ<CR><LF>

Open in new window

then it answers
01ST,GS,  0.0,kg<CR><LF>

Open in new window

it seems like if the program does not receive the CR or the LF even if I realized that even from putty I reiceve ERR04 sometimes.

May be I should read until I read the correct string and send another READ command if not?
Or may be the scale does not always receive the READ command in the correct format..
nociSoftware Engineer
Distinguished Expert 2018

Commented:
If it take too much time to read a character you indeed need to re-issue the READ command.

Note that a tty  only produces data once, so if you are snooping on the interface WHILE running your program, your program may see nothing as "someone"-else already read it. (And the time may very well get reset as well).
There should only be allowed ONE program to control / work on the serial port at a time.
Rocco GalatiR&D Engineer

Author

Commented:
>>If it take too much time to read a character you indeed need to re-issue the READ command.

yes it's what I was thinking but I do not know how to check when a specific amount of time is elapsed.
I tried to look at your code, but it's too hard for me to figure out how to use it since I'm a beginner, unfortunately!
I haven't read this whole thread so this may already have been addressed. If you enter the commands slowly does the device respond correctly? If so try putting a delay between your program outputs. This is something I had to do when hardware could not respond quickly enough to input data .
Rocco GalatiR&D Engineer

Author

Commented:
I just send the whole string READ\r\n by code so I think I cannot slow down the process unfortunately
nociSoftware Engineer
Distinguished Expert 2018

Commented:
If you forego on the VMIN/VTIME style:

you open the socket in non-block more (open ..... O_NONBLOCK).....

Any read that has would start to wait will return an error (errno = E Would Block )
you can look on that will only turn your CPU quite hot.... or you can Wait until something gets delivered.....
thats where select comes in:   select will wait a certain time (the timeout value, or indefinite if NULL is given).
and select can get 3 bit arrays: 1 for all reading sockets, one for all writing sockets on for all other IO types (ioctl f.e.) sockets.
And you need to give it the highest bitnumber to look for.
You only have one reading socket.

using the FD_ZERO, FD_SET you setup the right fd into the read bit set...
and then start select.

Select returns the number of bits that got changed.... the readmask/writemask/othermask have been reset by select, and it will set bits for the IO that was taken care of.

IF 0 is return NO IO was ready, timeout occurred...
if <0 some error occured.
if >0 you know how many channels are ready so you can walk the bitmaps to find out which one....

The timevalue is updated.... if time was 10 seconds before and after 4 seconds the IO arrived then there is still 6 seconds left....
so you can restart select after setting up the right bits again with time remaining or you can also reset the timer on select. to wait the full time again.   (a per character timeout vs. per line timeout).
nociSoftware Engineer
Distinguished Expert 2018

Commented:
you can slow down by: write( fd, "r",1) ; usleep(5000); write(fd,"e",1); ....usleep(5000); .... etc.

(or in a loop).
Rocco GalatiR&D Engineer

Author

Commented:
@noci: I tried to open putty and to write a character per time slowly, but it returns ERR04, so I think it's not related to the velocity :(

Your solution based on select() seems to be the best one since it can handle also the case when the device returns ERR04.
The only problem is that it is too hard to implement for me, I'll try later this evening, but I alreayd know i'll fail.
Rocco GalatiR&D Engineer

Author

Commented:
If I try to use non-blocking solution:

  fd = open(portname, O_RDWR | O_NOCTTY | O_NONBLOCK);

Open in new window



it gives me this error all the time:

Error from read: total:0 rdlen:-1: Resource temporarily unavailable

Open in new window


and the errno always return SUCCESS even when it gets stuck.
nociSoftware Engineer
Distinguished Expert 2018

Commented:
make a function read_timout and use that.
It behaves like read, with a timeout (in seconds added);

/*
** read_timeout, using non blocking IO to read from fd opened as non-blocking..
** (c) GPL-3 
*/
int read_timeout(int fd; char *buffer; int size; int maxtime) {
     fd_set read_set;
     int status;
     int length, n;
     struct timeval timeout = { maxtime, 0 };

     while (timeout.tv_sec > 0 && timeout_tv_usec > 0 && length <size) {
         FD_ZERO(read_set);
         FD_SET(fd, read_set)
         status = select(fd+1, read_set, NULL, NULL, &timeout);
         if (status <0)
             return -1;
         n = read(fd, &buffer[length], size-length);
         if (n < 0)
             return -1;
         length += n;
    }
    return length;
}

Open in new window

Rocco GalatiR&D Engineer

Author

Commented:
Thank you for your help and your support!

I tried to use your suggested code and to integrate it!
I had to change it a little bit to make it compile correctly and this is the result:

#include <errno.h>
#include <fcntl.h> 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/types.h>

int read_timeout(int fd, char *buffer, int size, int maxtime) {
     fd_set read_set;
     int status;
     int length, n;
     struct timeval timeout = { maxtime, 0 };

     while (timeout.tv_sec > 0 && timeout.tv_usec > 0 && length < size) {
         FD_ZERO(&read_set);
         FD_SET(fd, &read_set);
         status = select(fd+1, &read_set, NULL, NULL, &timeout);
         if (status <0)
             return -1;
         n = read(fd, &buffer[length], size-length);
         if (n < 0)
             return -1;
         length += n;
    }
    return length;
}


double get_double(const char *str)
{
    /* First skip non-digit characters */
    /* Special case to handle negative numbers and the `+` sign */
    while (*str && !(isdigit(*str) || ((*str == '-' || *str == '+') && isdigit(*(str + 1)))))
        str++;

    /* The parse to a double */
    return strtod(str, NULL);
}

char buf[80] = {'\0' };  // buffer is zero'ed

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(BRKINT | PARMRK | ISTRIP | INLCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;
    tty.c_lflag |= ICANON;
    tty.c_iflag |= (IGNCR | IGNBRK);

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}


int main()
{

    const char *portname = "/dev/ttyS4";
    int fd;
    int wlen; 
    int ret=0;

    struct timeval max_time;
    fd_set readfds;
    int ready;
    int current;

    fd = open(portname, O_RDWR | O_NOCTTY | O_NONBLOCK);
    max_time.tv_sec = 10;   /* 10 seconds */
    max_time.tv_usec = 0;  /* 0 micro-seconds */
    current = 0;
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    //set_mincount(fd, 0);                /* set to pure timed read */

   
    ret = tcflush(fd, TCIOFLUSH);
    printf("return value: %d\n", ret);			
    /* simple output */
    printf("Sending the command READ\n");
    wlen = write(fd, "READ\r\n", 6);
    if (wlen != 6) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }

    tcdrain(fd);    /* delay for output */


        int rdlen = 0, total = 0, timeoutcnt = 0;
        printf("valore prima: %d\n", rdlen);

        while ((rdlen = read_timeout(fd, &buf[total], sizeof(buf) - total - 1, 10)) >= 0 && total + rdlen < (int)sizeof(buf))  {
            printf("valore %d\n", rdlen);
            if (rdlen < 0) {
                printf("Timeout from read\n");
                if (++timeoutcnt > 100)
                       break;
                continue;
            }
            printf("%s", buf);
            total += rdlen;
            if (total > 1 && buf[total-1] == '\n')
                  break;
        }
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (total + rdlen >= (int)sizeof(buf)) {
           printf("message too long for buffer: total:%d rdlen:%d: %s\n", total, rdlen, buf);
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
        }  else {
            printf("Success. total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }
 
printf("Stringa: %s and number: %f\n", buf, get_double(buf));  

}

Open in new window


I'm not sure if
  fd = open(portname, O_RDWR | O_NOCTTY | O_NONBLOCK);

Open in new window

is correct and if I'm using correctly the while() loop:
 while ((rdlen = read_timeout(fd, &buf[total], sizeof(buf) - total - 1, 10)) >= 0 && total + rdlen < (int)sizeof(buf))  {
            printf("valore %d\n", rdlen);
            if (rdlen < 0) {
                printf("Timeout from read\n");
                if (++timeoutcnt > 100)
                       break;
                continue;
            }
            printf("%s", buf);
            total += rdlen;
            if (total > 1 && buf[total-1] == '\n')
                  break;
        }
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (total + rdlen >= (int)sizeof(buf)) {
           printf("message too long for buffer: total:%d rdlen:%d: %s\n", total, rdlen, buf);
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
        }  else {
            printf("Success. total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }

Open in new window

nociSoftware Engineer
Distinguished Expert 2018

Commented:
read_timeout does take care of a LOT ofyour while loop....

1) if no input has been passed and timeout happens 0 will be returned.
2) <0 is error return  (check errno)
3) if timeout happens (as given) then everything that is read until timeout will be returned.

rdlen = read_timeoud( fd, buf, total, 10  );
if (rdlen < 0) {
     check errno
} else if (rdlen == 0) {
    Timeout...
} else {
   check if there is sufficient data...
}

Open in new window


An enhancment in read_timout can be to stop reading when a linefeed is seen before timeout....

ALSO be prepared for an error "Would block" from the write...  or writes done in parts.  (i guess your code is simple enough, more complex IO protocol might need it).
Rocco GalatiR&D Engineer

Author

Commented:
So shouldn't I consider anymore the condition when the code reaches the '\n' from the device?

Because at the moment, I read the answer until I find the termination character:

 while ((rdlen = read_timeout(fd, &buf[total], sizeof(buf) - total - 1, 10)) >= 0 && total + rdlen < (int)sizeof(buf))  {
            printf("valore %d\n", rdlen);
            if (rdlen < 0) {
                printf("Timeout from read: %s\n", strerror(errno));
                if (++timeoutcnt > 100)
                       break;
                continue;
            }
            printf("%s", buf);
            total += rdlen;
            if (total > 1 && buf[total-1] == '\n')
                  break;
        }
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (total + rdlen >= (int)sizeof(buf)) {
           printf("message too long for buffer: total:%d rdlen:%d: %s\n", total, rdlen, buf);
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
        }  else {
            printf("Success. total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }

Open in new window


Should I only consider what I read after the timeout?
nociSoftware Engineer
Distinguished Expert 2018

Commented:
Slightly modified code: this stops reading with \n as well. (\n is included in the string read).
It has a slightly bigger overhead due to per character reading, should not be problem in your case.
/*
** readln_timeout, using non blocking IO to read from fd opened as non-blocking..
** stop reading when a linefeed is received.
** (c) GPL-3 
*/
int read_timeout(int fd; char *buffer; int size; int maxtime) {
     fd_set read_set;
     int status;
     int length, n;
     struct timeval timeout = { maxtime, 0 };

     while (timeout.tv_sec > 0 && timeout_tv_usec > 0 && length <size) {
         FD_ZERO(read_set);
         FD_SET(fd, read_set)
         status = select(fd+1, read_set, NULL, NULL, &timeout);
         if (status <0)
             return -1;
         n = read(fd, &buffer[length], 1);   /* read 1 character at a time, to prevent reading past \n */
         if (n < 0)
             return -1;
         if( buf[length] == '\n' )
           return length+1;
         length++;
    }
    return length;
}

Open in new window

Rocco GalatiR&D Engineer

Author

Commented:
I tried to follow your suggestion and I modified the operations after the while() in order to reflect your advices.

I think it is not working correctly because I always get an empty buffer:

robodyne@robodyne-isola1:~$ ./test 
return value: 0
Sending the command READ
valore prima: 0
Error from read: total:0 rdlen:-457204139: Success
Stringa:  and number: 0.000000
robodyne@robodyne-isola1:~$ ./test 
return value: 0
Sending the command READ
valore prima: 0
message too long for buffer: total:0 rdlen:566157625: 
Stringa:  and number: 0.000000
robodyne@robodyne-isola1:~$ ./test 
return value: 0
Sending the command READ
valore prima: 0
message too long for buffer: total:0 rdlen:1853438217: 
Stringa:  and number: 0.000000
robodyne@robodyne-isola1:~$ ./test 
return value: 0
Sending the command READ
valore prima: 0
message too long for buffer: total:0 rdlen:1448737115: 
Stringa:  and number: 0.000000

Open in new window


this is the current code:

#include <errno.h>
#include <fcntl.h> 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/types.h>

int read_timeout(int fd, char *buffer, int size, int maxtime) {
     fd_set read_set;
     int status;
     int length, n;
     struct timeval timeout = { maxtime, 0 };

     while (timeout.tv_sec > 0 && timeout.tv_usec > 0 && length < size) {
         FD_ZERO(&read_set);
         FD_SET(fd, &read_set);
         status = select(fd+1, &read_set, NULL, NULL, &timeout);
     if (status <0)
             return -1;
         n = read(fd, &buffer[length], 1);   /* read 1 character at a time, to prevent reading past \n */
         if (n < 0)
             return -1;
         if( buffer[length] == '\n' )
           return length+1;
         length++;
    }
    return length;
}



double get_double(const char *str)
{
    /* First skip non-digit characters */
    /* Special case to handle negative numbers and the `+` sign */
    while (*str && !(isdigit(*str) || ((*str == '-' || *str == '+') && isdigit(*(str + 1)))))
        str++;

    /* The parse to a double */
    return strtod(str, NULL);
}

char buf[80] = {'\0' };  // buffer is zero'ed

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(BRKINT | PARMRK | ISTRIP | INLCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;
    tty.c_lflag |= ICANON;
    tty.c_iflag |= (IGNCR | IGNBRK);

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}


int main()
{

    const char *portname = "/dev/ttyS4";
    int fd;
    int wlen; 
    int ret=0;

    struct timeval max_time;
    fd_set readfds;
    int ready;
    int current;

    fd = open(portname, O_RDWR | O_NOCTTY | O_NONBLOCK);
    max_time.tv_sec = 10;   /* 10 seconds */
    max_time.tv_usec = 0;  /* 0 micro-seconds */
    current = 0;
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    //set_mincount(fd, 0);                /* set to pure timed read */

   
    ret = tcflush(fd, TCIOFLUSH);
    printf("return value: %d\n", ret);			
    /* simple output */
    printf("Sending the command READ\n");
    wlen = write(fd, "READ\r\n", 6);
    if (wlen != 6) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }

    tcdrain(fd);    /* delay for output */


        int rdlen = 0, total = 0, timeoutcnt = 0;
        printf("valore prima: %d\n", rdlen);

        while ((rdlen = read_timeout(fd, &buf[total], sizeof(buf) - total - 1, 10)) >= 0 && total + rdlen < (int)sizeof(buf))  {
            printf("valore %d\n", rdlen);
            total += rdlen;
            if (total > 1 && buf[total-1] == '\n')
                  break;
           }
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (total + rdlen >= (int)sizeof(buf)) {
           printf("message too long for buffer: total:%d rdlen:%d: %s\n", total, rdlen, buf);
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
        }  else {
            printf("Success. total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }
    
printf("Stringa: %s and number: %f\n", buf, get_double(buf));  

}

Open in new window

nociSoftware Engineer
Distinguished Expert 2018

Commented:
read_timout does the timeout read ...  & loop for all characters.

No need to run this again in a loop unless your loop also does the prompting... (sending READ\n)

      int rdlen = 0, total = 0, timeoutcnt = 0;
        rdlen = read_timeout(fd, buf, sizeof(buf) -1, 10)
        printf("valore %d\n", rdlen);
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
        }  else {
           buf[rdlen]='\0';
           if (buf[rdlen-1] == '\n') 
               printf("Success. total:%d rdlen:%d  buf:%s", total, rdlen, buf);
           else 
               printf("Partial read: total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }

Open in new window


The read_timeout still needs an extra check: if select returns 0 you also need to terminate the IO. as there is no more data to be read...
line 25  then becomes:

 
              if (status == 0)
                     return length;
              n = read(fd, ....)

Open in new window

Rocco GalatiR&D Engineer

Author

Commented:
Really thank you for all your help and patience!

I modified the code as you suggested, but I get a segmentation fault when I call the read_timeout() function, I put some print statements to check where the error occurs and it seems to be located soon after the read_timeout function.

./test
return value: 0
Sending the command READ
test before the read_timeout
valore 32546
Segmentation fault

Open in new window


I checked with gdb but when I run it with the debugger, it doesnt generate the fault.

#include <errno.h>
#include <fcntl.h> 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/types.h>

int read_timeout(int fd, char *buffer, int size, int maxtime) {
     fd_set read_set;
     int status;
     int length, n;
     struct timeval timeout = { maxtime, 0 };

     while (timeout.tv_sec > 0 && timeout.tv_usec > 0 && length < size) {
         FD_ZERO(&read_set);
         FD_SET(fd, &read_set);
         status = select(fd+1, &read_set, NULL, NULL, &timeout);
     if (status <0)
             return -1;
     if (status == 0)
             return length;
         n = read(fd, &buffer[length], 1);   /* read 1 character at a time, to prevent reading past \n */
         if (n < 0)
             return -1;
         if( buffer[length] == '\n' )
           return length+1;
         length++;
    }
    return length;
}



double get_double(const char *str)
{
    /* First skip non-digit characters */
    /* Special case to handle negative numbers and the `+` sign */
    while (*str && !(isdigit(*str) || ((*str == '-' || *str == '+') && isdigit(*(str + 1)))))
        str++;

    /* The parse to a double */
    return strtod(str, NULL);
}

char buf[80] = {'\0' };  // buffer is zero'ed

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(BRKINT | PARMRK | ISTRIP | INLCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;
    tty.c_lflag |= ICANON;
    tty.c_iflag |= (IGNCR | IGNBRK);

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}


int main()
{

    const char *portname = "/dev/ttyS4";
    int fd;
    int wlen; 
    int ret=0;

    struct timeval max_time;
    fd_set readfds;
    int ready;
    int current;

    fd = open(portname, O_RDWR | O_NOCTTY | O_NONBLOCK);
    max_time.tv_sec = 10;   /* 10 seconds */
    max_time.tv_usec = 0;  /* 0 micro-seconds */
    current = 0;
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    //set_mincount(fd, 0);                /* set to pure timed read */

   
    ret = tcflush(fd, TCIOFLUSH);
    printf("return value: %d\n", ret);			
    /* simple output */
    printf("Sending the command READ\n");
    wlen = write(fd, "READ\r\n", 6);
    if (wlen != 6) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }

    tcdrain(fd);    /* delay for output */


        int rdlen = 0, total = 0, timeoutcnt = 0;
       printf("test before the read_timeout\n");
         rdlen = read_timeout(fd, buf, sizeof(buf) -1, 10);
        printf("valore %d\n", rdlen);
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
        }  else {
           buf[rdlen]='\0';
           if (buf[rdlen-1] == '\n') 
               printf("Success. total:%d rdlen:%d  buf:%s", total, rdlen, buf);
           else 
               printf("Partial read: total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }

printf("Stringa: %s and number: %f\n", buf, get_double(buf));  

}

Open in new window

nociSoftware Engineer
Distinguished Expert 2018

Commented:
length was uninitialised... ( sorry i have no serial ports here to test with, those seem to be removed from a lot of hardware. )
So you receive untested code..

int read_timeout(int fd, char *buffer, int size, int maxtime) {
     fd_set read_set;
     int status;
     int length = 0, n;
     struct timeval timeout = { maxtime, 0 };

     while (length < size && (timeout.tv_sec > 0 || timeout.tv_sec == 0 &&  timeout.tv_usec > 0) ) {
          FD_ZERO(&read_set);
          FD_SET(fd, &read_set);
          status = select(fd+1, &read_set, NULL, NULL, &timeout);
          if (status <0)
               return -1;
          if (status == 0)
               return length;
          n = read(fd, &buffer[length], 1);   /* read 1 character at a time, to prevent reading past \n */
          if (n < 0)
               return -1;
          if (n == 1 &&  buffer[length] == '\n' )
               return length+1;
          length += n;
     }
     return length;
}

Open in new window


Also corrected the timeout value test.
There is  a shorter way to test by adding  the two fields and test for non zero.
 (timeout.tv_sec + timeout.tv_usec) != 0    (timeout is if both are zero.)
Rocco GalatiR&D Engineer

Author

Commented:
Thank you again for all your support.

Now, it seems to work better, I even lowered the timetout (from 10s to 3s) and now it works almost all the times even if it return ZERO values sometimes as it is possible to see:

robodyne@robodyne-isola1:~$ ./test
return value: 0
Sending the command READ
test before the read_timeout
valore 0
Too many timeouts from read. total:0 buf:
Stringa:  and number: 0.000000

robodyne@robodyne-isola1:~$ ./test
return value: 0
Sending the command READ
test before the read_timeout
valore 18
Success. total:0 rdlen:18  buf:ST,GS,    0.00,kg
Stringa: ST,GS,    0.00,kg
 and number: 0.000000

robodyne@robodyne-isola1:~$ ./test
return value: 0
Sending the command READ
test before the read_timeout
valore 18
Success. total:0 rdlen:18  buf:ST,GS,    0.00,kg
Stringa: ST,GS,    0.00,kg
 and number: 0.000000

robodyne@robodyne-isola1:~$ ./test
return value: 0
Sending the command READ
test before the read_timeout
valore 0
Too many timeouts from read. total:0 buf:
Stringa:  and number: 0.000000

Open in new window


I tried 4 times and it worked fine only for two times (which is OK, but I cannot understand why it gives error in the other cases)

This is the full code:

#include <errno.h>
#include <fcntl.h> 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/types.h>

int read_timeout(int fd, char *buffer, int size, int maxtime) {
     fd_set read_set;
     int status;
     int length = 0, n;
     struct timeval timeout = { maxtime, 0 };

     while (length < size && (timeout.tv_sec > 0 || timeout.tv_sec == 0 &&  timeout.tv_usec > 0) ) {
          FD_ZERO(&read_set);
          FD_SET(fd, &read_set);
          status = select(fd+1, &read_set, NULL, NULL, &timeout);
          if (status <0)
               return -1;
          if (status == 0)
               return length;
          n = read(fd, &buffer[length], 1);   /* read 1 character at a time, to prevent reading past \n */
          if (n < 0)
               return -1;
          if (n == 1 &&  buffer[length] == '\n' )
               return length+1;
          length += n;
     }
     return length;
}



double get_double(const char *str)
{
    /* First skip non-digit characters */
    /* Special case to handle negative numbers and the `+` sign */
    while (*str && !(isdigit(*str) || ((*str == '-' || *str == '+') && isdigit(*(str + 1)))))
        str++;

    /* The parse to a double */
    return strtod(str, NULL);
}

char buf[80] = {'\0' };  // buffer is zero'ed

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(BRKINT | PARMRK | ISTRIP | INLCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;
    tty.c_lflag |= ICANON;
    tty.c_iflag |= (IGNCR | IGNBRK);

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}


int main()
{

    const char *portname = "/dev/ttyS4";
    int fd;
    int wlen; 
    int ret=0;

    struct timeval max_time;
    fd_set readfds;
    int ready;
    int current;

    fd = open(portname, O_RDWR | O_NOCTTY | O_NONBLOCK);
    max_time.tv_sec = 10;   /* 10 seconds */
    max_time.tv_usec = 0;  /* 0 micro-seconds */
    current = 0;
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    //set_mincount(fd, 0);                /* set to pure timed read */

   
    ret = tcflush(fd, TCIOFLUSH);
    printf("return value: %d\n", ret);			
    /* simple output */
    printf("Sending the command READ\n");
    wlen = write(fd, "READ\r\n", 6);
    if (wlen != 6) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }

    tcdrain(fd);    /* delay for output */


        int rdlen = 0, total = 0, timeoutcnt = 0;
       printf("test before the read_timeout\n");
         rdlen = read_timeout(fd, buf, sizeof(buf) -1, 3);
        printf("valore %d\n", rdlen);
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
        }  else {
           buf[rdlen]='\0';
           if (buf[rdlen-1] == '\n') 
               printf("Success. total:%d rdlen:%d  buf:%s", total, rdlen, buf);
           else 
               printf("Partial read: total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }


printf("Stringa: %s and number: %f\n", buf, get_double(buf));  

}

Open in new window


I noticed that when the device answers correctly, it replies immediately while when there is a problem it always needs to finish the timeout to print the result (which is ZERO)
nociSoftware Engineer
Distinguished Expert 2018

Commented:
Sending the string READ\n   might need to be send with slight delays..., you can try the following:

#include <unistd.h>

/*
**  write characters with a slight delay ... the delay is the 4th paramter and in mesured in ms (milliseconds).
*/
int write_delayed(fd, char *buffer, int size; int delay) {
   int n, count;

   for (count=0; size--; count++, buffer++) {
      n = write(fd,*buffer,1);
      if (n < 0) {
         return -1;
      }
      usleep(1000*delay);
   }
   return count;
}

Open in new window

Rocco GalatiR&D Engineer

Author

Commented:
I tried to give different timeout values and to check the results, it seems to work a little bit better even if I still get errors sometimes.

I was thinking to put the read and the write functions in a global while loop that terminates only when a good reading is obtained in order to repeat the operation if I get errors.

What do you think about that? Do you think it could be a good solution?

#include <errno.h>
#include <fcntl.h> 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/types.h>

int read_timeout(int fd, char *buffer, int size, int maxtime) {
     fd_set read_set;
     int status;
     int length = 0, n;
     struct timeval timeout = { maxtime, 0 };

     while (length < size && (timeout.tv_sec > 0 || timeout.tv_sec == 0 &&  timeout.tv_usec > 0) ) {
          FD_ZERO(&read_set);
          FD_SET(fd, &read_set);
          status = select(fd+1, &read_set, NULL, NULL, &timeout);
          if (status <0)
               return -1;
          if (status == 0)
               return length;
          n = read(fd, &buffer[length], 1);   /* read 1 character at a time, to prevent reading past \n */
          if (n < 0)
               return -1;
          if (n == 1 &&  buffer[length] == '\n' )
               return length+1;
          length += n;
     }
     return length;
}

int write_delayed(int fd, char *buffer, int size, int delay) {
   int n, count;

   for (count=0; size--; count++, buffer++) {
      n = write(fd,buffer,1);
      if (n < 0) {
         return -1;
      }
      usleep(1000*delay);
   }
   return count;
}

double get_double(const char *str)
{
    /* First skip non-digit characters */
    /* Special case to handle negative numbers and the `+` sign */
    while (*str && !(isdigit(*str) || ((*str == '-' || *str == '+') && isdigit(*(str + 1)))))
        str++;

    /* The parse to a double */
    return strtod(str, NULL);
}

char buf[80] = {'\0' };  // buffer is zero'ed

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(BRKINT | PARMRK | ISTRIP | INLCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;
    tty.c_lflag |= ICANON;
    tty.c_iflag |= (IGNCR | IGNBRK);

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}


int main()
{

    const char *portname = "/dev/ttyS4";
    int fd;
    int wlen; 
    int ret=0;

    struct timeval max_time;
    fd_set readfds;
    int ready;
    int current;

    fd = open(portname, O_RDWR | O_NOCTTY | O_NONBLOCK);
    max_time.tv_sec = 10;   /* 10 seconds */
    max_time.tv_usec = 0;  /* 0 micro-seconds */
    current = 0;
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    //set_mincount(fd, 0);                /* set to pure timed read */

   
    ret = tcflush(fd, TCIOFLUSH);
    printf("return value: %d\n", ret);			
    /* simple output */
    printf("Sending the command READ\n");
    wlen = write_delayed(fd, "READ\r\n", 6, 2);
    if (wlen != 6) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }

    tcdrain(fd);    /* delay for output */


        int rdlen = 0, total = 0, timeoutcnt = 0;
       printf("test before the read_timeout\n");
         rdlen = read_timeout(fd, buf, sizeof(buf) -1, 1);
        printf("valore %d\n", rdlen);
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
        }  else {
           buf[rdlen]='\0';
           if (buf[rdlen-1] == '\n') 
               printf("Success. total:%d rdlen:%d  buf:%s", total, rdlen, buf);
           else 
               printf("Partial read: total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }


printf("Stringa: %s and number: %f\n", buf, get_double(buf));  

}

Open in new window

nociSoftware Engineer
Distinguished Expert 2018

Commented:
Yes that would be a good solution. When wheiging you don't known if the scales are still moving due to momentum.
So getting several similar result should indicate that.
(You can make it a continuous process that only produced a stable result when the wheight changes and is stable again for a short while..).

btw. 2ms between characters is on the short side. If it needs to be typing speed you need to think    3-5 strokes a second ==> 300 - 200 ms.  fast typeing 100 ms (10 CPS. = typing speed of 600 / minute).
Rocco GalatiR&D Engineer

Author

Commented:
I modified again the code by adding the while loop until I receive a good result in this way:

#include <errno.h>
#include <fcntl.h> 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/types.h>

int read_timeout(int fd, char *buffer, int size, int maxtime) {
     fd_set read_set;
     int status;
     int length = 0, n;
     struct timeval timeout = { maxtime, 0 };

     while (length < size && (timeout.tv_sec > 0 || timeout.tv_sec == 0 &&  timeout.tv_usec > 0) ) {
          FD_ZERO(&read_set);
          FD_SET(fd, &read_set);
          status = select(fd+1, &read_set, NULL, NULL, &timeout);
          if (status <0)
               return -1;
          if (status == 0)
               return length;
          n = read(fd, &buffer[length], 1);   /* read 1 character at a time, to prevent reading past \n */
          if (n < 0)
               return -1;
          if (n == 1 &&  buffer[length] == '\n' )
               return length+1;
          length += n;
     }
     return length;
}

int write_delayed(int fd, char *buffer, int size, int delay) {
   int n, count;

   for (count=0; size--; count++, buffer++) {
      n = write(fd,buffer,1);
      if (n < 0) {
         return -1;
      }
      usleep(1000*delay);
   }
   return count;
}

double get_double(const char *str)
{
    /* First skip non-digit characters */
    /* Special case to handle negative numbers and the `+` sign */
    while (*str && !(isdigit(*str) || ((*str == '-' || *str == '+') && isdigit(*(str + 1)))))
        str++;

    /* The parse to a double */
    return strtod(str, NULL);
}

char buf[80] = {'\0' };  // buffer is zero'ed

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(BRKINT | PARMRK | ISTRIP | INLCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;
    tty.c_lflag |= ICANON;
    tty.c_iflag |= (IGNCR | IGNBRK);

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}


int main()
{

    const char *portname = "/dev/ttyS4";
    int fd;
    int wlen; 
    int ret=0;

    struct timeval max_time;
    fd_set readfds;
    int ready;
    int current;
    int try_again=0;

    fd = open(portname, O_RDWR | O_NOCTTY | O_NONBLOCK);
    max_time.tv_sec = 10;   /* 10 seconds */
    max_time.tv_usec = 0;  /* 0 micro-seconds */
    current = 0;
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    //set_mincount(fd, 0);                /* set to pure timed read */

   
    while(try_again == 0){

    ret = tcflush(fd, TCIOFLUSH);
    printf("return value: %d\n", ret);			
    /* simple output */
    printf("Sending the command READ\n");
    wlen = write_delayed(fd, "READ\r\n", 6, 4);
    if (wlen != 6) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }

    tcdrain(fd);    /* delay for output */


        int rdlen = 0, total = 0, timeoutcnt = 0;
       printf("test before the read_timeout\n");
         rdlen = read_timeout(fd, buf, sizeof(buf) -1, 1);
        printf("valore %d\n", rdlen);
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        }  else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
	    try_again=0;
        }  else {
           buf[rdlen]='\0';
           if (buf[rdlen-1] == '\n' && rdlen == 18){ 
               printf("Success. total:%d rdlen:%d  buf:%s", total, rdlen, buf);
	      try_again=1; 
              } 
           else 
               printf("Partial read: total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }

}
try_again=0;
printf("Stringa: %s and number: %f\n", buf, get_double(buf));  
printf("Peso: %f\n", get_double(buf));

}

Open in new window


Now, I should always get a good result and, if not, the while loop send another READ command and wait for the answer.
It seems to work:

./test
return value: 0
Sending the command READ
test before the read_timeout
valore 18
Success. total:0 rdlen:18  buf:ST,GS,    0.00,kg
Stringa: ST,GS,    0.00,kg
 and number: 0.000000
Peso: 0.000000
robodyne@robodyne-isola1:~$ ./test
return value: 0
Sending the command READ
test before the read_timeout
valore 18
Success. total:0 rdlen:18  buf:ST,GS,    0.00,kg
Stringa: ST,GS,    0.00,kg
 and number: 0.000000
Peso: 0.000000
robodyne@robodyne-isola1:~$ ./test
return value: 0
Sending the command READ
test before the read_timeout
valore 18
Success. total:0 rdlen:18  buf:ST,GS,    0.00,kg
Stringa: ST,GS,    0.00,kg
 and number: 0.000000
Peso: 0.000000
robodyne@robodyne-isola1:~$ ./test
return value: 0
Sending the command READ
test before the read_timeout
valore 18
Success. total:0 rdlen:18  buf:ST,GS,    0.00,kg
Stringa: ST,GS,    0.00,kg
 and number: 0.000000
Peso: 0.000000
robodyne@robodyne-isola1:~$ ./test
return value: 0
Sending the command READ
test before the read_timeout
valore 0
Too many timeouts from read. total:0 buf:
return value: 0
Sending the command READ
test before the read_timeout
valore 18
Success. total:0 rdlen:18  buf:ST,GS,    0.00,kg
Stringa: ST,GS,    0.00,kg
 and number: 0.000000
Peso: 0.000000
robodyne@robodyne-isola1:~$ ./test
return value: 0
Sending the command READ
test before the read_timeout
valore 18
Success. total:0 rdlen:18  buf:ST,GS,    0.00,kg
Stringa: ST,GS,    0.00,kg
 and number: 0.000000
Peso: 0.000000
robodyne@robodyne-isola1:~$ ./test
return value: 0
Sending the command READ
test before the read_timeout
valore 18
Success. total:0 rdlen:18  buf:ST,GS,    0.00,kg
Stringa: ST,GS,    0.00,kg
 and number: 0.000000
Peso: 0.000000
robodyne@robodyne-isola1:~$ ./test
return value: 0
Sending the command READ
test before the read_timeout
valore 0
Too many timeouts from read. total:0 buf:
return value: 0
Sending the command READ
test before the read_timeout
valore 0
Too many timeouts from read. total:0 buf:
return value: 0
Sending the command READ
test before the read_timeout
valore 18
Success. total:0 rdlen:18  buf:ST,GS,    0.00,kg
Stringa: ST,GS,    0.00,kg
 and number: 0.000000
Peso: 0.000000

Open in new window


I tried to increase the time between characters but it gives not better results. it seems even worst and I do not know the reason!
Software Engineer
Distinguished Expert 2018
Commented:
Your main program slightly tidied up...
int main()
{
    const char *portname = "/dev/ttyS4";
    int fd;
    int wlen; 
    int ret=0;

    struct timeval max_time;
    fd_set readfds;
    int ready;
    int current;
    int try_again=0;

    fd = open(portname, O_RDWR | O_NOCTTY | O_NONBLOCK);
    max_time.tv_sec = 10;   /* 10 seconds */
    max_time.tv_usec = 0;  /* 0 micro-seconds */
    current = 0;
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    //set_mincount(fd, 0);                /* set to pure timed read */

   
    while(try_again == 0){

        ret = tcflush(fd, TCIOFLUSH);
        printf("return value: %d\n", ret);			
        /* simple output */
        printf("Sending the command READ\n");
        wlen = write_delayed(fd, "READ\r\n", 6, 4);
        if (wlen != 6) {
            printf("Error from write: %d, %d\n", wlen, errno);
        }
 
        tcdrain(fd);    /* delay for output */


        int rdlen = 0, total = 0, timeoutcnt = 0;
        printf("test before the read_timeout\n");
        rdlen = read_timeout(fd, buf, sizeof(buf) -1, 1);
        printf("valore %d\n", rdlen);
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
        } else if (rdlen == 0) {
            printf("Too many timeouts from read. total:%d buf:%s\n", total, buf);
	          try_again=0;
        } else {
            buf[rdlen]='\0';
            if (buf[rdlen-1] == '\n' && rdlen == 18){ 
                printf("Success. total:%d rdlen:%d  buf:%s", total, rdlen, buf);
	              try_again=1; 
            } else 
                printf("Partial read: total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }
    }
    try_again=0;
    printf("Stringa: %s and number: %f\n", buf, get_double(buf));  
    printf("Peso: %f\n", get_double(buf));
}

Open in new window


And nonfunctional items removed:
int main() {
    const char *portname = "/dev/ttyS4";
    int fd;
    int wlen; 
    int ret=0;
    int try_again=1;

    fd = open(portname, O_RDWR | O_NOCTTY | O_NONBLOCK);
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);

    ret = tcflush(fd, TCIOFLUSH);
    printf("return value: %d\n", ret);			

    while (try_again){
        /* simple output */
        printf("Sending the command READ\n");
        wlen = write_delayed(fd, "READ\r\n", 6, 4);
        if (wlen != 6) {
            printf("Error from write: %d, %d\n", wlen, errno);
        }
        tcdrain(fd);    /* delay for output */

        int rdlen = 0;
        printf("test before the read_timeout\n");
        rdlen = read_timeout(fd, buf, sizeof(buf) -1, 1);
        printf("valore %d\n", rdlen);
        if (rdlen < 0) {
            printf("Error from read: total:%d rdlen:%d: %s\n", total, rdlen, strerror(errno));
            try_again=0;
        } else if (rdlen == 0) {
            printf("Timeout from read. total:%d buf:%s\n", total, buf);
        } else {
            buf[rdlen]='\0';
            if (buf[rdlen-1] == '\n' && rdlen == 18){ 
                printf("Success. total:%d rdlen:%d  buf:%s", total, rdlen, buf);
	              try_again=0; 
            } else 
                printf("Partial read: total:%d rdlen:%d  buf:%s\n", total, rdlen, buf);
        }
    }
    printf("Stringa: %s and number: %f\n", buf, get_double(buf));  
    printf("Peso: %f\n", get_double(buf));
}

Open in new window

Rocco GalatiR&D Engineer

Author

Commented:
Hello noci, thank you for your support, I didn't answer you yet because I lost the remote connection with my host where I was trying the code and so I need to go there and reboot it during next days.
nociSoftware Engineer
Distinguished Expert 2018

Commented:
no problem.
Rocco GalatiR&D Engineer

Author

Commented:
I had the chance to test it today and I think it works great, now!
Thank you, noci, for all your support and your suggestions!