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 // standard input / output functions #include #include // string function definitions #include // UNIX standard function definitions #include // File control definitions #include // Error number definitions #include // POSIX terminal control definitions /* Open File Descriptor */ int USB = open( "/dev/ttyUSB0", O_RDWR| O_NONBLOCK | O_NDELAY ); /* Error Handling */ if ( USB < 0 ) < cout /* *** Configure Port *** */ struct termios tty; memset (&tty, 0, sizeof tty); /* Error Handling */ if ( tcgetattr ( USB, &tty ) != 0 ) < cout /* 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 /* *** WRITE *** */ unsigned char cmd[] = ; 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 /* Print what I read. */ cout
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
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 . 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.
Receiving data from Serial Port
I want to receive data from RS232 Serial port to my terminal and with a directive to a file. It should be pretty straight forward.
cat /dev/ttyS0 cat /dev/ttyS0 > file.txt
sudo stty -F /dev/ttyS0 9600 -parity cs8 cstopb
But I don't receive anything on the terminal.It's just blank. What is the problem?
1 Answer 1
Try Minicom first and see what your serial port returns. Install it with sudo apt-get install minicom
You start it as follows (for ttyS0):
You can set the communication parameters from within Minicom (using ctrl-A P ), so you're sure that they are correct.
If your device uses a specific protocol, it might need a command to start its communication. So have a look at the user manual.
Maybe your device is set up to use hardware handshaking. If possible, turn it off (at least to start with).
If you can't turn it off, then you will have to set that up as well on your side. In Minicom this is under ctrl-A O and then serial port setup .
I've used Minicom often to debug serial communications and I find it works best.
I've received some more information from the OP:
The laboratory instrument(Cobas C311) uses ASTM protocol. There in the interface , we just need to click "Send to Host" and it sends a bunch of ASTM records. I just need to receive them in a file. Is there any other setting? What is the command to start acquiring data. and how to save the data in a file?
Chosen the right paramters. Minicom is showing 9600 8N2. Hope it's alright. But receiving nothing - not a single bit. The analyser says, "The instrument transmitted ENQ as a send request,but the Host did not return ACK or NAK within 15 seconds.(Link Timeout)"
The device uses the ASTM Protocol. The device sends the character as a signal that it is ready to start sending data.
This is not something you will be able to receive using minicom or any other terminal program. You will need proper application software that supports this protocol.
You definitely have to read your user manual and check the installation disk (if any) and the manufacturer's website for an application that supports this protocol.
I would be surprised if they don't have application support for this device. If they offer something it is likely to be a Windows application or some example code plus a library.
It is possible to write something yourself, but it won't be easy. There is some Python support for ASTM and there is a Perl script that you could try.
SO also has a post with some information about ASTM..