0

I'm using a digital scale connected to the rs232 port on the back of my pc with under Ubuntu. The application is written in WxWidgets 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>

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? Sometimes, it prints "Entering the while loop.." and nothing else then I need to send CTRL+c to quit the process. 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 code (minimal working code for tests):

#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 */

    /* 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));  

}
Marcus Barnet
  • 2,083
  • 6
  • 28
  • 36
  • 1
    Define "stuck before the while loop". There is a very obvious bug in the shown code which will happen under certain conditions; unfortunately because the shown code falis to meet the requirements for [mre] -- it won't even compile, because it appears that bits and pieces of random parts of your real code have been glued together (like the main loop appears to be outside of any function declaration), it's a waste of time trying to explain it, because your real problem could be something else, entirely. Please read [ask], for more information. – Sam Varshavchik Nov 29 '19 at 00:18
  • Thank you for your comment, I added a working reproducible example and I defined the stuck situation: sometimes, it prints only and then I need to manually close the program. – Marcus Barnet Nov 29 '19 at 09:09
  • Instead of a **while (...) {...}** loop, you would be better off learning how to program a **do {...} while (...)** loop, which would allow a simpler conditional expression. Looks like a portion of what you call *"my code"* has been copied from my post (without attribution). But apparently you don't want to use my advice from [your previous post](https://stackoverflow.com/questions/57859690/read-and-write-from-serial-port-under-ubuntu-for-usb-scale). – sawdust Nov 29 '19 at 10:56
  • Your program gets *"stuck"* because it fails to properly scan every pair of bytes for "\r\n". Nor does it count the "timeout" consistently. Did you print out what was the read returned? If **read()** ever returns an error, then your code does not handle that correctly. – sawdust Nov 29 '19 at 11:22
  • I'm sorry, sawdust, but that isn't your code, I'm pretty sure about this because it has been generated by another programmer and I adapted it to my needs. However, the program works fine 80% of cases, when it fails, I'm not able to read the read() return value. – Marcus Barnet Nov 29 '19 at 11:43
  • *"that isn't your code, I'm pretty sure about this"* -- Try a diff of 74 lines of what you posted starting at **set_interface_attribs()** with [code I posted](https://stackoverflow.com/questions/6947413/how-to-open-read-and-write-from-serial-port-in-c/38318768#38318768). Except for about 5 word edits & two inserted lines in **main()**, it's a perfect match, including comments and formatting. That cannot be a coincidence. Since you seem to be unaware of the origin of what you call "my code", that could explain why you're using my code but ignoring my advice. Seems hypocritical, though. – sawdust Nov 30 '19 at 01:02
  • I got that code from EE which is a payment-based community and a user suggested me to use that code. I still have that conversation so I can even give you the screenshot. – Marcus Barnet Nov 30 '19 at 08:48
  • So you paid for this code that you think was *"generated by another programmer"*? Can you post that screenshot on some sharable site, e.g. dropbox, google? – sawdust Dec 02 '19 at 07:51

0 Answers0