1

I am writing a c program to log data that arrives via serial port from an instrument. The instrument is interrogated using various ascii characters terminated in a single carriage return.

The instrument responds immediately with the value requested terminated with <0D><0A>.

I have tested using SerialTools (I am running a MAC OSX M1 Max, deleoping the code using XCode/C).

I have previosuly written c-code to interrogate other instruments using the serial port and logged data successfully. In this new case I am unable to receive any data after the very first request.

The instrument requires: Serial: 9600baud, N, 8, 1

I have taken advice from online forums - notably from the termios code provided by sawdust termios code, which is excellent - on how to set up the serial port for canonical input. I took the view that canonical input was best as the instrument always terminates the data with <0D><0A>. The code below shows the operation. The first time it writes to the port 'd\r', and immediately reads from the port, we get the expected response ' 2.31<0D><0A>'. Every time I subsequently write the same character 'd\r' the read does not provide a new value but rather ' .31<0D><0A>', which looks like it is reporting back a shifted value. I suspect the instrument is not receiving the second request correctly and the shift is something to do with the buffer/serial port. Here's the relevant code:


#define TERMINAL    "/dev/cu.usbserial-4013420"
#define DISPLAY_STRING 1

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

#include <sys/stat.h>
#include <sys/types.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 */

    tty.c_lflag |= ICANON | ISIG;  /* canonical input */
    tty.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);
    tty.c_lflag |= (ICANON | ECHO | ECHOE);

        tty.c_iflag &= ~IGNCR;  /* preserve carriage return */
        tty.c_iflag &= ~INPCK;
        tty.c_iflag &= ~(INLCR | ICRNL | IMAXBEL);
        tty.c_iflag &= ~(IXON | IXOFF | IXANY);   /* no SW flowcontrol */
        tty.c_oflag &= ~OPOST;
 tty.c_cc[VEOF] = 0x0A;
    
    
    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }

   
    return 0;
}


int main(int argc, const char * argv[])
{
    char *portname = TERMINAL;
    int fd;
    size_t wlen;            // use size_t for unsigned int
    int tester;
    char *xstr = "d\r";
    size_t xlen = strlen(xstr);
    unsigned char buff[10]={0};
    size_t rdlen2;
    
    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);

    
    /* simple output  write, then read and repeat...*/
   
    printf("xstr is %s\n", xstr);
    
    wlen = write(fd, xstr, xlen);
    
    rdlen2 = read(fd, buff, sizeof(buff));

    wlen = write(fd, xstr, xlen);

    rdlen2 = read(fd, buff, sizeof(buff));

    wlen = write(fd, xstr, xlen);

    rdlen2 = read(fd, buff, sizeof(buff));

    wlen = write(fd, xstr, xlen);

    rdlen2 = read(fd, buff, sizeof(buff));

    wlen = write(fd, xstr, xlen);

    rdlen2 = read(fd, buff, sizeof(buff));
   

    close(fd);
}

I have tried various ways of flushing buffers, and use of tcdrain to make sure write was completed. Nothing seems to resolve the problem. Thanks for your advice.

Markys
  • 33
  • 5
  • I'm not sure if this could be a way of debugging it further, but it seems like it could be: _"Applications that need all of the requested changes made to work properly should follow `tcsetattr()` with a call to `tcgetattr()` and compare the appropriate field values."_ - That is, even a successful `tcsetattr` call may have failed to perform some (all but one) of the requested changes. – Ted Lyngmo Jul 26 '23 at 09:57
  • 1
    Thanks - I have actually just spotted my error! I had echo turned OFF (correctly) and then on the next line I turn it ON - tty.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN); tty.c_lflag |= (ICANON | ECHO | ECHOE); - -- really silly mistake on my part. Apologies. The poling and logging now works as expected. – Markys Jul 26 '23 at 10:05
  • 1
    Cool! Please provide a proper answer to your question. That's ok and even encouraged since it may help others with similar problems to find the solution - unless you disregard this as a simple typo yourself. – Ted Lyngmo Jul 26 '23 at 10:08

1 Answers1

1

I have actually just spotted my error!

I had echo turned OFF (correctly) and then on the following line I turned it ON - see snippet here:

tty.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN); 

tty.c_lflag |= (ICANON | ECHO | ECHOE); 

We need to remove the line:

tty.c_lflag |= (ICANON | ECHO | ECHOE); 
  • -- A simple mistake on my part, but maybe this will be of help to someone else, as suggested by Ted Lyngmo.

It is embarrassing that I used some perfectly working code (see above link) and broke it. My fault, caused by not fully understanding the serial port set up conditions.

The poling and logging now work as expected.

Markys
  • 33
  • 5
  • "*The code below shows how I have set this up.*" -- That termios code looks a lot like [my code](https://stackoverflow.com/questions/57152937/canonical-mode-linux-serial-port/57155531#57155531) but without any attribution. Amazing how you managed to break a working example. You also added a garbage VEOF assignment. – sawdust Jul 26 '23 at 19:16
  • Oh gosh - I am so sorry. I forgot to add your attribution. I really appreciate the support and once again apologies. For sure, I am no expert and messed up. – Markys Jul 27 '23 at 16:39