Serial read() issues in linux C - c

I have an instrument which I need to talk to by RS232. I am using C and following is the code. The problem is that, when I try to read field value (reading from meter) which is 11 byte long, one read command is reading only 8 bytes and subsequently I have to issue another read command which gives me final 3 bytes. Finally I am concatenating both read buffers to make final meaningful value.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXWAIT 30
#define BAUDRATE B9600
#define TESLAMETER "/dev/ttyS0"
#define _POSIX_SOURCE 1 /* POSIX compliant source */
#define FALSE 0
#define TRUE 1
#define NOREAD 255
volatile int STOP = FALSE;
int fd;
struct termios oldtp, newtp;
int openComPort(void)
{
fd = open(TESLAMETER, O_RDWR | O_NOCTTY |O_NDELAY );
if (fd <0)
{
perror(TESLAMETER);
return fd;
}
else
fcntl(fd,F_SETFL,0);
tcgetattr(fd,&oldtp); /* save current serial port settings */
bzero(&newtp, sizeof(newtp));
newtp.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
newtp.c_iflag = IGNPAR | ICRNL;
newtp.c_oflag = 0;
newtp.c_lflag = 0;//ICANON;
newtp.c_cc[VINTR] = 0; /* Ctrl-c */
newtp.c_cc[VQUIT] = 0; /* Ctrl-\ */
newtp.c_cc[VERASE] = 0; /* del */
newtp.c_cc[VKILL] = 0; /* # */
newtp.c_cc[VEOF] = 4; /* Ctrl-d */
newtp.c_cc[VTIME] = 1; /* inter-character timer unused, 0.5 seconds read timeout */
newtp.c_cc[VMIN] = 0; /* blocking or non blocking read until 1 character arrives */
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtp);
return fd;
}
float readMagField()
{
unsigned char cmd[]="FA0\r"; // Read Field
char buff2[11] = {0x00};
char buff3[11] = {0x00};
float fieldFloat = 0.00;
int n_written= 0, spot = 0, res;
do
{
n_written = write( fd, &cmd[spot], 1 );
spot += n_written;
} while (cmd[spot-1] != '\r' && n_written > 0);
if (n_written < 0)
{
printf("write() of 4 bytes failed!\n");
return FALSE;
}
else
{
//printf("Field Read Command sent successfully %d\n",n_written);
res = read(fd,buff2,11); // Reads 8 bytes
res = read(fd,buff3,11); // Reads remaining 3 bytes
fieldFloat = atof(strcat(buff2,buff3)); // Final string of 11 bytes here
return fieldFloat;
}
}
Is there something that I am doing or setting wrong? Because, I can read the complete set of characters in one go using Python serial module, but not in C. I am working on Ubuntu 12.04 LTS.

read() may return without reading the specified length.
read(2) - Linux manual page
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.
How about retrying until the desired length of data have been read?
ssize_t read2(int fd, void *buf, size_t count) {
ssize_t read_length = 0;
while (read_length < count) {
ssize_t delta = read(fd, buf + read_length, count - read_length);
if (delta == -1) return -1;
read_length += delta;
}
return read_length;
}

Related

How to send raw binary data over serial in C without non-native libraries in linux

I'm currently trying to send raw binary data in the format of decimal to an external device over serial. I currently have the data in a buffer array but would like it in a structure like this:
struct packetData{
uint8_t sync1;
uint8_t sync2;
uint16_t messageId;
uint16_t dataWordCount;
uint16_t flags;
uint16_t checksum;
};
I'm also using 9600 baud, and have all the termios settings set using cfmakeraw and I'm currently writing using:
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
int flags = O_RDWR | O_NOCTTY | O_NDELAY;
fd = open(device, flags);
uint16_t buf_tx[BUFFER_SIZE] = {255,129,191,0,2057,0};
if(fd == -1){
printf("\n Failed to open port! ");
return -1;
}
tcgetattr(fd, &tty); //Get the current attributes of the Serial port
cfmakeraw(&tty);
cfsetispeed(&tty, B9600); //Set read speed as 9600 baud
cfsetospeed(&tty, B9600); //Set write speed as 9600 baud
if((tcsetattr(fd, TCSANOW, &tty)) != 0){
printf("Error! Can't set attributes.\n");
return -1;
}
else{
printf("Connection successful! \n");
}
while(x < 1000){
memset(buf_tx, 0, sizeof(buf_tx));
tcflush(fd, TCOFLUSH);
if(y < 5){
if(write(fd, buf_tx, 5) == -1){
printf("\n");
printf("Error>>: %s\n",strerror(errno));
y++;
}
}
tcflush(fd, TCIOFLUSH);
usleep(1000);
x++;
}
This code isnt the full code, just the setup/write parts so no need to worry about its syntax. if possible it would be nice not to have that buffer array and just use the struct directly, but I'll take what I can get.
It seems you have the serial port opening more or less in hand. I prefer to set the termios member components explicitly myself, but cfmakeraw() is perfectly fine too.
What you should consider, is having a separate function to send one or more of those structures at a time. For example,
int write_all(const int fd, const void *buf, const size_t len)
{
const char *data = buf;
size_t written = 0;
ssize_t n;
while (written < len) {
n = write(fd, data + written, len - written);
if (n > 0) {
written += n;
} else
if (n != -1) {
/* C library bug, should never occur */
errno = EIO;
return -1;
} else {
/* Error; n == -1, so errno is already set. */
return -1;
}
}
/* Success. */
return 0;
}
The function will return 0 if all data was successfully written, and -1 with errno set if an error occurs.
To send a struct packetData pkt; just use write_all(fd, &pkt, sizeof pkt).
To send a full array struct packetData pkts[5]; use write_all(fd, pkts, sizeof pkts).
To send n packets starting at pkts[i], use write_all(fd, pkts + i, n * sizeof pkts[0]).
However, you do not want to use tcflush(). It does not do what you think it does; it actually just discards data.
Instead, to ensure that the data you have written has been transmitted, you need to use tcdrain(fd).
I recommend against adding tcdrain(fd) at the end of write_all() function, because it blocks, pauses the program, until the data has been transmitted. This means that you should only use tcdrain() before you do something that requires the other end has received the transmission; for example before trying to read the response.
However, if this is a query-response interface, and you do intend to also read from the serial device, you should set tty.c_cc[VMIN] and tty.c_cc[VTIME] to reflect how you intend to use the interface. I prefer asynchronous full-duplex operation, but that requires select()/poll() handling. For half-duplex, with these exact structures only, you can use tty.c_cc[VMIN] = sizeof (struct packetData) with say tty.c_cc[VTIME] = 30, which causes read() to try and wait until a full structure is available, but at most 30 deciseconds (3.0 seconds). Something like tty.c_cc[VMIN] = 1; tty.c_cc[VTIME] = 1; is more common; that causes read() to return a short count (even 0!) if there is no additional data received within a decisecond (0.1 seconds). Then, the receive function could be along the following lines:
int read_all(const int fd, void *buf, const size_t len)
{
char *const ptr = buf;
size_t have = 0;
ssize_t n;
/* This function is to be used with half-duplex query-response protocol,
so make sure we have transmitted everything before trying to
receive a response. Also assumes c_cc[VTIME] is properly set for
both the first byte of the response, and interbyte response interval
in deciseconds. */
tcdrain(fd);
while (have < len) {
n = read(fd, ptr + have, len - have);
if (n > 0) {
have += n;
} else
if (n == 0) {
/* Timeout or disconnect */
errno = ETIMEDOUT;
return -1;
} else
if (n != -1) {
/* C library bug, should never occur */
errno = EIO;
return -1;
} else {
/* Read error; errno set by read(). */
return -1;
}
}
/* Success; no errors. */
return 0;
}
If this returns -1 with errno == ETIMEDOUT, the other side took too long to answer. There may be remainder of the late response in the buffer, which you can discard with tcflush(TCIFLUSH) (or with tcflush(TCIOFLUSH), which also discards any written data not yet transmitted). Synchronization in this case is a bit difficult, because the above read_all() function doesn't return how many bytes it received (and therefore how many bytes to discard of a partial structure).
Sometimes the interface used always returns the number of bytes, but also sets errno (to 0 if no error occurred, and a nonzero error constant otherwise). That would be better for a query-response interface read and write functions, but many programmers find this use case "odd", even though it is perfectly okay by POSIX.1 standard (which is the relevant standard here).

Sending commands to Arduino while it's already running

I'm currently making a robot and I'm using Raspberry pi 3 and Arduino uno for it.
What I want to do is send commands to Arduino via Pi by using a command line app (written in C)which takes in user input to make certain parts of the robot to move.
Use the following programs to send and receive serial data from Arduino:
Original Source
SEND.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BAUDRATE B115200 //Baud Rate Here
#define MODEMDEVICE "/dev/ttyUSB0" //Serial Port Here
#define _POSIX_SOURCE 1 /* POSIX compliant source */
#define FALSE 0
#define TRUE 1
FILE *file;
int fileLen;
char *tmpbuffer;
void openport(void);
void readport(void);
void sendport(void);
int fd=0;
struct termios oldtp, newtp;
//char sendcmd1[256]="\0";
char buffer[512];
void main()
{
openport();
sleep(1);
// readport();
sendport();
}
void sendport(void)
{
printf("enter write\n");
int n;
// sem_wait(&len);
file = fopen("sample.txt", "r");
//get file size
fseek(file, 0, SEEK_END);
fileLen = ftell(file);
fseek(file, 0, SEEK_SET);
tmpbuffer = (char *)malloc(fileLen + 1);
//read file contents
printf("Start send\n");
fread(tmpbuffer, fileLen, 1, file);
fclose(file);
n = write(fd, tmpbuffer, fileLen + 1);
if (n < 0)
{
fputs("write() of bytes failed!\n", stderr);
}
else
{
printf("File sent successfully %d\n",n);
}
close(fd);
}
void openport(void)
{
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY |O_NDELAY );
printf("Oviya %d\n",fd);
if (fd <0)
{
perror(MODEMDEVICE); }
fcntl(fd,F_SETFL,0);
tcgetattr(fd,&oldtp); /* save current serial port settings */
// tcgetattr(fd,&newtp); /* save current serial port settings */
bzero(&newtp, sizeof(newtp));
// bzero(&oldtp, sizeof(oldtp));
newtp.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
newtp.c_iflag = IGNPAR | ICRNL;
newtp.c_oflag = 0;
newtp.c_lflag = ICANON;
newtp.c_cc[VINTR] = 0; /* Ctrl-c */
newtp.c_cc[VQUIT] = 0; /* Ctrl-\ */
newtp.c_cc[VERASE] = 0; /* del */
newtp.c_cc[VKILL] = 0; /* # */
//newtp.c_cc[VEOF] = 4; /* Ctrl-d */
newtp.c_cc[VEOF] = 0; /* Ctrl-d */
newtp.c_cc[VTIME] = 0; /* inter-character timer unused */
newtp.c_cc[VMIN] = 1; /* blocking read until 1 character arrives */
newtp.c_cc[VSWTC] = 0; /* '\0' */
newtp.c_cc[VSTART] = 0; /* Ctrl-q */
newtp.c_cc[VSTOP] = 0; /* Ctrl-s */
newtp.c_cc[VSUSP] = 0; /* Ctrl-z */
newtp.c_cc[VEOL] = 0; /* '\0' */
newtp.c_cc[VREPRINT] = 0; /* Ctrl-r */
newtp.c_cc[VDISCARD] = 0; /* Ctrl-u */
newtp.c_cc[VWERASE] = 0; /* Ctrl-w */
newtp.c_cc[VLNEXT] = 0; /* Ctrl-v */
newtp.c_cc[VEOL2] = 0; /* '\0' */
// tcflush(fd, TCIFLUSH);
// tcsetattr(fd,TCSANOW,&newtp);
}
Go through the code and modify it according to your will.
This code expects a .txt file as input.
This answer is written from https://www.codeproject.com/Questions/718340/C-program-to-Linux-Serial-port-read-write
Visit the original site for additional information

Entire characters in the text file is not getting transmitted in Hi 3520D

I am trying to read some data from a text file and writing it to the ttyUSB* socket id.
I am using Hi3520d Dvr. I have it's RS485 port connected to a "RS485 to RS232 converter". This converter is connected to the PC through a USB port.
The text file is getting read properly to the buffer, but while writing last few lines of the text is not transmitting. This is happening with file with size more than 4.5kb exactly and without usleep() function.
I am using minicom on linux terminal to display both read and written text.
Thanks in advance for looking into this.
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#define Bdrate B9600
int Intserial(char *dev, int Baudrate)
{
//printf("Insterial func\n");
int sid;
int iDebug = -1;
struct termios serial_struct;
sid = open(dev, O_RDWR | O_NOCTTY | O_NDELAY);
if (sid > 0)
{
memset(&serial_struct, 0, sizeof(serial_struct)); /* clear the new struct */
serial_struct.c_cflag = Baudrate | CS8 | CLOCAL | CREAD;
serial_struct.c_iflag = IGNPAR;
serial_struct.c_oflag = 0;
serial_struct.c_lflag = 0;
serial_struct.c_cc[VMIN] = 0; /* block untill n bytes are received */
serial_struct.c_cc[VTIME] = 0; /* block untill a timer expires (n * 100 mSec.) */
iDebug = tcsetattr(sid, TCSANOW, &serial_struct);
if (iDebug < 0)
{
printf("Err 0\n"); //Unable to set serial port parameters
return (0);
}
}
else
{
printf("Err 1\n"); //Serial port not open
return (0);
}
//printf("sid is %d \n",sid);
return (sid);
}
int main()
{
int sid1 = -1, size = 0, i = 0, x, w;
size_t ln;
FILE *fd;
char buf[2233];
fd = fopen("h.txt", "r");
if (fd)
{
sid1 = Intserial("/dev/ttyAMA1", Bdrate); //RS485 port of Hi3520d
if (sid1 > -1)
{
system("himm 0x200F004C 0"); // commands transmitting and recieving
system("himm 0x201A0400 1");
system("himm 0x201a0004 1");
while (!feof(fd))
{
memset(buf, 0, sizeof(buf));
fread(buf, sizeof(buf), 1, fd);
printf("%s", buf);
write(sid1, buf, sizeof(buf));
usleep(5);
}
getchar();
}
else
printf("com port cant open\r\n ");
fclose(fd);
close(sid1);
}
else
printf("File cant open\r\n");
printf("task completed............\r\n");
}
You have to observe return value of fread for number of bytes read by fread function. the actual read size may not equal to bytes requested, also you have to pass number of bytes read by fread (as valid bytes in buffer) to write function as number of bytes to write.
The code should be something like this
memset(buf,0,sizeof(buf));
size_t bytesRead = fread(buf,sizeof(buf),1,fd);
if(bytesRead > 0)
write(sid1,buf, bytesRead);
Also as LPs said, fread doesn't end buffer with termination character, so passing buffer filled by fread to printf("%s") will be undefined behavior
There are numerous issues with your code, but the salient cause of "the text is not transmitting" is probably the failure to check the return value of
write(sid1, buf, sizeof(buf));
Because the serial terminal was opened in non-blocking mode, each write() will return immediately, before the data has been actually transmitted.
Since the serial terminal is configured for a rather slow 9600 baud, the data could be queued up in the line discipline buffer and other intermediate buffers.
The line discipline buffer is typically 4096 bytes long.
Assuming that the fread() operations are always successful (which you seem to have verified), then the second iteration of the write() of 2233 bytes could potentially saturate the line discipline buffer, and return with a short write return value (which would be ignored).
The third iteration of the write(), if it's quick enough, could then be outright rejected with a return value of -1 and an errno of EAGAIN to indicate that the write would block.
This error condition would be silently ignored, and this 2233 bytes of data will never be transmitted.
This seems to correlate perfectly with your observation of "last few lines of the text is not transmitting ... with file with size more than 4.5kb exactly and without usleep() function."
ADDENDUM
Revised code for blocking mode, proper terminal setup, and checking of return values is shown below.
A corrected version of #e.jahandar's suggestion and comments from #LPs are also incorporated.
...
sid = open(dev, O_RDWR | O_NOCTTY);
if (sid < 0) {
printf("Err 1\n"); //Serial port not open
return (-1);
}
if (tcgetattr(sid, &serial_struct) < 0) {
printf("Err 2\n");
return (-2);
}
cfsetospeed(&serial_struct, (speed_t)Baudrate);
cfsetispeed(&serial_struct, (speed_t)Baudrate);
cfmakeraw(&serial_struct);
serial_struct.c_cc[VMIN] = 1;
serial_struct.c_cc[VTIME] = 10;
serial_struct.c_cflag &= ~CSTOPB;
serial_struct.c_cflag &= ~CRTSCTS; /* no HW flow control? */
serial_struct.c_cflag |= CLOCAL | CREAD;
if (tcsetattr(sid, TCSANOW, &serial_struct) < 0) {
printf("Err 3\n"); //Unable to set serial port parameters
return (-3);
}
...
#define BUFSIZE 2233
char buf[BUFSIZE + 1];
...
size_t frv;
ssize_t wrv;
...
do {
frv = fread(buf, 1, BUFSIZE, fd);
buf[frv] = 0; /* terminate string for printf */
if (frv > 0) {
wrv = write(sid1, buf, frv);
if (wrv < frv) {
/* handle error or short write */
}
} else
break;
} while (1);
...

UART not reading as much data as I would like

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.

UART compare charts. Beaglebone

I have a problem trying to compare the uart input data (from a GPS) with '$' in order to detect a new package. I am sure that the problem is in how I manipulate the charRead variable. I tried one thousand of things, but probably because of my inexperience I have not figured out what it is the problem.
The code compiles and the data is coming all the time, but once I load the code into the beaglebone, it gets stacked but and it doesn't enter in the "if (charRead =='$')".
Thank you in advance!
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <time.h>
#include <iostream>
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <limits.h>
#include "Payload.h"
#define SLOTS "/sys/devices/bone_capemgr.9/slots"
#define CR 0x0d
#define SPACE 0x20
#define COMMA 0x2C
#define MAXSIZE 100
unsigned long time_data;
unsigned int button = 45;
int i,z =0, j=0, value;
int rx_length;
int main()
{
//uart4 configuration using termios
int fd;
//unsigned char *mess;
unsigned int value = 0;
gpio_export(button);
//Wait until the button is pushed
while (value != 1){
if (z==0){
printf("waiting\n");}
z++;
gpio_get_value(button, &value);}
//OPEN THE UART
//open uart4 for tx/rx, not controlling device
if((fd = open("/dev/ttyO4", O_RDONLY | O_NOCTTY|O_NONBLOCK)) < 0){
printf("Unable to open uart4 access.\n");
}
termios uart4;
cfsetospeed(&uart4, B9600); //Set the speed
//set attributes of uart4
uart4.c_iflag = 0;
uart4.c_oflag = 0;
uart4.c_lflag = 0;
tcsetattr(fd, TCSANOW, &uart4);
//----- CHECK FOR ANY RX BYTES -----
// Read up to 100 characters from the port if they are there
unsigned char stringRead[MAXSIZE];
unsigned char charRead;
do{
if (rx_length = read(fd, (void*)charRead, MAXSIZE)>0){
if (charRead =='$'){
i=0;
stringRead[i] = charRead; //Store in the first position of the char --> $
do {
rx_length = read(fd, (void*)charRead, MAXSIZE); //Read the next bit
if( (charRead != '\0') ) {
i++;
stringRead[i] = charRead; //write into stringRead
}
} while(charRead != 'CR'); //ASCII Carriage Return
stringRead[i+1] = charRead;
printf("%s", stringRead);
}}
if (rx_length==0){
//No data
}
gpio_get_value(button, &value);
}while (value!=0);
gpio_unexport(button);
close(fd);
return 0;
}
You're passing a cast of the variable value of charRead rather than a pointer to a memory location as the function read() expects void *.
read(fd, (void*)charRead, MAXSIZE)
You need to either read one character at a time:
read(fd, &charRead, 1)
Or change your reading logic to maximize amount read and data processing. I also recommend adding a bounds check on accessing stringRead.
// The following should handle the reading of a GPS NMEA message and display it
// I have not run the program, but compiling it was successful
// note:
// 1) the handling of the 'i' variable
// 2) the calls to reading the GPS input
// 3) the handling of error conditions
// 4) the simple logic flow
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <time.h>
//#include <iostream> // this is not C++ so this line not needed
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <limits.h>
#include "Payload.h"
// #define SLOTS "/sys/devices/bone_capemgr.9/slots" // not used, raises compiler warning
#define CR (0x0D)
// #define SPACE (0x20) // not used, raises compiler warning
// #define COMMA (0x2C) // not used, raises compiler warning
#define MAXSIZE (100)
#define BUTTON_PORT (45)
// unsigned long time_data; // not used, raises compiler warning
// int j=0; // not used, raises compiler warning
// int value = 0; // not used, raises compiler warning about variable masking
int main()
{
int i;
int z = 0; // flag used to control execution flow
int rx_length; // return status value from read()
//uart4 configuration using termios
int fd; // file descriptor number
//unsigned char *mess; // not used, raises compiler warning
unsigned int value = 0;
gpio_export(BUTTON_PORT);
//Wait until the button is pushed
// burn mass CPU cycles, while waiting
while (0 == value)
{
if (z==0)
{
printf("waiting\n");
z++; // to stop re-entry to this 'if' block
}
// suggest using nsleep() to free up CPU
gpio_get_value(BUTTON_PORT, &value);
} // end while
//open uart4 for rx
if((fd = open("/dev/ttyO4", O_RDONLY | O_NOCTTY|O_NONBLOCK)) < 0)
{
perror("open failed for /dev/tty04");
exit(1);
}
// implied else, open successful
termios uart4;
cfsetospeed(&uart4, B9600); //Set the speed to match the GPS output
//set attributes of uart4
// Note: probably better to read the current termois values
// then modify them to the desired states
uart4.c_iflag = 0;
uart4.c_oflag = 0;
uart4.c_lflag = 0;
tcsetattr(fd, TCSANOW, &uart4);
//----- CHECK FOR ANY RX BYTES -----
// Read up to 100 characters from the port if they are there
unsigned char stringRead[MAXSIZE]; // will contain a GPS NMEA message
unsigned char charRead; // input buffer
do{
while(1)
{
rx_length = read(fd, &charRead, 1);
if( 0 == rx_length )
{ // this will execute a lot since fd set to non-blocking
; // do nothing, while hogging CPU cycles
// suggest using nsleep() to free CPU
}
else if( 0 > rx_length )
{
perror( "read failed" );
exit(1);
}
else if (charRead =='$')
{
stringRead[0] = charRead; //Store first char of NMEA GPS message
i=1; // index for second char of NMEA message from GPS
}
else
{
stringRead[i] = charRead; //Store char
i++; // index for next char into stringRead buffer
if( MAXSIZE <= i )
{ // then overrun input buffer
perror( "read- overrun input buffer, GPS message too long");
exit(2);
}
if( CR == charRead ) //ASCII Carriage Return - end of message
{ // then, got complete message
break; // exit read loop, so can process message
}
} // end if
} // end while
stringRead[i] = '\0'; // terminate string so it can be printed
printf("%s", stringRead);
// get button state via BUTTON_PORT(45)
gpio_get_value(BUTTON_PORT, &value);
} while (value!=0); // then read next gps message
gpio_unexport(BUTTON_PORT);
close(fd);
return 0;
}

Resources