How to open, read, and write from serial port in C? - c

I am a little bit confused about reading and writing to a serial port. I have a USB device in Linux that uses the FTDI USB serial device converter driver. When I plug it in, it creates: /dev/ttyUSB1.
I thought itd be simple to open and read/write from it in C. I know the baud rate and parity information, but it seems like there is no standard for this?
Am I missing something, or can someone point me in the right direction?

I wrote this a long time ago (from years 1985-1992, with just a few tweaks since then), and just copy and paste the bits needed into each project.
You must call cfmakeraw on a tty obtained from tcgetattr. You cannot zero-out a struct termios, configure it, and then set the tty with tcsetattr. If you use the zero-out method, then you will experience unexplained intermittent failures, especially on the BSDs and OS X. "Unexplained intermittent failures" include hanging in read(3).
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
int
set_interface_attribs (int fd, int speed, int parity)
{
struct termios tty;
if (tcgetattr (fd, &tty) != 0)
{
error_message ("error %d from tcgetattr", errno);
return -1;
}
cfsetospeed (&tty, speed);
cfsetispeed (&tty, speed);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
tty.c_iflag &= ~IGNBRK; // disable break processing
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_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag |= parity;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
if (tcsetattr (fd, TCSANOW, &tty) != 0)
{
error_message ("error %d from tcsetattr", errno);
return -1;
}
return 0;
}
void
set_blocking (int fd, int should_block)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
error_message ("error %d from tggetattr", errno);
return;
}
tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
if (tcsetattr (fd, TCSANOW, &tty) != 0)
error_message ("error %d setting term attributes", errno);
}
...
char *portname = "/dev/ttyUSB1"
...
int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0)
{
error_message ("error %d opening %s: %s", errno, portname, strerror (errno));
return;
}
set_interface_attribs (fd, B115200, 0); // set speed to 115,200 bps, 8n1 (no parity)
set_blocking (fd, 0); // set no blocking
write (fd, "hello!\n", 7); // send 7 character greeting
usleep ((7 + 25) * 100); // sleep enough to transmit the 7 plus
// receive 25: approx 100 uS per char transmit
char buf [100];
int n = read (fd, buf, sizeof buf); // read up to 100 characters if ready to read
The values for speed are B115200, B230400, B9600, B19200, B38400, B57600, B1200, B2400, B4800, etc. The values for parity are 0 (meaning no parity), PARENB|PARODD (enable parity and use odd), PARENB (enable parity and use even), PARENB|PARODD|CMSPAR (mark parity), and PARENB|CMSPAR (space parity).
"Blocking" sets whether a read() on the port waits for the specified number of characters to arrive. Setting no blocking means that a read() returns however many characters are available without waiting for more, up to the buffer limit.
Addendum:
CMSPAR is needed only for choosing mark and space parity, which is uncommon. For most applications, it can be omitted. My header file /usr/include/bits/termios.h enables definition of CMSPAR only if the preprocessor symbol __USE_MISC is defined. That definition occurs (in features.h) with
#if defined _BSD_SOURCE || defined _SVID_SOURCE
#define __USE_MISC 1
#endif
The introductory comments of <features.h> says:
/* These are defined by the user (or the compiler)
to specify the desired environment:
...
_BSD_SOURCE ISO C, POSIX, and 4.3BSD things.
_SVID_SOURCE ISO C, POSIX, and SVID things.
...
*/

For demo code that conforms to POSIX standard as described in Setting Terminal Modes Properly
and Serial Programming Guide for POSIX Operating Systems, the following is offered.
This code should execute correctly using Linux on x86 as well as ARM (or even CRIS) processors.
It's essentially derived from the other answer, but inaccurate and misleading comments have been corrected.
This demo program opens and initializes a serial terminal at 115200 baud for non-canonical mode that is as portable as possible.
The program transmits a hardcoded text string to the other terminal, and delays while the output is performed.
The program then enters an infinite loop to receive and display data from the serial terminal.
By default the received data is displayed as hexadecimal byte values.
To make the program treat the received data as ASCII codes, compile the program with the symbol DISPLAY_STRING, e.g.
cc -DDISPLAY_STRING demo.c
If the received data is ASCII text (rather than binary data) and you want to read it as lines terminated by the newline character, then see this answer for a sample program.
#define TERMINAL "/dev/ttyUSB0"
#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 = TERMINAL;
int fd;
int wlen;
char *xstr = "Hello!\n";
int xlen = strlen(xstr);
fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) {
printf("Error opening %s: %s\n", portname, strerror(errno));
return -1;
}
/*baudrate 115200, 8 bits, no parity, 1 stop bit */
set_interface_attribs(fd, B115200);
//set_mincount(fd, 0); /* set to pure timed read */
/* simple output */
wlen = write(fd, xstr, xlen);
if (wlen != xlen) {
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) {
#ifdef DISPLAY_STRING
buf[rdlen] = 0;
printf("Read %d: \"%s\"\n", rdlen, buf);
#else /* display hex */
unsigned char *p;
printf("Read %d:", rdlen);
for (p = buf; rdlen-- > 0; p++)
printf(" 0x%x", *p);
printf("\n");
#endif
} 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);
}
For an example of an efficient program that provides buffering of received data yet allows byte-by-byte handing of the input, then see this answer.

Related

Different serial port behaviour between FreeBSD and Linux

I am learning C and decided to build a small LCD status panel for one of my servers. This server runs FreeBSD but I, as a matter of discipline, wants to keep the code portable, so I also test in Linux.
So I decided to try to build the code from this answer as a learning exercise:
How to open, read, and write from serial port in C?
I built the code from the answer by sawdust:
#define TERMINAL "/dev/ttyUSB0"
#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 = TERMINAL;
int fd;
int wlen;
char *xstr = "Hello!\n";
int xlen = strlen(xstr);
fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) {
printf("Error opening %s: %s\n", portname, strerror(errno));
return -1;
}
/*baudrate 115200, 8 bits, no parity, 1 stop bit */
set_interface_attribs(fd, B115200);
//set_mincount(fd, 0); /* set to pure timed read */
/* simple output */
wlen = write(fd, xstr, xlen);
if (wlen != xlen) {
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) {
#ifdef DISPLAY_STRING
buf[rdlen] = 0;
printf("Read %d: \"%s\"\n", rdlen, buf);
#else /* display hex */
unsigned char *p;
printf("Read %d:", rdlen);
for (p = buf; rdlen-- > 0; p++)
printf(" 0x%x", *p);
printf("\n");
#endif
} 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);
}
The code builds fine and without warning. My Arduino is set to only clear the LCD screen if there's something new coming via serial, and it answers back OK when it updates the screen. I confirmed it works fine via screen.
For testing, I wrote manually some random characters to the screen and then launched the code. It works fine in Linux, but in FreeBSD the LCD goes blank.
So I launched lldb to see what's going on, set a few breakpoints in the relevant places and started going through the code. I couldn't see any problems and, to my surprise, the LCD was showing the test text. But whenever I launch it directly, I only get a blank screen. I inserted a few sleeps in the code to see if, by any chance, FreeBSD was not running something too fast or screwing up with timers, but nothing helped.
Do you have any ideas on how I could better leverage lldb to find out what is going on or an idea of what the problem could be?
Sleeping for 2 seconds after opening the port before trying any other operation did the trick for me in FreeBSD
fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
sleep(2);
if (fd < 0) {
printf("Error opening %s: %s\n", portname, strerror(errno));
return -1;
}

C programming checking for messages in a buffer

I am new to C programming so this may be a basic question.
I have an Arduino talking to my PC over serial. The problem I am encountering is my PC is reading faster then serial data coming in (probably a common problem).
The messages that will be sent between PC and arduino will have some defined package structure. For this example, each message starts with an ASCII '<' and ends with a '>'
I am able to open/create the serial port and receive data (copied some code from stackoverflow), however I would like to modify the program to:
1) Read all available data from the serial port into a message buffer, non blocking so if no data is available continue to #2
2) Start at the beginning of the message buffer for a full message, if there is a '<' keep searching until a '>' is found. Once a full message is found, print the message to the screen, and erase the message from the buffer, moving any non processed data to the beginning of the buffer (This is the part I am really stuck on). If no '>' is found, continue to #1
I would like to copy the logic/functions to the arduino so both arduino and PC are communicating the same way.
Here is the code I copied that opens the serial port and continuously reads anything from the serial port
#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/ttyACM0";
int fd;
int wlen;
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 */
wlen = write(fd, "<Hello Arduino!>", 7);
if (wlen != 7) {
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) {
unsigned char *p;
printf("Read %d:", rdlen);
for (p = buf; rdlen-- > 0; p++)
printf(" 0x%x", *p);
printf("\n");
} 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);
}
(1) Set the file descriptor to nonblocking when opening or just use poll() to check if there is data to read. If you needed to get into the kernel buffer and check for how many bytes are in the buffer, you could use ioctl(fd, FIONREAD, &result), but as someone pointed out you really shouldn't need to. Either way, man ioctl_tty(2) has details about the ioctls available for ttys.
(2) You just need to check for '<' in your buffer as you iterate through the input. Something like this:
int in_tag = 0;
for (;;) {
// read block
// for each char 'c'
if (in_tag) {
if (c == '>') in_tag = 0;
} else if (c == '<') in_tag = 1;
of course, figuring out how to handle things like "<>" is a whole other question....

Linux UART transmitting Problems

I'm trying to communicate with an external IC via UART/tty in C. I'm using a nanoPI neo2 (allwinner h5) and friendlyARM's own Linux OS (which is a modified Ubuntu core).
I'v tried to get it working, but the TX output of the pi is ?logical inverted?. You can see what I mean in the oscilloscope pictures. There you can see if I select the "Polarity Invert"- Mode the data is ok, but in normal mode it isn't working.
In the past, I got around that by setting the invert Bit of the MCU (e.g. PIC MCU), but the IC I'm using now doesn't support this feature.
http://wiki.friendlyarm.com/wiki/index.php/NanoPi_NEO2
oscilloscope picture 1
oscilloscope picture 2
c source
#include <stdio.h>
//#include <errno.h>
//#include <termios.h>
//#include <unistd.h>
#include <string.h>
#include <fcntl.h> /* File Control Definitions */
#include <termios.h>/* POSIX Terminal Control Definitions*/
#include <unistd.h> /* UNIX Standard Definitions */
#include <errno.h> /* ERROR Number Definitions */
#include <unistd.h>
#include <sys/types.h>
#include <sys/signal.h>
/* source : http://stackoverflow.com/a/6947758 */
int fd;
int set_interface_attribs (int fd, int speed, int parity) {
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0) {
perror ("error from tcgetattr");
return -1;
}
cfsetospeed (&tty, speed);
cfsetispeed (&tty, speed);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
tty.c_iflag &= ~IGNBRK; // disable break processing
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_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag |= parity;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
if (tcsetattr (fd, TCSANOW, &tty) != 0) {
perror ("error from tcsetattr");
return -1;
}
return 0;
}
void set_blocking (int fd, int should_block) {
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
perror ("error from tggetattr");
return;
}
tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
if (tcsetattr (fd, TCSANOW, &tty) != 0)
perror ("error setting term attributes");
}
int main()
{
char *portname = "/dev/ttyS0";
int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0)
{
//perror ("error %d opening %s: %s", errno, portname, strerror (errno));
return;
}
set_interface_attribs (fd, B115200, 0); // set speed to 115,200 bps, 8n1 (no parity)
set_blocking (fd, 0); // set no blocking
write (fd, "hello!\n", 7); // send 7 character greeting
usleep ((7 + 25) * 100); // sleep enough to transmit the 7 plus
// receive 25: approx 100 uS per char transmit
char buf [100];
int n = read (fd, buf, sizeof buf); // read up to 100 characters if ready to read
}
Thanks in advance
Axel
The picture on the scope looks fine - it is an UART signal. The problem here is not programming-related, but failure to understand the difference between UART and RS-232.
You have picked RS-232 encoding on your scope, and RS-232 uses a different, inverted polarity compared to UART.
UART idles high voltage=Vdd, RS-232 idles at negative voltage < -3V.
On UART, a high voltage=Vdd bit means 1 and low voltage=0V means 0.
On RS-232, a high voltage > +3V bit means 0, and negative voltage < -3V means 1.
So when you try to decode a UART signal with RS-232 encoding, it works when you inverse the polarity. Apparently Rigol isn't picky about the actual voltage levels, since it doesn't understand that the signal is not RS-232 at all.

Working with linux serial port in C, Not able to get full data

I am working with Linux Serial port written in C. Below is my UART settings
int fd;
struct termios tty_attributes;
fd = open(comport, O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK );
if(fd < 0)
{
perror("open comport error.\n");
exit(EXIT_FAILURE);
}
else
{
if(tcgetattr(fd, &tty_attributes) == -1)
{
perror("tcgetattr termios function error.\n");
exit(EXIT_FAILURE);
}
tty_attributes.c_lflag = 0;
tty_attributes.c_oflag = 0;
tty_attributes.c_iflag = 0;
tty_attributes.c_cflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
tty_attributes.c_cflag |= CS8;
tty_attributes.c_cflag |= CLOCAL;
tty_attributes.c_cflag &= ~CREAD;
tty_attributes.c_oflag &= ~OPOST;
tty_attributes.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
tty_attributes.c_cc[VMIN] = SIZE_STR_FRAME;
cfsetospeed(&tty_attributes, BAUDRATE); //setting communication speed and other attributes
cfsetispeed(&tty_attributes, BAUDRATE);
tcflush(fd, TCIOFLUSH);
tcsetattr(fd, TCSANOW, &tty_attributes); //change immediately
return fd;
}
}
And below is my code for Reading the frame
char* frame_read(int fd)
{
char *ret = NULL;
int read_ret_val;
struct timeval time_val;
if (fd < 0)
{
printf("Before read over comm channel, channel must be initialize\n");
exit(EXIT_FAILURE);
}
memset(frame, 0, SIZE);
fd_set rfds; //read file discriptors
int return_val;
FD_SET(fd, &rfds);
setReceiveMode(fd, TRUE);
tcflush(fd, TCIFLUSH);
tcflush(fd, TCOFLUSH); //flush previous values
return_val = select((fd) + 1, &rfds, NULL, NULL, &time_val);
if (return_val == -1)
{
perror("select");
exit(EXIT_FAILURE);
}
else if (return_val)
{
usleep(100 * 1000);
read_ret_val = read(fd, frame, SIZE);
if (read_ret_val < 0)
{
perror("read");
exit(EXIT_FAILURE);
}
ret = frame;
//printf("inside else if of read\n");
}
}
I have one gps module is connected with the UART and when i check with minicom I am getting full frame but when i receive over uart(using this code) I am getting first 16 bytes only.
Can anyone point my mistake.?
Here baud is 9600 , frame is of 64 bytes and SIZE is of 64 bytes.,buffer i took is also 64 bytes. Please forgive me for formating errors if any.
My main.c file
int main(int argc, char *argv[])
{
int i=0,j=0;
char *readb;
unsigned char data[34];
static int fd = -1;
struct struct_base_gps *gps;
int command=0;
char COMM_PORTNAME[13];
strcpy( COMM_PORTNAME, argv[1] );// give the first port number for GPS receiving
if((fd = init_comm_channel(COMM_PORTNAME)) < 0 )
{
exit(EXIT_FAILURE);
printf("port is not opened\n");
}
else
{
printf("port is open for communication:\n");
readb = frame_read(fd);
for (i=0;i<=34;i++)
{
data[i] = *(readb +j);
printf("the data is %x\n",data[i]);
j++;
}
}
close (fd);
}
for SIZE is
#define SIZE 64
and frame is
char frame[64];
Thank you for feedback, I have updated the code.
Also Updating the Frame pics which I am getting on terminal as well as with program. Might It will clear more.
Received the data from UART by program
minicom recived
Looking at The Man
RETURN VALUE
On success, the number of bytes read is returned (zero indicates end
of file), and the file position is advanced by this number. It is
not an error if this number is smaller than the number of bytes
requested; this may happen for example because fewer bytes are
actually available right now (maybe because we were close to end-of-
file, or because we are reading from a pipe, or from a terminal), or
because read() was interrupted by a signal. See also NOTES.
Emphasis mine
So you cannot expect that a whole frame can be retrieved by a single read.
You should loop until all expected chars are received, for example:
int total_rec = 0;
char temp[SIZE];
while( total_rec < SIZE )
{
read_ret_val = read(fd, temp, SIZE);
if (read_ret_val != -1)
{
if ( (total_rec + read_ret_val) >= SIZE)
{
read_ret_val = SIZE - total_rec;
}
memcpy(&frame[total_rec], temp, read_ret_val);
total_rec += read_ret_val;
}
else
{
perror("error reading serial line: ");
}
}
Try with
memset(&tty_attributes,0,sizeof(tty_attributes));
tty_attributes.c_iflag=0;
tty_attributes.c_oflag=0;
tty_attributes.c_cflag=CS8|CREAD|CLOCAL;
tty_attributes.c_lflag=0;
tty_attributes.c_cc[VMIN]=1;
tty_attributes.c_cc[VTIME]=5;
Most GPS modules and serial interfaces for devices in general send you data line by line. For this you can use canonical mode which you have explicitly disabled.
Canonical mode as stated in manual
In canonical mode:
Input is made available line by line. An input line is available when one of the line delimiters is typed (NL, EOL, EOL2; or EOF at the
start of line).
Except in the case of EOF, the line delimiter is included in the buffer returned by read(2).
I post code to set serial interface speed and parity with canonical mode enabled:
int set_interface_attribs(int fd, int speed, int parity)
{
// setup based on stty < /dev/ttyACM0 (cfg.txt) output which
// worked for ABSniffer in pyserial implementation
// otherwise module responded only once for every two prompts
struct termios tty;
int rc;
memset(&tty, 0, sizeof tty);
if (tcgetattr(fd, &tty) != 0)
{
log_info("error from tcgetattr %s\r\n", strerror(errno));
return -1;
}
rc = cfsetospeed(&tty, speed);
if (rc == - 1) return -1;
rc = cfsetispeed(&tty, speed);
if (rc == - 1) return -1;
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
tty.c_cc[VMIN] = 0; // read doesn't block
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
tty.c_cflag |= (CLOCAL | CREAD); // ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag |= parity;
tty.c_cflag &= ~CSTOPB;
// tty.c_iflag |= ICRNL | BRKINT; //ICRNL
tty.c_iflag |= IGNCR;
tty.c_cflag &= ~CRTSCTS;
// tty.c_oflag |= OPOST | ONLCR;
// tty.c_iflag |= ISIG | ICANON | IEXTEN;
tty.c_lflag |= ISIG | IEXTEN | ICANON;
tty.c_lflag &= ~ECHO;
tty.c_cc[VEOF] = 0x0;
tty.c_cc[VEOL] = 0x0;
if (tcsetattr(fd, TCSANOW, &tty) != 0)
{
log_info("error from tcsetattr %s\r\n", strerror(errno));
return -1;
}
return 0;
}
Here is how you use it:
rc = set_interface_attribs(fd, B9600, 0);
From now on data should be available line by line. All the errors and possible return values are explained in read manual. Assuming there are no errors, reading a buffer of some arbitrary size should return either EAGAIN (Resource temporarily unavailable) with return code -1 or bytes to the newline character '\n'.
Your original code has numerous issues which cause it to "getting first 16 bytes only":
The code (as posted) only performs a single read() syscall (rather than continuously loop to read the data from the device).
The input is obviously ASCII text delimited into lines terminated with carriage return and line feed, yet your program uses non-canonical mode to read rather than canonical mode. The assumption by #pbn is confirmed by the minicom output.
Your program uses the serial terminal in non-blocking mode, rather than blocking mode, and resorts to using select() and usleep() calls to wait for the arrival of data.
The termios initialization (besides not being POSIX compliant) has several errors, including improper iflag symbols applied to the cflag member, the character size bits are not cleared with ~CSIZE, and CREAD is not enabled.
Your read routine unnecessarily flushes (i.e. discards) all received but unread data prior to the select() call.
A revised routine for opening and configuring the serial terminal (for blocking canonical mode):
#define BAUDRATE B9600
int init_comm_channel(char *comport)
{
struct termios tty_attributes;
int fd;
fd = open(comport, O_RDWR | O_NOCTTY);
if (fd < 0) {
perror("open comport error.\n");
return (-2);
}
if (tcgetattr(fd, &tty_attributes) == -1) {
perror("tcgetattr termios function error.\n");
return (-3);
}
tty_attributes.c_cflag |= CLOCAL | CREAD;
tty_attributes.c_cflag &= ~CSIZE;
tty_attributes.c_cflag |= CS8; /* 8-bit characters */
tty_attributes.c_cflag &= ~PARENB; /* no parity bit */
tty_attributes.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty_attributes.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
tty_attributes.c_lflag |= ICANON | ISIG; /* canonical input */
tty_attributes.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);
tty_attributes.c_iflag &= ~INPCK;
tty_attributes.c_iflag |= IGNCR;
tty_attributes.c_iflag &= ~(INLCR | ICRNL | IUCLC | IMAXBEL);
tty_attributes.c_iflag &= ~(IXON | IXOFF | IXANY); /* no SW flowcontrol */
tty_attributes.c_oflag &= ~OPOST;
cfsetospeed(&tty_attributes, BAUDRATE); //setting communication speed and other attributes
cfsetispeed(&tty_attributes, BAUDRATE);
tcflush(fd, TCIOFLUSH);
if (tcsetattr(fd, TCSANOW, &tty_attributes) < 0) {
perror("tcsetattr function error.\n");
return (-4);
}
return fd;
}
The revised routine for reading a line per syscall:
#define SIZE 64
unsigned char frame[SIZE];
char *frame_read(int fd)
{
int read_ret_val;
if (fd < 0) {
printf("Before read over comm channel, channel must be initialize\n");
exit (EXIT_FAILURE);
}
read_ret_val = read(fd, frame, SIZE - 1);
if (read_ret_val < 0) {
perror("read");
exit (EXIT_FAILURE);
}
frame[read_ret_val] = 0; /* terminate string */
return (frame);
}
A revised main() routine that loops forever:
int main(int argc, char *argv[])
{
int fd;
char *readb;
char com_portname[13] = {0};
if (argc > 1)
strcpy(com_portname, argv[1]); // give the first port number for GPS receiving
if ((fd = init_comm_channel(com_portname)) < 0) {
printf("port is not opened\n");
exit (EXIT_FAILURE);
}
printf("port is open for communication:\n");
do {
readb = frame_read(fd);
while (*readb > 0)
printf("the data is 0x%x\n", *readb++);
printf("The line is: %s", frame);
} while (1); /* loop for another line */
close(fd);
}

C Failing to Read Data from Serial Port in Linux

I'm using the following C code borrowed from here: how to open, read, and write from serial port in C
with some slight modifications so that the program waits for keyboard input, then writes this data to a serial port and waits to read it back. Here's my code:
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
int
set_interface_attribs (int fd, int speed, int parity)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
error_message ("error %d from tcgetattr", errno);
return -1;
}
cfsetospeed (&tty, speed);
cfsetispeed (&tty, speed);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
tty.c_iflag &= ~IGNBRK; // ignore break signal
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_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag |= parity;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
if (tcsetattr (fd, TCSANOW, &tty) != 0)
{
error_message ("error %d from tcsetattr", errno);
return -1;
}
return 0;
}
void
set_blocking (int fd, int should_block)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
error_message ("error %d from tggetattr", errno);
return;
}
tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
if (tcsetattr (fd, TCSANOW, &tty) != 0)
error_message ("error %d setting term attributes", errno);
}
int main{
char *portname = "/dev/tty0"
int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0)
{
error_message ("error %d opening %s: %s", errno, portname, strerror (errno));
return;
}
set_interface_attribs (fd, B115200, 0); // set speed to 115,200 bps, 8n1 (no parity)
set_blocking (fd, 0); // set no blocking
while (1){
char data[20];
if (fgets(data, sizeof data, stdin)){
int m = write(fd, data, sizeof data);
printf("%d chars written\n", m);
char buf[100];
sleep(10);
int n = read (fd, buf, sizeof buf);
printf("%d chars read", n);
}
}
}
The problem is that while the keyboard input is successfully written to serial file, after sleeping for 10 seconds the program still reports that it has read 0 chars. Why is this? Do I need to actually have a physical device waiting on the other end of the port which receives the data and returns something, or is it fine to write to and read from the serial port with the same program? Thanks in advance for any help!
EDIT:
Ok so now I've created a second program which waits for data on the serial port sent by the above program, but this program is still unable to read the data sent to it. Here's the code:
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
int
set_interface_attribs (int fd, int speed, int parity)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
error_message ("error %d from tcgetattr", errno);
return -1;
}
cfsetospeed (&tty, speed);
cfsetispeed (&tty, speed);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
tty.c_iflag &= ~IGNBRK; // ignore break signal
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_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag |= parity;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
if (tcsetattr (fd, TCSANOW, &tty) != 0)
{
error_message ("error %d from tcsetattr", errno);
return -1;
}
return 0;
}
void
set_blocking (int fd, int should_block)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
error_message ("error %d from tggetattr", errno);
return;
}
tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
if (tcsetattr (fd, TCSANOW, &tty) != 0)
error_message ("error %d setting term attributes", errno);
}
int main{
char *portname = "/dev/tty0"
int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0)
{
error_message ("error %d opening %s: %s", errno, portname, strerror (errno));
return;
}
set_interface_attribs (fd, B115200, 0); // set speed to 115,200 bps, 8n1 (no parity)
set_blocking (fd, 0); // set no blocking
char buf[20];
int n = 0;
while (n == 0){
n = read(fd, buf, sizeof buf);
}
printf("%d chars read\n", n);
return 0;
}
When I run this and right afterwards run the first program and write some data to the serial port, this program just stays in the while loop forever and never reads in any data. Help?
No, you do not need to actually have a physical device waiting on the other end of the port which receives the data.
Yes, you need to actually have a physical device waiting on the other end of the port to return something. (It could be a wire looping transmit and receive (pins 2 & 3 on 9-pin.)
Yes, it fine to write to and read from the serial port with the same program.
As to "Why is this?": there is nothing sending data to your program via the serial interface.

Resources