[c] Reading and writing to serial port in C on Linux

I'm trying to send/receive data over an USB Port using FTDI, so I need to handle serial communication using C/C++. I'm working on Linux (Ubuntu).

Basically, I am connected to a device which is listening for incoming commands. I need to send those commands and read device's response. Both commands and response are ASCII characters.

Everything works fine using GtkTerm but, when I switch to C programming, I encounter problems.

Here's my code:

#include <stdio.h>      // standard input / output functions
#include <stdlib.h>
#include <string.h>     // string function definitions
#include <unistd.h>     // UNIX standard function definitions
#include <fcntl.h>      // File control definitions
#include <errno.h>      // Error number definitions
#include <termios.h>    // POSIX terminal control definitions

/* Open File Descriptor */
int USB = open( "/dev/ttyUSB0", O_RDWR| O_NONBLOCK | O_NDELAY );

/* Error Handling */
if ( USB < 0 )
{
cout << "Error " << errno << " opening " << "/dev/ttyUSB0" << ": " << strerror (errno) << endl;
}

/* *** Configure Port *** */
struct termios tty;
memset (&tty, 0, sizeof tty);

/* Error Handling */
if ( tcgetattr ( USB, &tty ) != 0 )
{
cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << endl;
}

/* Set Baud Rate */
cfsetospeed (&tty, B9600);
cfsetispeed (&tty, B9600);

/* Setting other Port Stuff */
tty.c_cflag     &=  ~PARENB;        // Make 8n1
tty.c_cflag     &=  ~CSTOPB;
tty.c_cflag     &=  ~CSIZE;
tty.c_cflag     |=  CS8;
tty.c_cflag     &=  ~CRTSCTS;       // no flow control
tty.c_lflag     =   0;          // no signaling chars, no echo, no canonical processing
tty.c_oflag     =   0;                  // no remapping, no delays
tty.c_cc[VMIN]      =   0;                  // read doesn't block
tty.c_cc[VTIME]     =   5;                  // 0.5 seconds read timeout

tty.c_cflag     |=  CREAD | CLOCAL;     // turn on READ & ignore ctrl lines
tty.c_iflag     &=  ~(IXON | IXOFF | IXANY);// turn off s/w flow ctrl
tty.c_lflag     &=  ~(ICANON | ECHO | ECHOE | ISIG); // make raw
tty.c_oflag     &=  ~OPOST;              // make raw

/* Flush Port, then applies attributes */
tcflush( USB, TCIFLUSH );

if ( tcsetattr ( USB, TCSANOW, &tty ) != 0)
{
cout << "Error " << errno << " from tcsetattr" << endl;
}

/* *** WRITE *** */

unsigned char cmd[] = {'I', 'N', 'I', 'T', ' ', '\r', '\0'};
int n_written = write( USB, cmd, sizeof(cmd) -1 );

/* Allocate memory for read buffer */
char buf [256];
memset (&buf, '\0', sizeof buf);

/* *** READ *** */
int n = read( USB, &buf , sizeof buf );

/* Error Handling */
if (n < 0)
{
     cout << "Error reading: " << strerror(errno) << endl;
}

/* Print what I read... */
cout << "Read: " << buf << endl;

close(USB);

What happens is that read() returns 0 (no bytes read at all) or block until timeout (VTIME). I'm assuming this happens because write() does not send anything. In that case, device wouldn't receive command and I cannot receive response. In fact, turning off the device while my program is blocked on reading actually succeded in getting a response (device sends something while shutting down).

Strange thing is that adding this

cout << "I've written: " << n_written << "bytes" << endl; 

right after write() call, I receive:

I've written 6 bytes

which is exactly what I expect. Only my program doesn't work as it should, like my device cannot receive what I'm actually writing on port.

I've tried different things and solution, also regarding data types (I've tried using std::string, such as cmd = "INIT \r" or const char) but nothing really worked.

Can someone tell me where I'm wrong?

Thank you in advance.

EDIT: Previously version of this code used

unsigned char cmd[] = "INIT \n"

and also cmd[] = "INIT \r\n". I changed it because command sintax for my device is reported as

<command><SPACE><CR>.

I've also tried avoiding the O_NONBLOCK flag on reading, but then I only block until forever. I've tried using select() but nothing happens. Just for a try, I've created a waiting loop until data is avaliable, but my code never exit the loop. Btw, waiting or usleep() is something I need to avoid. Reported one is only an excerpt of my code. Complete code needs to work in a real-time environment (specifically OROCOS) so I don't really want sleep-like function.

This question is related to c linux serial-port tty

The answer is


1) I'd add a /n after init. i.e. write( USB, "init\n", 5);

2) Double check the serial port configuration. Odds are something is incorrect in there. Just because you don't use ^Q/^S or hardware flow control doesn't mean the other side isn't expecting it.

3) Most likely: Add a "usleep(100000); after the write(). The file-descriptor is set not to block or wait, right? How long does it take to get a response back before you can call read? (It has to be received and buffered by the kernel, through system hardware interrupts, before you can read() it.) Have you considered using select() to wait for something to read()? Perhaps with a timeout?

Edited to Add:

Do you need the DTR/RTS lines? Hardware flow control that tells the other side to send the computer data? e.g.

int tmp, serialLines;

cout << "Dropping Reading DTR and RTS\n";
ioctl ( readFd, TIOCMGET, & serialLines );
serialLines &= ~TIOCM_DTR;
serialLines &= ~TIOCM_RTS;
ioctl ( readFd, TIOCMSET, & serialLines );
usleep(100000);
ioctl ( readFd, TIOCMGET, & tmp );
cout << "Reading DTR status: " << (tmp & TIOCM_DTR) << endl;
sleep (2);

cout << "Setting Reading DTR and RTS\n";
serialLines |= TIOCM_DTR;
serialLines |= TIOCM_RTS;
ioctl ( readFd, TIOCMSET, & serialLines );
ioctl ( readFd, TIOCMGET, & tmp );
cout << "Reading DTR status: " << (tmp & TIOCM_DTR) << endl;

Some receivers expect EOL sequence, which is typically two characters \r\n, so try in your code replace the line

unsigned char cmd[] = {'I', 'N', 'I', 'T', ' ', '\r', '\0'};

with

unsigned char cmd[] = "INIT\r\n";

BTW, the above way is probably more efficient. There is no need to quote every character.


Examples related to c

conflicting types for 'outchar' Can't compile C program on a Mac after upgrade to Mojave Program to find largest and second largest number in array Prime numbers between 1 to 100 in C Programming Language In c, in bool, true == 1 and false == 0? How I can print to stderr in C? Visual Studio Code includePath "error: assignment to expression with array type error" when I assign a struct field (C) Compiling an application for use in highly radioactive environments How can you print multiple variables inside a string using printf?

Examples related to linux

grep's at sign caught as whitespace How to prevent Google Colab from disconnecting? "E: Unable to locate package python-pip" on Ubuntu 18.04 How to upgrade Python version to 3.7? Install Qt on Ubuntu Get first line of a shell command's output Cannot connect to the Docker daemon at unix:/var/run/docker.sock. Is the docker daemon running? Run bash command on jenkins pipeline How to uninstall an older PHP version from centOS7 How to update-alternatives to Python 3 without breaking apt?

Examples related to serial-port

python to arduino serial read & write Reading serial data in realtime in Python Python: Writing to and Reading from serial port IOException: read failed, socket might closed - Bluetooth on Android 4.3 Reading and writing to serial port in C on Linux Python Serial: How to use the read or readline function to read more than 1 character at a time Serial Port (RS -232) Connection in C++ "The semaphore timeout period has expired" error for USB connection What is the correct way to read a serial port using .NET framework? Arduino COM port doesn't work

Examples related to tty

How to fix 'sudo: no tty present and no askpass program specified' error? How to enter in a Docker container already running with a new TTY Reading and writing to serial port in C on Linux What do pty and tty mean?