I am trying to write a function that reads a chunk of data sent through UART. I am using Raspbian Jessie running on a RaspberryPi model B but I wanted to use this C code (with any necessary revisions) on openwrt. So far, this is what I wrote.
Header:
#ifndef __UART_LIB__
#define __UART_LIB__
#include <stdlib.h> //Errors, etc
#include <unistd.h> //Used for UART
#include <fcntl.h> //Used for UART
#include <termios.h> //Used for UART
#include <sys/types.h> //These includes are for timeout
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h> //
#include <sys/ioctl.h>
#define BITS_PER_PACKAGE_ 11
#define WAIT_PROLONGATION_CONSTANT_ 1.1f
//Some values used by default, left for the user to change if needed
unsigned int BAUD_ ;
unsigned int NUM_BITS_ ;
char *UART_PATH_ ;
unsigned int MAX_SIZE_ ;
unsigned int OPEN_FLAG_ ;
time_t TIMEOUT_SEC_ ;
suseconds_t TIMEOUT_USEC_ ;
struct timeval WAIT_CONSTANT_ ;
int open_conf_UART_() ;
int read_UART_(int uart_filestream, char** dest, int max_len) ;
#endif
.c file:
#include "uartlib.h"
unsigned int BAUD_ = B115200 ;
unsigned int NUM_BITS_ = CS8 ;
char *UART_PATH_ = "/dev/ttyAMA0" ;
unsigned int MAX_SIZE_ = 128 ;
unsigned int OPEN_FLAG_ = O_RDWR ;
time_t TIMEOUT_SEC_ = 5 ;
suseconds_t TIMEOUT_USEC_ = 0 ;
int open_conf_UART_()
{
int indicator, old_fl;
int uart_filestream ;
struct termios options ;
// Opening the port in a read/write mode
uart_filestream = open(UART_PATH_, OPEN_FLAG_ | O_NOCTTY );
if (uart_filestream < 0)
{
// Unable to open the serial port, so produce an error and halt
return -1;
}
// Configuring the options for UART
// Retrieve the options and modify them.
indicator = tcgetattr(uart_filestream, &options);
if(indicator < 0)
{
// Unable to get the attributes
close(uart_filestream);
return -1;
}
// I found a question on stackoverlow where the answer said that VTIME and VMIN will be ignored unless I
// switch the FNDELAY flag off
old_fl = fcntl(uart_filestream, F_GETFL);
if(old_fl < 0)
{
return -1;
}
old_fl &= ~FNDELAY;
fcntl(uart_filestream, old_fl);
//Setting the options
options.c_cflag = CRTSCTS | BAUD_ | NUM_BITS_ | CLOCAL | CREAD ;
options.c_iflag = 0;
options.c_oflag = 0;
options.c_lflag = 0;
//I want the uart to wait 1/10 of a second between bytes at most
options.c_cc[VTIME] = 1;
options.c_cc[VMIN] = 0;
// Flushing the file stream (the input and the output area)
indicator = tcflush(uart_filestream, TCIOFLUSH);
if(indicator < 0)
{
// Unable to flush
close(uart_filestream);
return -1;
}
// Setting the options for the file stream.
indicator = tcsetattr(uart_filestream, TCSANOW, &options);
if(indicator < 0)
{
// Unable to set the attributes
close(uart_filestream);
return -1;
}
return uart_filestream;
}
int read_UART_(int uart_filestream, char** dest, int max_len)
{
int indicator;
int buffer_length;
indicator = tcflush(uart_filestream, TCIFLUSH);
if(indicator < 0)
{
// Unable to flush
return -1;
}
//Do the actual reading
buffer_length = read(uart_filestream, (void*)(*dest), max_len);
if(indicator < 0)
{
return -1;
}
else
{
// Returning number of read bytes
return buffer_length;
}
// Both branches of the if statement above have return, so this will not be reached
}
So, when I try to read more than 8 bytes, the message gets truncated to 8 bytes. As I read, setting VTIME to a certain value allows the time interval between two bytes to be at most that long. I am not certain what is going on but I suspect that the read() call reads the buffer before the receiving of the data is complete. My wish is to read a chunk of data of undefined size. I also used select() with a timeout before the read to make sure the program won't block entirely. I read a lot of forum topics, stackoverflow questions, guides, etc. on this topic and none seem to help me with my solution. So, can anyone explain what is going on here? Is it possible to do what I want?
Note that I removed some of the code (I also wrote a function that writes to a UART port) so here might be some redundant includes, global variables, etc.
So, I solved my problem. I still can't flush (when the system is freshly booted up and there was a signal before I turned my program on, there is some old content in the buffer, as it seems to me). I used this assumption: The package will arrive in smaller bursts and they will be separated by TIMEOUT_BYTE_ amount of time. If that expires, I assume that the package is over. Also, I have a timeout for initial waiting for the data but I reckon that is situational.
Header file:
#ifndef UART_LIB_
#define UART_LIB_
#include <stdlib.h> //Errors, etc
#include <unistd.h> //Used for UART
#include <fcntl.h> //Used for UART
#include <termios.h> //Used for UART
#include <sys/types.h> //These includes are for timeout
#include <sys/select.h> //Used for select(), etc
//Some values used by default, left for the user to change if needed
//Used to set up the baud rate
unsigned int BAUD_ ;
//Used to indicate number of bits in one backage
unsigned int NUM_BITS_ ;
//Path to the UART device
char *UART_PATH_ ;
//Flag for opening the device
unsigned int OPEN_FLAG_ ;
//Timeout for answer from the other side
time_t TIMEOUT_SEC_ ;
suseconds_t TIMEOUT_USEC_ ;
//Time interval between two bursts of data inside the package
suseconds_t TIMEOUT_BYTE_ ;
int open_conf_UART_(void) ;
int read_UART_(int uart_filestream, char* dest, int max_len) ;
#endif
Source file:
#include <errno.h>
#include "uartlib.h"
unsigned int BAUD_ = B38400 ;
unsigned int NUM_BITS_ = CS8 ;
char *UART_PATH_ = "/dev/ttyAMA0" ;
unsigned int OPEN_FLAG_ = O_RDWR ;
time_t TIMEOUT_SEC_ = 2 ;
suseconds_t TIMEOUT_USEC_ = 0 ;
// This needs to be finely tuned
suseconds_t TIMEOUT_BYTE_ = 5000;
int open_conf_UART_()
{
// Variable section
int indicator;
int uart_filestream ;
struct termios options ;
// Opening the port in a read/write mode
uart_filestream = open(UART_PATH_, OPEN_FLAG_ | O_NOCTTY | O_NONBLOCK);
if (uart_filestream < 0)
{
// Unable to open the serial port, so produce an error and halt
return -1;
}
// Configuring the options for UART
// Flushing the file stream (the input and the output area)
indicator = tcflush(uart_filestream, TCIOFLUSH);
if(indicator < 0)
{
// Unable to flush
close(uart_filestream);
return -1;
}
// Retrieve the options and modify them.
indicator = tcgetattr(uart_filestream, &options);
if(indicator < 0)
{
// Unable to get the attributes
close(uart_filestream);
return -1;
}
// Setting the options
cfmakeraw(&options);
options.c_cflag |= BAUD_ | NUM_BITS_ | CREAD;
// Setting the options for the file stream.
indicator = tcsetattr(uart_filestream, TCSANOW, &options);
if(indicator < 0)
{
// Unable to set the attributes
close(uart_filestream);
return -1;
}
return uart_filestream;
}
int read_UART_(int uart_filestream, char* dest, int max_len)
{
// Variable section
int indicator;
int buffer_length;
char *tmp_dest;
fd_set set;
struct timeval timeout, init_timeout;
while(1)
{
// Reseting the set and inserting the uart_filestream in it
FD_ZERO(&set);
FD_SET(uart_filestream, &set);
// Setting the time for initial contact
init_timeout.tv_sec = TIMEOUT_SEC_ ;
init_timeout.tv_usec = TIMEOUT_USEC_ ;
// Waiting for the first contact. If this times out, we assume no contact.
indicator = select(uart_filestream + 1, &set, NULL, NULL, &init_timeout);
if(indicator < 0)
{
if(errno == EINTR)
{
// Try again
continue;
}
return -1;
}
else if(indicator == 0)
{ // Timeout has occurred
return -2;
}
else
{
break;
}
}
// This section means that there is something to be read in the file descriptor
buffer_length = 0 ;
tmp_dest = dest ;
// The first select is redundant but it is easier to loop this way.
while(buffer_length < max_len)
{
// select changes the timeval structure so it is reset here
timeout.tv_sec = 0;
timeout.tv_usec = TIMEOUT_BYTE_;
// Reinitialize the sets for reading
FD_ZERO(&set);
FD_SET(uart_filestream, &set);
// Wait for the file descriptor to be available or for timeout
indicator = select(uart_filestream+1, &set, NULL, NULL, &timeout);
if(indicator < 0)
{
if(errno == EINTR)
{
// Try again
continue;
}
// This indicates an error
return -1;
}
else if(indicator == 0)
{
// This indicates a timeout; We assume that the transmission is over once first timeout is reached
return buffer_length;
}
// There's been a select that didn't time out before this read
indicator = read(uart_filestream, (void*)tmp_dest, max_len - buffer_length);
if(indicator < 0)
{
if(errno == EINTR)
{
// If the call was interrupted, try again
continue;
}
// If it was any other condition, the read is corrupt.
return -1;
}
else if(indicator == 0)
{
// If, somehow, EOF was reached
break;
}
// Change the necessary values
buffer_length += indicator ;
tmp_dest += indicator;
}
// Both branches of the if statement above have return, so this will not be reached
// but a warning is generated
return buffer_length;
}
void flush_buffer_UART_(int uart_filestream)
{
char c;
while(read(uart_filestream, &c, 1) > 0);
}
I know it is not the topic here, but if someone knows how to solve the flush issue, please respond. Also, any constructive criticism is very welcome.
P.S. I also have a write_UART() function but I did not deem necessary to post it as it represented no problem (measured with an oscilloscope and later, tried with echo. Echo wouldn't be able to give me the same message).
EDIT: Flush has been introduced and then merged with the source file. Still haven't figured out is it working or not.