Linux C++ reading UART device not working consistently - c++

I am using a BeagleBone Black to read data coming from a microcontroller(s) via a UART port(s). I need the reading of the UART port to be a blocking call. Additionally, for the usage of this software there will be some non-standard baud rates in use (aka not provided by termios). Additionally, the UART should follow 8-N-1 (8 data bits, no parity, 1 stop bit).
The code I have for the opening the UART port is as follows:
int UART::UART_open(unsigned int baudRate)
{
mFd = open(mPath.c_str(), O_RDWR | O_NOCTTY);
if(mFd < 0)
{
return -1;
}
struct termios2 tty;
if(ioctl(mFd, TCGETS2, &tty) == -1)
{
return -1;
}
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 5;
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
tty.c_cflag &= ~CBAUD;
tty.c_cflag |= (BOTHER | CREAD | CLOCAL);
tty.c_ispeed = baudRate;
tty.c_ospeed = baudRate;
if(ioctl(mFd, TCSETS2, &tty) == -1)
{
return -1;
}
return 0;
}
The code I have for reading the UART port is as follows:
int UART::UART_read(unsigned char* buf, int length)
{
if(mFd < 0)
{
return -1;
}
if(read(mFd, buf, length) != length)
{
return -1;
}
return length;
}
There is some odd behavior going on. What happens is, the reading is inconsistent. Sometimes when I test it with an Mbed microcontroller sending data continuously (with small delays in between) via UART to the right port, and a test program to continuously read the UART port on the BeagleBone Black, and print out the data it gets, it works fine, and I am able to print out the data sent and everything works as expected. However, what happens often is the very first read simply blocks forever. No errors occur from the functions, the UART_read function simply hangs. So, to debug the error the first thing I do is I use 'screen' to monitor the /dev/ttyO* port I am trying to read from. What I find is that data is being sent to that port just fine. Then, the odd thing is, after I use screen, if I run my test program to continuously read the UART port, it works fine. This happens consistently too, if I do a quick 'screen' of the port when it is not working, I see data being sent, then my test program works. I have tried changing some of the opening termios2 struct options, to no avail. Any help would be appreciated!

Your termios initialization is obviously incomplete. There's a partial configuration for raw mode, e.g.
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 5;
yet you never actually enable noncanonical mode.
The "blocks forever" behavior of read() is symptomatic of a serial terminal (mis)configured for canonical mode when only binary data is received (i.e. there is no line terminator to "complete" the request).
Another possible cause of "blocks forever" behavior is failure to disable hardware flow-control when it is not used.
The insertion of the following statements would enable noncanonical mode and disable the HW handshake:
cfmakeraw(&tty);
tty.c_cflag &= ~CRTSCTS;
Refer to this answer for a complete example.
if(read(mFd, buf, length) != length)
{
return -1;
}
There is no requirement that a read() of a serial terminal will fill the buffer. Depending on the configuration, a "successful" read can return zero or more bytes up to the number requested.
Therefore a short read is not actually an error condition.

Related

linux pseudo terminal (open select read)

I have a following scenario: Someone creates a pseudo terminal via opening /dev/ptmx. New terminal is created and named for example /dev/pts/2. Then, in my program I open /dev/pts/2 for reading. But I also open other devices for reading and use select() function to wait for any incoming data. The select have also some timeout specified for performing other stuff when no data arrives for too long. After successful select i read data using read() function and then print it on the screen.
I encountered an issue if the pseudo terminal is closed by the one who created it. In this case select function ends immediately indicating success as well as read ends indicating "no data" by returning zero. The issue imho is that neither select nor read returns error in such case. How should I handle this to detect that the terminal is no longer existing?
Status processData()
{
fd_set readFileDescriptorSet; // defined somewhere else
int maxFileDescriptor; // defined somewhere else
struct timeval timeout; // defined somewhere else
int ret = select(maxFileDescriptor + 1, &readFileDescriptorSet, nullptr, nullptr, &timeout);
if (!ret) // timeout
return Status::success();
if (ret < 0) // error from select()
return Status::error("select error");
ssize_t rd;
char buff[10];
do {
rd = read(interfaces.serialPort.getFileDescriptor(), buff, sizeof(buff) - 1);
if (rd > 0) { // some data has been read
buff[rd] = '\0';
std::cout << buff;
}
} while (rd > 0);
if (rd < 0) // error from read()
return Status::error("read error");
return Status::success();
}
While the way I open the pseudo terminal is following:
Status internalOpen(std::string fileName)
{
close();
fileDescriptor = ::open(fileName.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fileDescriptor == -1)
return Status::error("Terminal::internalOpen::open('" + fileName + "')");
struct termios attributes;
if (tcgetattr(fileDescriptor, &attributes))
return Status::error("Terminal::internalOpen::tcgetattr()");
setAttributes(attributes);
if (tcsetattr(fileDescriptor, TCSANOW, &attributes))
return Status::error("Terminal::internalOpen::tcsetattr()");
return Status::success();
}
void setAttributes(struct termios &attributes)
{
cfmakeraw(&attributes);
cfsetspeed(&attributes, Config::baudRate);
attributes.c_iflag &= ~(IXOFF | IXANY);
attributes.c_oflag &= ~(ONLCR);
attributes.c_lflag &= ~(ECHOE);
attributes.c_cflag &= ~(CSTOPB | CRTSCTS);
attributes.c_cflag |= CREAD | CLOCAL;
attributes.c_cc[VMIN] = 0;
attributes.c_cc[VTIME] = 0;
}
After select() returns indicating that there's something to be read, the shown code loops repeatedly trying to read() from the non-blocking file descriptor until it is 0:
do {
rd = read( ...
} while (rd > 0);
That's certainly reasonable. Except that the closed connection results in the very first read() returning 0, which the shown logic cannot discriminate.
All that's really needed here is to keep track of whether anything has been read, prior read() returning 0. But if read() returned 0 right off the bat, your goose is cooked.
Additionally, there a few other improvements will make things more robust.
After select() returns, actually check if the file descriptor's bit remains set in the readFileDescriptorSet. The shown logic simply assumes that it is, by checking for all other possibilities. Still, this is somewhat fragile. It's easy to forget this assumption if something tangentially related gets modified (i.e., another fle descriptor gets thrown into the mix).
Use poll() instead of select(), and explicitly check for POLLHUP|POLLRDHUP in revents. The file descriptor closure condition is more explicitly called out, in the poll() interface.

How to receive complete data from serial port in linux

I am trying to do a serial communication application. I have a device which is running angstrom qt linux image. I need to write the serial code application for that device. Because the cross compiler is set for QT4.8.7, so it do not include the QSerialPort. So I am following this link for serial communication and I have also found many good examples on it.
Below is the code:
void MainWindow::SerialOpen(QString PORT)
{
fd = open(PORT.toStdString().c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
perror("open_port: Unable to open /dev/ttyLP0\n");
exit(1);
}
saio.sa_handler = signal_handler_IO;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction(SIGIO,&saio,NULL);
fcntl(fd, F_SETFL, FNDELAY);
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, O_ASYNC );
tcgetattr(fd,&termAttr);
cfsetispeed(&termAttr,B9600);
cfsetospeed(&termAttr,B9600);
termAttr.c_cflag &= ~PARENB;
termAttr.c_cflag &= ~CSTOPB;
termAttr.c_cflag &= ~CSIZE;
termAttr.c_cflag |= CS8;
termAttr.c_cflag |= (CLOCAL | CREAD);
termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
termAttr.c_iflag &= ~(IXON | IXOFF | IXANY);
termAttr.c_oflag &= ~OPOST;
tcsetattr(fd,TCSANOW,&termAttr);
qDebug("Serial Port configured....\n");
}
and for reading I am using:
void signal_handler_IO (int status)
{
char buf [100];
int n = read (fd, buf, sizeof buf);
if(n > 0)
{
buf[n] = '\0';
printf("Receive OK %s\n",buf);
}
}
I am using event based serial read. I am wiriting it in QT creator. The problem I am facing is that whenever I am sending characters like A or B or any other single char. It receives it. But when I send the complete string, it breaks automatically. Have a look at the below image:
As you can see in the image, it receives char like p l h but then I sent hello world. It breaks it and first received hello and then world. I again sent the hello world it receives the he then llo then worl and then d. Why is this showing this behaviour. Please help. Thanks.
You are receiving your data in small chunks. Try running the read function in a while loop to append all chunks into one message like so:
#include <cstring> // needed for strcat.
#include <errno.h> // needed for strerror and errno
void signal_handler_IO (int status)
{
char buf [100];
char msg[1024];
int n;
int length = 0;
while((n = read (fd, buf, (sizeof buf)-1)) > 0)
{
buf[n] = '\0';
// you can add a check here to see if (length+n) < 1024
strcat(msg, buf);
length += n;
}
if(n<0)
{
//error handling
//EDIT: adding error handling
fprintf(stderr, "Error while receiving message: %s\n", strerror(errno));
return;
}
printf("Receive OK: '%s'\n", msg);
}
The signal handling needs to be turned off for the time of reading data so that the signal handler doesn't get called for each new chunk of data. After we're done reading the handler needs to be restored.
EDIT: Thanks to #MarkPlotnick for giving a better example on masking the signal instead of turning it off (could cause race condition). Add these two lines before calling sigaction to MainWindow::SerialOpen to mask the signal so that the handler doesn't get called again while the signal is being handled:
sigemptyset(&saio.sa_mask);
sigaddset(&saio.sa_mask, SIGIO);
The size of msg is just an example, you can set it to any value that will fit the message. Also, if you're using c++ then it would be better to use std::string instead of an array for msg.
Note the (sizeof buf)-1, if you read all characters there would be no room for the '\0' at the end of the array.
ANOTHER EDIT: After our chat conversation it turned out that read is blocking when there are no more characters to read (even though open was called with O_NDELAY flag for non-blocking reading). To fix this issue one can first check how many bytes are available for reading:
while(true)
{
size_t bytes_avail;
ioctl(fd, FIONREAD, &bytes_avail);
if(bytes_avail == 0)
break;
int n = read (fd, buf, (sizeof buf)-1);
if(n<0)
{
//error handling
fprintf(stderr, "Error while receiving message: %s\n", strerror(errno));
return;
}
buf[n] = '\0';
// you can add a check here to see if (length+n) < 1024
strcat(msg, buf);
length += n;
}
QT4.8.7, so it do not include the QSerialPort
You can build QSerialPort from sources and to use it. Please read Wiki: https://wiki.qt.io/Qt_Serial_Port

Linux blocking I/O doesn't work as expected

So I have a development board which has some UART peripherals muxed to some tty*-like files.
I have connected some other device on one port (RX pin), and I expect to be able to read the data that it was sent to me.
The devices sends me data in 11-bytes chunks.
This is the code I have been used:
if(-1 == (fd_read = open(port.c_str(), O_RDONLY))) {
throw std::ios_base::failure("Unable to open UART descriptor.");
}
tcgetattr(fd_read, &settings);
/*!
* Set some default settings.
*/
cfsetospeed(&settings, baud); /* baud rate */
settings.c_cflag &= ~PARENB; /* no parity */
settings.c_cflag &= ~CSTOPB; /* 1 stop bit */
settings.c_cflag &= ~CSIZE;
settings.c_cflag |= CS8 | CLOCAL; /* 8 bits */
settings.c_lflag = ICANON; /* canonical mode */
settings.c_oflag &= ~OPOST; /* raw output */
settings.c_cc[VMIN] = 0;
settings.c_cc[VTIME] = 0;
/*!
* Apply the settings for both: the writing and reading file descriptors.
*/
tcsetattr(fd_read, TCSANOW, &settings);
tcflush(fd_read, TCOFLUSH);
The problem is that the other devices sends me a data chunk (I can see it on scope), but the read() function doesn't return.
This is the read function call:
auto bytesReceived = ::read(fd_read, UARTDataBuffer, max_incoming_uart_data_length); // "max_incoming_uart_data_length" is 2048.
The strange thing is that when I use another TX pin from my board, and send the exact the same data (as I suppose to receive from the another device), everything works as expected, the read() function returns with the correct data.
Why is this happening?
L.E. More details about the problem:
Both, the development board (BeagleBone Black) and the other sensor are using 3.3V for UART signals.
After configuring the communication with the code shown above, I also applied the following command on the associated tty*-like file:
stty -F /dev/ttyO2 921600
Where /dev/ttyO2 is my UART-associated port, and 921600 is the baud rate value.
I use this because termios structure don't provide such higher baud values.
I'm expecting that stty won't change the other settings (like parity, start/stop bits, etc), it will only set the baud rate.
Update2: When I open the same port (where I'm expecting data) using minicom, everything works as expected, I'm able to receive data in my application. (and the minicom also captures the data).

read() not returning Specified amount of Bytes

I am reading the Sensor data (broadcasted on RS232) with read() function. The data rate is 264 Bytes / Sec.
I am using the following code to read the data (I just needed to read 60 Bytes).
int BytesToRead = 60;
unsigned char* iBuffer = new unsigned char[BytesToRead];
int ret = read(COM, iBuffer, BytesToRead);
cout<<ret<<endl;
if (ret == 0) {
cout<<"Error Reading COM Port"<<endl;
exit(EXIT_FAILURE); // Error Handling
}
delete[] iBufer;
And this program is returning random bytes (~30).
I am a beginner for C++ programming. Sorry, If I am doing some stupid mistake.
Thanks.
My COM function:
int Connect(const char *DeviceName){
long BAUD = B115200;
long DATABITS = CS8; // CS8 = 8n1 Config.(8 bits, No parity, 1 Stop Bit)
long VMIN_CC = 1; // 1 input byte is enough to return from read()
long VTIME_CC = 0; // Inter-character timer
long STOPBITS = 0; // Defined with CS8
long PARITYON = 0; // NONE (Ref.: IH2 Azzura Hand User Guide)
long PARITY = 0; // NONE (Ref.: IH2 Azzura Hand User Guide)
struct termios config; // Configuration of the termios structure
fd_set rdset; // File discription set
//Basic serial interface configuration
//iflag = Input flag || oflag = Output flag || lflag = No-line processing flag
//c_cflag = Caracter processing flag || c_cc = Special character flag
memset(&config,0,sizeof(config));
config.c_iflag = 0; // Turning OFF Input processing
config.c_oflag = 0; // Turning OFF Output processing
config.c_lflag = 0;
config.c_cflag = DATABITS |CREAD|CLOCAL;// Enable the receiver and set local mode
config.c_cc[VMIN] = VMIN_CC;
config.c_cc[VTIME] = VTIME_CC;
//Opening the Port for communication
int com = open(DeviceName, O_RDWR | O_NOCTTY);
//Error Handling
if (com < 0) {
cout<<"ERROR!! Opening Port \n"<<"Sys:"<<strerror(errno)<<endl;
exit(EXIT_FAILURE);
} else {cout<<"Serial Communication (Opening Port): "<<strerror(errno)<<endl;}
//Setting the BaudRate for Communcation
cfsetispeed(&config, BAUD);
cfsetospeed(&config, BAUD);
//Applying Configuration / Attributes
int Attr = tcsetattr(com, TCSANOW, &config);
//Error Handling
if(Attr < 0){
cout<<"ERROR!! Setting Attributes \n"<<"Sys:"<< strerror(errno)<<endl;
exit(EXIT_FAILURE);
} else {cout<<"Serial Communication (Setting Attributes): "<<strerror(errno)<<endl;}
return(com);
}
I am assuming this is a Linux system....
Your BytesToRead variable is only a suggestion to read() in that read() will try to read up to BytesToRead. If read() returns less than your requested amount, then fewer bytes were available to be read.
From the linux manpage on read
... 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...
It is possible that your sensor is not sending the data you expect, or you are not giving it enough time to transfer all of the data, or there is some other logic/hardware problem that is not apparent from your code example.
read() is not guaranteed to return the number of bytes requested:
From the read(2) Linux manpage:
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. On error, -1 is returned, and errno is set appropriately. In this case it is left unspecified whether the file position (if any) changes.

Why is serial port skipping data when sending data?

I have written some C++ code to talk to my arduino via serial. It just tries to make oscillations on two servo motors using sine and cosine, but it is skipping data. I'm not sure why this is happening. I am using the termios.h for the serial stuff. The output from C++ is something like "V180H90" i.e. Vertical 180, Horizontal 90. I was using fstream and usleep() to send data before and it was working, but I'd like to use a better method than delaying by some arbitrary number.
Thanks for any help or guidance.
My arduino code
#include <Servo.h>
typedef enum { NONE, GOT_V, GOT_H } states;
states state = NONE;
Servo pan;
Servo tilt;
int laser = 11;
unsigned int currentValue;
int v_pan = 0;
int v_tilt = 0;
void setup()
{
pan.attach(10);
tilt.attach(9);
Serial.begin(9600);
state = NONE;
}
void processVertical(const unsigned int value)
{
Serial.print("Vertical = ");
Serial.println(value);
int result = 1300 + (value - 90) * 2;
//Serial.println(result);
tilt.writeMicroseconds(result);
}
void processHorizontal(const unsigned int value)
{
Serial.print("Horizontal = ");
Serial.println(value);
int result = 1500 + (value - 180) * 1;
//Serial.println(result);
pan.writeMicroseconds(result);
}
void handlePreviousState()
{
switch(state)
{
case GOT_V:
processVertical(currentValue);
break;
case GOT_H:
processHorizontal(currentValue);
break;
}
currentValue = 0;
}
void processIncomingByte (const byte c)
{
if (isdigit(c))
{
currentValue *=10;
currentValue += c - '0';
}
else
{
handlePreviousState();
switch (c)
{
case 'V':
state = GOT_V;
break;
case 'H':
state = GOT_H;
break;
default:
state = NONE;
break;
}
}
}
void loop()
{
if(Serial.available() > 0)
{
processIncomingByte(Serial.read());
}
digitalWrite(laser, HIGH);
}
//check out writeMicroseconds
My C++ code
// Program for sending data to serial
#include <iostream>
#include <sstream>
#include <string>
#include <termios.h>
#include <fcntl.h>
#include <math.h>
using namespace std;
//open serial port
int openPort(string path)
{
int fd; //file descriptor for port
fd = open(path.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
cerr << "Cannot open port" << endl;
else
fcntl(fd, F_SETFL, 0);
return (fd);
}
//set options for an open serial port
void setOptions(int fd)
{
struct termios options;
tcgetattr(fd, &options);
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
//No parity 8N1
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
//No flow control
options.c_cflag &= ~CRTSCTS;
//Turn off s/w flow control
options.c_iflag &= ~(IXON | IXOFF | IXANY);
//Turn on read and ignore ctrl lines
options.c_cflag |= (CLOCAL | CREAD);
if( tcsetattr(fd, TCSANOW, &options) < 0) {
cerr << "Could not set attributes" << endl;
}
}
//write to serial port
void writePort(int fd, string data)
{
int n = write(fd, data.c_str(), 9);
if (n < 0)
cerr << "Cannot write to port" << endl;
}
int main() {
string path = "/dev/tty.usbmodemfd131";
//string path = "/dev/tty.usbmodemfa141";
int fd = openPort(path);
setOptions(fd);
stringstream ss;
string output;
unsigned short vertical = 0;
unsigned short horizontal = 0;
unsigned short freq = 10;
for(int i = 0; i < 360; i++) {
vertical = ((cos(i * freq * ((M_PI)/180))) + 1) * 90;
horizontal = ((sin(i * freq * ((M_PI)/180))) + 1) * 90;
ss << "V" << vertical << "H" << horizontal << endl;
output = ss.str();
ss.str("");
writePort(fd, output);
// cout << output; //DEBUG
}
close(fd);
return 0;
}
The "processIncomingByte" loop inside the device may have suffered a speed problem as you are processing the previous state (handlePreviousState) immediately after you receive a new mode.
The problem may be caused by doing a Serial.print in the corresponding function while the value-data bytes are still incoming continuously from the PC. Serial print is a relatively slow process in micro-controller logic.
I am not familiar with Arduino hardware, but some lower end micro-controller board is performing software serial interface using bitbanging method, so when you transmit, the receiving is completely stopped. To verify this you can remark the Serial.print to see whether it helps.
Anyway, doing lengthy processing in the middle of incoming data stream is alway problematic, unless you have a hardware serial interface in the device with lots of FIFO buffers.
A proper way to this problem is to receive the whole message inside a buffer first and then process it only when a end-of-message marker is received. For example, insert your message inside the [] pair like [V180H90]. Reset the buffer upon the "[" and process the buffer after you receive the "]". When you are collecting bytes into the buffer, make sure you also check for buffer overflow.
If you just shove data down the port's throat, it'll do its best not to set on fire, but the excess data isn't going to be sent. After all, the port operates at a finite speed and is a pretty limited and dump device.
So, before sending a character to the port you need to check the status of the port to see if it's actually ready to accept another character of data for transmission. Some serial ports can even generate interrupts when they can take more data to help you avoid wasteful status polling.
Also, sometimes two serial ports on the two devices can be connected with an extra pair of non-data signals (RTS and CTS) to indicate whether the receiving side is ready to receive more data. If you have those connected and your device is using them to indicate its readiness, your program should take the state of the device's CTS into account as well.
Clearly your device reads/process data slower than you send it via serial port. I see few possible solutions here:
1) Implement flow control and send data via serial port in blocking mode. You still have to wait after sending, but only as much as it is needed for your device to read and process data.
2) Implement two way communication so your device sends confirmation message (i.e. any single ASCII symbol) to indicate that it is ready to accept data.
3) Divide your code into two parallel parts i.e. : main loop (or an ISR) only reads data from serial port and stores it in a ring buffer, another loop polls the ring buffer and takes/process data from it as soon as there is some data available. This is the most difficult solution of the three as you need two separate threads (or a thread and an ISR) and protect ring buffer from concurrent access, but also the most powerful and flexible.
You are writing data out too quickly to the serial device and the device itself is spitting out data faster than you can read it back in on the other side of the device.
The correct way to cope with this is to throttle the speed of writes to the serial device to avoid flooding it with data.