Read /dev/ttyUSB0 with a c program on LINUX - c

I want to read data frames sent by a GPS XBee protocol. The USB XStick receives the following data:
CHARS : 15931 SENTENCES = 0 CHECKSUM : 58
Heading : 55 Tilt: -46 Roll:2
CHARS : ....
and so on ... I can read by typing in the terminal control :
$ screen /dev/ttyUSB0
I'd like to see these details in the same way, but with a program written in C. Here's what I do :
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include "serial_port.h"
void read_Serial_Port(const char* DEVICE_PORT)
{
int file;
struct termios options;
char message[100];
unsigned int nCountMax = 60;
bool b;
file = open(DEVICE_PORT, O_RDONLY | O_NOCTTY | O_NDELAY);
if(file == -1){perror("Unable to open the serial port\n");}
printf("Serial port open successful\n");
tcgetattr(file, &options);
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag |= PARENB; //No parity
options.c_cflag |= PARODD;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8; //8 bits
options.c_iflag |= (INPCK | ISTRIP);
tcsetattr(file, TCSANOW, &options);
fcntl(file, F_SETFL, FNDELAY);
printf("Reading serial port ...\n\n");
b = readMessage(file, message, nCountMax);
if (b == 0){printf("Error while reading serial port\n");}
else printf("Serial port read successful\n");
close(file);
printf("Serial port closed\n");
};
bool readMessage(int file, char *message, unsigned int nCountMax)
{
int nbCharToRead;
char data[100];
int i;
if (file != 0)
{
i = 0;
while (i<nCountMax && data != ".")
{
if (read(file,&data,1) == -1)
{
printf("reception error\n");
return false;
}
else
{
message[i] = *data;
printf("%c", message[i]);
i++;
}
}
message[i] = 0;
return true;
}
}
But it does not work, I get "reception error", corresponding to:
read(file,&data,1) == -1
Where am I going wrong?
My program is the following:
bool readMessage(int file, unsigned int nCountMax)
{
int i;
size_t nbytes;
ssize_t bytes_read;
if (file != -1)
{
i = 0;
char message[100];
char data[100];
while (i<nCountMax && data != ".")
{
if (read(file, data, 1) == -1)
{
printf("reception error\n");
printf("code errno = %d\n", errno);
return false;
}
else
{
nbytes = sizeof(data);
bytes_read = read(file, data, nbytes);
message[i] = *data;
printf("%c", message[i]);
i++;
}
}
message[i] = 0;
return true;
}
}
This time there are no more errors, but the characters shown are wrong:
$$$$QUC
U$C
$$$$JQMJ' J$Cz(HSQ'Q'y
UKUNiQUMJ
the dollar signs $$$$ represent numbers in groups of four... I repeat that what I would like to have is
CHARS : 15931 SENTENCES = 0 CHECKSUM : 58
Heading : 55 Tilt: -46 Roll:2
CHARS : .....
I have tried using %c, %d, %x in the format string, but obviously none of them worked properly ...
Thank you!

In my code, I only change c_cflag, as follows:
// Enable the receiver and set local mode...
options.c_cflag |= (CLOCAL | CREAD);
// Set 8-bit mode
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
I'll post more code tomorrow, but give that a shot (and don't modify c_iflag).

Related

VTIME and VMIN don't seem to change the blocking behaviour of the posix read() call

I am trying to understand how VMIN and VTIME works to control the blocking behaviour of the read() posix call.
In my example, I have set VTIME to 10 (and have tried other combinations too) which should block the read for 1 second until it's unblocked, yes? That's my understanding but that doesn't seem to be the case.
I opened minimum on my host, and I see read() would only unblock as soon as I hit enter in minicom as opposed to waiting for 1 second to unblock.
Is my understanding incorrect? If not so, what could be wrong?
int Serial_Open(char *port)
{
int serial_port = open(port, O_RDWR);
struct termios tty;
// Read in existing settings, and handle any error
if(tcgetattr(serial_port, &tty) != 0)
{
printf("Error from tcgetattr: %s\n", strerror(errno));
}
tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common)
tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common)
tty.c_cflag &= ~CSIZE; // Clear all bits that set the data size
tty.c_cflag |= CS8; // 8 bits per byte (most common)
tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common)
tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1)
tty.c_lflag &= ~ICANON;
tty.c_lflag &= ~ECHO; // Disable echo
tty.c_lflag &= ~ECHOE; // Disable erasure
tty.c_lflag &= ~ECHONL; // Disable new-line echo
tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes
tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
tty.c_cc[VTIME] = 10; // Wait for up to 1s, returning as soon as any data is received.
tty.c_cc[VMIN] = 0;
cfsetspeed(&tty, B115200);
if (tcsetattr(serial_port, TCSANOW, &tty) != 0)
{
printf("Error tcsetattr %s\n", strerror(errno));
}
return serial_port;
}
int main(void)
{
char buffer[100];
int ret;
int fd = Serial_Open("/dev/ttyUSB4");
while(1)
{
ret = read(fd, buffer, sizeof(buffer));
if (ret <= 0)
{
printf ("No data or error\n");
}
else
{
printf ("Rxd data: %s\n", buffer);
}
}
return 1;
}
I cannot duplicate the claimed behaviour, using the following example program:
// SPDX-License-Identifier: CC0-1.0
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
static int tty_fd = -1;
static struct termios tty_old;
static void tty_close(void)
{
if (tty_fd == -1)
return;
tcsetattr(tty_fd, TCSAFLUSH, &tty_old);
if (tty_fd != STDIN_FILENO &&
tty_fd != STDOUT_FILENO &&
tty_fd != STDERR_FILENO)
close(tty_fd);
tty_fd = -1;
}
static int tty_open(const char *ttypath)
{
struct termios tty_new;
int fd;
if (tty_fd != -1)
tty_close();
if (!ttypath || !*ttypath)
return errno = ENOENT;
do {
fd = open(ttypath, O_RDWR | O_NOCTTY | O_CLOEXEC);
} while (fd == -1 && errno == EINTR);
if (fd == -1)
return errno;
if (!isatty(fd)) {
close(fd);
return errno = ENOTTY;
}
if (tcgetattr(fd, &tty_old) == -1) {
const int saved_errno = errno;
close(fd);
return errno = saved_errno;
}
tty_new = tty_old;
/* No input processing. No input flow control. Ignore parity. Break reads as '\0'. */
tty_new.c_iflag &= ~(IGNBRK | BRKINT | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IUCLC | IXON | IXANY | IXOFF);
tty_new.c_iflag |= IGNPAR;
/* No output processing. */
// tty_new.c_oflag &= ~(OPOST | OLCUC | ONLCR | OCRNL | ONOCR | ONLRET);
tty_new.c_oflag &= ~OPOST;
/* 8 data bits, No parity, 1 stop bit, no hardware flow control, ignore modem control lines. */
tty_new.c_cflag &= ~(CSIZE | PARENB | CSTOPB | CRTSCTS);
tty_new.c_cflag |= CS8 | CREAD | CLOCAL;
/* Raw mode, no signals, no echo. */
tty_new.c_lflag &= ~(ICANON | ISIG | ECHO | IEXTEN);
/* VMIN=0, VTIME=10 */
tty_new.c_cc[VMIN] = 0;
tty_new.c_cc[VTIME] = 10;
if (tcsetattr(fd, TCSANOW, &tty_new) == -1) {
const int saved_errno = errno;
close(fd);
return errno = saved_errno;
}
/* Some of the above settings may not have been applied.
We could check, but really, we don't care that much. */
tty_fd = fd;
return 0;
}
#ifndef TTY_BUFSIZ
#define TTY_BUFSIZ 128
#endif
static unsigned char tty_buf[TTY_BUFSIZ];
static volatile unsigned char *tty_head = tty_buf;
static volatile unsigned char *tty_tail = tty_buf;
#define TTY_NONE -1
#define TTY_EOF -2
#define TTY_ERROR -3
static int tty_getc_read(void)
{
tty_head = tty_tail = tty_buf;
if (tty_fd == -1)
return TTY_EOF;
ssize_t n = read(tty_fd, tty_buf, sizeof tty_buf);
if (n > 0) {
tty_tail = tty_buf + n;
return *(tty_head++);
} else
if (n == 0) {
return TTY_NONE;
} else
if (n != -1 || (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)) {
return TTY_ERROR;
} else {
return TTY_NONE;
}
}
static inline int tty_getc(void)
{
if (tty_tail > tty_head)
return *(tty_head++);
else
return tty_getc_read();
}
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
if (!done)
done = (signum > 0) ? signum : -1;
}
static int install_done(int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
int main(int argc, char *argv[])
{
if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *cmd = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", cmd);
fprintf(stderr, " %s TTY-DEVICE\n", cmd);
fprintf(stderr, "\n");
fprintf(stderr, "This reads in raw mode from the TTY device.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
if (install_done(SIGINT) || install_done(SIGTERM) ||
install_done(SIGHUP) || install_done(SIGQUIT)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_SUCCESS;
}
if (tty_open(argv[1])) {
fprintf(stderr, "%s: Cannot open TTY: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
printf("Press CTRL+C to exit.\r\n");
fflush(stdout);
int last_none = 0;
while (!done) {
int ch = tty_getc();
if (ch == TTY_NONE) {
printf(".");
fflush(stdout);
last_none = 1;
continue;
} else
if (last_none) {
printf("\r\n");
fflush(stdout);
last_none = 0;
}
if (ch == TTY_EOF) {
printf("End-of-input received.\r\n");
fflush(stdout);
break;
} else
if (ch == TTY_ERROR) {
printf("Read error occurred.\r\n");
fflush(stdout);
break;
} else
if (ch == 3) {
printf("Received 0x03 = \\003, assuming Ctrl+C. Exiting.\r\n");
fflush(stdout);
break;
} else {
if (ch >= 32 && ch <= 126)
printf("Received 0x%02x = \\%03o = %3d = '%c'\r\n", (unsigned int)ch, (unsigned int)ch, ch, ch);
else
printf("Received 0x%02x = \\%03o = %3d\r\n", (unsigned int)ch, (unsigned int)ch, ch);
fflush(stdout);
}
}
if (last_none) {
printf("\r\n");
fflush(stdout);
}
tty_close();
return EXIT_SUCCESS;
}
Specify the serial or terminal or pseudoterminal device path on the command line; use $(tty) for the current terminal/pseudoterminal.
Comparing OP's, Serial_Open() and my tty_open(), I do believe the terminal settings are essentially the same (and moreover, any differences there are do not explain the difference in behaviour).
You can compile the above example (example.c) as e.g. gcc -Wall -O2 example.c -o example and then run it via ./example $(tty) to read input from the same terminal window.
This leads me to believe the problem is in the other end: that whatever OP uses to generate the data read by this end, is line-buffered.

Connecting a program and Arduino with Serial-port

I'm using Ubuntu 16 (although i have the same problem on Debian) Arduino Mega 2560, 2 versions of Arduino IDE: 2:1.0.5+dfsg2-4.1 and 1.8.9.
I'm working on a connecting a C program with Arduino via COM-port.
For example, I have following code; a PC program sends some text to Arduino, while Arduino receives and prints it.
pc.c:
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
int openPort(char port_name[], speed_t speed)
{
int F_ID = open(port_name, O_RDWR | O_NOCTTY);
fcntl(F_ID, F_SETFL, 0);
//printf("%s\n", port_name);
if(F_ID == -1)
{
return -1;
}
struct termios options;
tcgetattr(F_ID, &options);
cfsetispeed(&options, speed);
cfsetospeed(&options, speed);
options.c_cc[VTIME] = 20;
options.c_cc[VMIN] = 0;
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_lflag = 0;
options.c_oflag &= ~OPOST;
tcsetattr(F_ID, TCSANOW, &options);
return F_ID;
}
void closePort(int id)
{
close(id);
return;
}
int sendData(int id, unsigned char* buf, int len)
{
int n = write(id, buf, len);
if(n == -1)
{
char *errmsg = strerror(errno);
printf("%s\n",errmsg);
}
return n;
}
int main(int argc, char * argv[])
{
int port=openPort("/dev/ttyUSB0", B9600);
if ( (port) == -1){
//char *errmsg = strerror(errno);
printf("Cannot open port: %s\n",strerror(errno));
return -1;
}
sendData(port, "Hello, Arduino!*", 16);
closePort(port);
return 0;
}
ard.ino:
int stage = 0;
String recv;
char* line;
char* get_line(void)
{ // '*' is a terminal character
if (Serial.available() > 0)
{
char c = Serial.read();
if (c=='*')
{
char* res = (char*)malloc((recv.length()+1)*sizeof(char));
recv.toCharArray(res, recv.length()+1);
stage=1;
recv="";
return res;
}
else { recv += c; }
} // if (Serial.available() > 0)
}
void setup()
{
Serial.begin(9600);
}
void loop()
{
if (stage==0)
{
line = get_line();
}
else
{
Serial.print("Got line: ");
Serial.println(line);
stage = 0;
}
}
To check, if my program works fine, I'm using Port Monitor in the IDE.
I have following problem:
I prefer to use Arduino IDE 1.8.9, but when I open Port Monitor and run my PC program - nothing works, PC-prog can't access the port and send information (it says Bad file descriptor);
while with Arduino 2:1.0.5+dfsg2-4.1 everything works fine and i can see on Port Monitor:
Got line: Hello, Arduino!
So I'm asking, what I have to do with Arduino IDE 1.8.9 parameters or with /dev/ttyUSB* for it to work fine with each other?

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);
}

Epoll reads wrong bytes

I'm newbie in linux system programming, so be kind please.
I have to read data from serial port (/dev/ttyX) with baudrate 921600 in 8N1 mode with no parity via RS-422.
stty output signals that this baudrate is supported.
So I've decided to call epoll.
Problem is that epoll returns wrong bytes.
Since I have specified messages format I'm trying to debug by verifying incoming data by hands. So I've drawn this:
All messages have 2 bytes crc on the tail.
11 b5 means message start. Message should be 36 bytes in length.
72 b5 is another message start marker. 112 bytes len.
73 b5 is message marker too. 36 bytes.
Please find blue underline: this is good message.
Tiny red + fat red is a bad one.
It is 37 bytes len. I have one extra byte and crc mismatch.
Next good one goes (green).
And next bad one. It is 114 bytes instead of 112 bytes and crc mismatch of course.
Here is my code:
... All necessary includes
#define SERIAL_BAUD 921600
#define MAXEVENTS 1024
int openSerial()
{
struct termios options;
int fd;
if ((fd = open("/dev/ttyUSB0", O_RDWR)) == -1)
{
return -1;
}
if (tcgetattr(fd, &options) < 0)
{
printf("Unable to get options with tcgetattr\n");
return -1;
}
if (cfsetispeed(&options, SERIAL_BAUD) < 0)
{
printf("Unable to set input speed with cfsetispeed\n");
return -1;
}
if (cfsetospeed(&options, SERIAL_BAUD) < 0)
{
printf("Unable to set output speed with cfsetispeed\n");
return -1;
}
cfmakeraw(&options);
//options.c_cflag |= SERIAL_BAUD; // Set Baudrate first time
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~CRTSCTS;
options.c_cflag &= ~ECHO; // Disable echoing of input characters
options.c_cflag &= ~ECHOE;
// set to 8N1
options.c_cflag &= ~PARENB; // no parity
options.c_cflag &= ~CSTOPB; // 1 stop bit
options.c_cflag &= ~CSIZE; // Mask the character size bits
options.c_cflag |= CS8; // 8 data bits
options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
options.c_oflag = 0;
options.c_cc[VTIME] = 2; // inter-character timer
options.c_cc[VMIN] = 1; // blocking read until
if (tcflush(fd, TCIFLUSH) < 0)
{
printf("Unable to flush fd with tcflush\n");
return -1;
}
if (tcsetattr(fd, TCSANOW, &options) != 0)
{
printf("Unable to set options with tcsetattr\n");
return -1;
}
return fd;
}
int main(void)
{
int fd;
int efd;
struct epoll_event event;
struct epoll_event* events;
int length;
unsigned char buff[512];
if ((fd = openSerial()) < 0)
{
printf("Exiting because of openSerial failure\n");
return 1;
}
efd = epoll_create1(0);
event.data.fd = fd;
event.events = EPOLLIN;
if (epoll_ctl(efd, EPOLL_CTL_ADD, fd, &event) < 0)
{
printf("Epoll_ctl error occured\n");
return 1;
}
events = (epoll_event*) calloc(MAXEVENTS, sizeof(event));
for(;;)
{
int n = epoll_wait(efd, events, MAXEVENTS, 5000);
if (n < 0)
{
// No ready descriptors, so wait a bit longer
continue;
}
if(events[0].events & EPOLLIN)
{
length = read(events[0].data.fd, buff, sizeof(buff) / 2);
if(length > 0)
{
printf("\n------MESSAGE START-------\n");
for (int i = 0 ; i < length ; ++i)
{
if (i && i % 16 == 0)
{
printf("\n");
}
printf("%02x ", buff[i]);
}
printf("\n------MESSAGE FINISH-------\n");
}
}
else if(events[0].events & EPOLLOUT)
{
// TODO Write here to serial
}
else if(events[0].events & EPOLLHUP || events[0].events & EPOLLERR)
{
printf("Error occured on serial port\n");
}
else
{
printf("No data whthin 5 seconds.\n");
}
}
free(events);
close(fd);
return 0;
}
Your problem is that read() returns bytes that fail to meet your expectations. I see no reason to believe that epoll has anything to do with that.
I also see no reason to suppose that read() is delivering different bytes than the system received from device /dev/ttyUSB0. If these are different than you expected then I'm inclined to believe that either the device is malfunctioning or your expectations are incorrect (or at least incomplete).

Linux - reading from serial port error in C (Resource temporarily unavaliable)

I am trying to read some data from serial port using usb/rs232 converter. I am sending data from atmega and it is received properly in minicom. But trying to read sth in my program ends up with an error "Resource temporarily unavailable". I know that this is caused by O_NDELAY, but removing that gives me plenty of empty messages which also isn't good.
Actually, what I want to achieve is a program, which every second will transmit some char to atmega and wait for a response. Depending on a response it will do different actions. Also after few unanswered transmissions program will indicate an error with communication.
But for now I'd like to at least receive something properly.
Here is my code:
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
int fd; //file descriptor
int bytes; // bytes to read
int PortOpen(void)
{
fd = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY | O_NDELAY);
if(fd < 0)
{
//opening error
printf("Port opening error\r\n");
}
else
{
printf("Port opened\r\n");
//port config
struct termios options;
tcgetattr(fd, &options);
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cflag |= CLOCAL;
options.c_cflag |= CREAD;
options.c_cflag &= ~CRTSCTS;
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 5;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
//Modifying c_iflag by bitwise OR and AND
options.c_iflag &= ~(ICRNL | INLCR | IGNCR | IUCLC);
options.c_iflag &= ~(IXON | IXOFF | IXANY);
//Modifying c_oflag by bitwise OR and AND
options.c_oflag &= ~OPOST;
tcflush(fd, TCIFLUSH);
//Setting the new settings for the port immediately
tcsetattr(fd, TCSANOW, &options);
}
return 1;
}
int PortRead(void)
{
int n = 0, spot = 0;
char buf = '\0';
/* Whole response*/
char response[1024];
memset(response, '\0', sizeof response);
do {
n = read( fd, &buf, 1 );
sprintf( &response[spot], "%c", buf );
spot += n;
} while( buf != '\r' && n > 0);
if (n < 0)
{
printf("Error reading: %s\r\n",strerror(errno));
}
else if (n == 0)
{
printf("Read nothing!\r\n");
}
else
{
printf("Response: %s", response);
}
return 1;
}
int main(void)
{
PortOpen();
int j;
for(j=0; j<100; j++) //just testing
{
PortRead();
}
return 0;
}

Resources