0

I have a digital scale connected via USB to my Ubuntu laptop and I would like to read the measurements from it. The serial protocol is very simple (9600,8N1, ttyUSB0) and I'm able to correctly read the measurements by using putty (VT100+) from terminal.

The scale needs to receive the command

"READ<CR><LF>"

in order to send the measurement. Each measurement has this format:

01ST,GS,   2.5,kg<CR><LF>

if, for example, I'm measuring a 2.5Kg load.

Now, I'm trying to send the READ command from a C application, but I'm not able to get any answer.

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

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()
{
    char *portname = "/dev/ttyUSB0";
    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\n", 5);
    if (wlen != 5) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    tcdrain(fd);    /* delay for output */


    /* simple noncanonical input */
    do {
        unsigned char buf[80];
        int rdlen;

        rdlen = read(fd, buf, sizeof(buf) - 1);
        if (rdlen > 0) {

            buf[rdlen] = 0;
            printf("Read %d: \"%s\"\n", rdlen, buf);

        } else if (rdlen < 0) {
            printf("Error from read: %d: %s\n", rdlen, strerror(errno));
        } else {  /* rdlen == 0 */
            printf("Timeout from read\n");
        }               
        /* repeat read to get full message */
    } while (1);
}

Can you help me, please? Thank you! I'm a beginner, so may be it's just a stupid error I cannot see.

However, is there any other faster way to acquire the same task?

Marcus Barnet
  • 2,083
  • 6
  • 28
  • 36
  • 1
    You would be better off using canonical mode, and then your program will retrieve a complete line per **read()** syscall (assuming you provide a buffer large enough). See https://stackoverflow.com/questions/57152937/canonical-mode-linux-serial-port/57155531#57155531 – sawdust Sep 09 '19 at 21:24
  • @Marcus Barnet: 1) please upvote and "accept" wallyk's response, if you haven't already. 2) sawdust is correct: since "\r]n" is already part of the device's protocol ... you might just as well stay in "canonical" mode and just read a line at a time. 3) Just as the scale has a "READ" command ... it might also have some kind of "STOP" command. Look at the manual :) – paulsm4 Sep 10 '19 at 00:48
  • OT: regarding statements like: `printf("Error from tcgetattr: %s\n", strerror(errno));` Error messages should be output to `stderr`, not `stdout` Suggest using: `fprintf( stderr, "Error from tcgetattr: %s\n", strerror( errno ));` – user3629249 Sep 10 '19 at 01:12

1 Answers1

3

Probably the command should be terminated with a carriage return (not a linefeed as you have written):

    wlen = write(fd, "READ\n", 5);

change to

    wlen = write(fd, "READ\r", 5);

Of if it really (it might, but maybe not) has to receive cr lf:

    wlen = write(fd, "READ\r\n", 6);
wallyk
  • 56,922
  • 16
  • 83
  • 148
  • 1
    I think you've very likely identified the problem. And thank you for updating your post with `\r\n` :) – paulsm4 Sep 09 '19 at 18:58
  • 1
    Thank you, \r\n solved the problem and now I get read the scale answer! Is there any method to stop reading from the scale when the program receives \r\n? – Marcus Barnet Sep 09 '19 at 19:28
  • 1
    `/* simple noncanonical input */ do { unsigned char buf[80]; int rdlen; rdlen = read(fd, buf, sizeof(buf) - 1); if (rdlen > 0) { buf[rdlen] = 0; printf("%s", buf); } else if (rdlen < 0) { printf("Error from read: %d: %s\n", rdlen, strerror(errno)); } else { /* rdlen == 0 */ printf("Timeout from read\n"); } /* repeat read to get full message */ } while (1); ` – Marcus Barnet Sep 09 '19 at 19:31
  • @MarcusBarnet, The function: `read()` does not return an 'int' rather it returns an `ssize_t` – user3629249 Sep 10 '19 at 01:16