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.