Read UART continuously without blocking host application - c

I have been searching around and reading the documentation, but I am at the point where I need a little help. The use case is relatively simple, but the implementation is where I am stuck.
On one end is a wireless Xbee radio. This radio transmits a signal every 300ms. The payload of this signal consists of button states and joystick positions.
On the receiving end is a Linux based SBC running Ubuntu with an Xbee attached. For the sake of this post, I have combined the following into one single file and added a main for readability:
This code is based off the Serial programming Guide
#include <stdio.h>
#include <stdlib.h>
#include <linux/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <unistd.h>
#include "XbeeReceiver.h"
#define FALSE 0
#define TRUE 1
volatile int STOP = FALSE;
void signal_handler_IO(int status);
int wait_flag = TRUE;
int fileDescriptor = -1;
/* Constants */
const char *DEFAULT_PORT = "/dev/ttyTHS1";
const int DEFAULT_BAUD = B115200;
/* Prototypes */
struct N64_DTO queryController(int fd);
void init(const char *port, const int baud);
void setTermConfig(int fd, const int baud);
void signal_handler_IO(int status);
int openUART(const char *port);
void init(const char *port, const int baud) {
fileDescriptor = openUART(port ? port : DEFAULT_PORT);
setTermConfig(fileDescriptor, baud ? baud : DEFAULT_BAUD);
while (STOP == FALSE) {
if (wait_flag == FALSE) {
struct N64_DTO controller = queryController(fileDescriptor);
printf("\n\n\n Y Joystick:: %i \n", (signed char) controller.y);
printf("X Joystick:: %i \n", (signed char) controller.x);
/* I am supposed to set STOP to TRUE for this loop to stop, however I always want to read from the controller,
which is why this is now blocking */
wait_flag = TRUE;
}
}
}
struct N64_DTO queryController(int fd) {
struct N64_DTO controller;
int byteCount = read(fd, (char *) &controller, sizeof(struct N64_DTO));
if (byteCount == -1) {
printf("I'll handle this error later on");
exit(1);
}
return controller;
}
void signal_handler_IO(int status) {
printf("received SIGIO signal.\n");
wait_flag = FALSE;
}
void setTermConfig(int fd, const int baud) {
struct sigaction saio;
saio.sa_handler = signal_handler_IO;
sigemptyset(&saio.sa_mask);
sigaddset(&saio.sa_mask, SIGINT);
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction(SIGIO, &saio, NULL);
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, FASYNC);
struct termios toptions;
tcgetattr(fd, &toptions);
cfsetspeed(&toptions, baud);
/* man termios for individual definitions */
toptions.c_cflag &= ~PARENB;
toptions.c_cflag &= ~CSTOPB;
toptions.c_cflag &= ~CSIZE;
toptions.c_cflag |= CS8;
toptions.c_cflag &= ~CRTSCTS;
toptions.c_cflag |= CREAD | CLOCAL;
toptions.c_iflag &= ~(IXON | IXOFF | IXANY);
toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
toptions.c_oflag &= ~OPOST;
toptions.c_cc[VMIN] = sizeof(struct N64_DTO);
toptions.c_cc[VTIME] = 0;
tcsetattr(fd, TCSANOW, &toptions);
tcflush(fd, TCIFLUSH);
}
int openUART(const char *port) {
return open(port, O_RDWR | O_NOCTTY | O_NONBLOCK);
}
int main(int argc, char *argv[]) {
int i;
init("/dev/ttyTHS1", 115200);
for (i = 0; i < 10; i++) {
printf("This loop never prints..");
}
return 0;
}
Although it needs more refactoring, this code works and gives me the expected output. However, now that I have the output, I would like to do something with it, i.e send as input to a motor controller etc. The problem is, due to the frequency of reads(300ms) the program is ultimately blocking any other routines from executing.
I went back to the drawing board a few weeks ago and started down a few different paths. I explored threading, signal handlers, leveraging poll/select API, and the possibility of creating a dedicates kernel module. Last week, for a different reason, I put the controller up on a scope and setup triggers for the rising edge of the signal. That’s when it hit me. What I want is an event driven model where the OS notifies me when a signal is detected on a particular serial port. I figured I would reach out and see if someone has experience in this area that would be willing to steer me in the right direction. I have been at this issue for a week and still have not made any progress. In the end, the goal is to continuously receive serial data from the port without blocking other parts of my applications(driving motors, moving servos).

Related

Receive extra null byte when using serial port

I'm writing a program for serial port data transmission on Linux, but find that every time the sender opens the port, the receiver gets an extra null byte '\x00'.
Here's the code of sender:
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
int main(int argc, char* argv[]) {
int fd_com_ = open("/dev/ttyAM0", O_RDWR | O_NOCTTY | O_NONBLOCK);
struct termios attrs_;
attrs_.c_iflag = IGNBRK;
attrs_.c_oflag = 0;
attrs_.c_cflag = (CLOCAL | CREAD);
attrs_.c_cflag |= CS8;
attrs_.c_lflag = 0;
attrs_.c_cc[VMIN] = 0;
attrs_.c_cc[VTIME] = 0;
cfsetspeed(&attrs_, B115200);
tcsetattr(fd_com_, TCSANOW, &attrs_);
const char *s = "abcd";
write(fd_com_, s, 4);
sleep(1);
write(fd_com_, s, 4);
sleep(1);
close(fd_com_);
fd_com_ = open("/dev/ttyAM0", O_RDWR | O_NOCTTY | O_NONBLOCK);
write(fd_com_, s, 4);
return 0;
}
The receiver has the same configuration, but receives "\x00abcdabcd\x00abcd". How to fix this problem so the receiver could get "abcdabcdabcd"?
Update:
The code of receiver:
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
int main(int argc, char* argv[]) {
int fd_com_ = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NONBLOCK);
struct termios attrs_;
attrs_.c_iflag = IGNBRK;
attrs_.c_oflag = 0;
attrs_.c_cflag = (CLOCAL | CREAD);
attrs_.c_cflag |= CS8;
attrs_.c_lflag = 0;
attrs_.c_cc[VMIN] = 0;
attrs_.c_cc[VTIME] = 0;
cfsetspeed(&attrs_, B115200);
tcsetattr(fd_com_, TCSANOW, &attrs_);
char buf[100];
ssize_t sz;
while(1) {
sz = read(fd_com_, buf, 100);
if (sz > 0) {
for (ssize_t i=0; i<sz; i++) {
printf("%02hhx\n", buf[i]);
}
}
sleep(1);
}
return 0;
}
Please check in your ARM board's documentation how the ARM's UART is hooked up and how it is configured in hardware and by your platform driver.
From what you describe, I would suppose that when opening the UART port on the ARM, the physical UART (i.e., the hardware peripheral module within the ARM or maybe an externally wired UART chip) is enabled or restored from some unknown idle state to the proper -12 Volt idle state of RS232. This transistion may be enough for your PC's UART to recognize a start bit and receive a bogus character.
You may want to check the serial line using an oscilloscope to see what happens when actually opening the port.
the initial \x0 is the indication that a parity or framing error occurred.
this error occurs because the termios fields are not being setup properly.
so the initial line high/low status and number of start/stop bits and parity
are not being setup properly.
you might read: http://man7.org/linux/man-pages/man3/termios.3.html
which discusses each of the fields and their contents and meaning.
(The linked page is way too long to post here)

fgets not blocking in descriptor promoted to stream

I am using fgets in a small C program - running under Ubuntu - to read data coming from Arduino via its FTDI USB/Serial converter.
I am using low level I/O function from GNU libc (since I want - in the future - be able to control baud rate etc) and then promoting from descriptor to stream in order to use higher leve I/O functions.
The observed behaviour of my program suggests that the fgets function does not block until the line terminator is acquired (as in my opinion it should do).
Any explanation?
The codes of my programs are here
-> PC side:
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#define bufLen 81
int main(void) {
int fd; /* File descriptor for the port */
FILE * f; /* port will be identified also as stream 'f'*/
char buf[bufLen];
fd = open("/dev/ttyACM0", O_RDWR | O_NOCTTY);
if (fd == -1) {perror(""); return errno;}
else {
f = fdopen(fd, "r+t");
while (1) {
if (fgets(buf,bufLen,f)) {
printf("{%s}\n",buf);
sleep(1);
} else {
{perror("ahhhh! ");}
}
}
}
return 0;
}
-> Arduino side:
char ar[]="ABCDEFGHIJKLMNOPQRSTUVWXYZ ";
int k1 = 0, k2 = 0;
void setup() {
// initialize serial:
Serial.begin(9600);
}
void loop() {
Serial.print(ar[k1]);
k1 = (++k1) % 26;
k2 = (++k2) % 5;
if (k2==0) {Serial.println();}
delay(300);
}
And the result of run..
xxx#RevoR3600:~/CProg$ ./SerialPort
ahhhh! : Success
ahhhh! : Success
ahhhh! : Success
...
... (many identical lines...)
...
ahhhh! : Success
ahhhh! : Success
{AP}
{A}
{BCD}
{E
}
{FGHIJ
}
^C
xxx#RevoR3600:~/CProg$
After sleeping one night on it and two more hours of digging this site (and testing) I finally came across a solution right here; sorry I was not able to find it before posting (I can't figure out why); now it is matter for me to study exactly why the found solution is working (and why there is still a minor problem: some strange characters acquired on starting).
Thanks (and apology) to everybody has read my post.
Here is the working (test) code:
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<unistd.h>
#include<fcntl.h>
#include<termios.h>
#define bufLen 81
int main() {
char buf[bufLen];
struct termios tty;
FILE * f;
int fd=open("/dev/ttyACM0",O_RDWR | O_NOCTTY);
if(fd == -1) {
perror("Unable to open /dev/ttyACM1\n");
return -1;
} else {
if(tcgetattr(fd, &tty)!=0) {perror("tcgetatt() error"); return -1;}
else {
cfsetospeed(&tty, B9600);
cfsetispeed(&tty, B9600);
tty.c_cflag &= ~PARENB;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
tty.c_cflag &= ~CRTSCTS;
tty.c_cflag |= CLOCAL | CREAD;
tty.c_iflag |= IGNPAR | IGNCR;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_lflag |= ICANON;
tty.c_oflag &= ~OPOST;
tcsetattr(fd, TCSANOW, &tty);
if (!(f = fdopen(fd, "r+t"))) {perror("fdopen() error"); return -1;}
while (1) {
fgets(buf,bufLen,f);
printf("%s--\n",buf);
}
}
}
close(fd);
return 0;
}

read() in c raspberry pi gsm-module

I am working on a project where i can send sms with my raspberry pi thats connected to a sim300s module thrue a usb to serial connection.
problem:
sim300 doesnt detect the simcard , harware malfunction, i have ordered a new one. Until then i want to check if the connection works between the 2.
Now i want to send at command and receive OK(or something like that) Here is my code:
the value that is stored in string buf is TATATATAT.... etc.. etc..
Can someone explain why i am not getting OK back? Am i doing something wrong?
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
/*
* 'open_port()' - Open serial port 1.
*
* Returns the file descriptor on success or -1 on error.
*/
int main(int argc, char* argv[])
{
puts("start open_port");
open_port();
puts("open_port started ");
}
int
open_port(void)
{
char reply;//not shure if its gonna be used
struct termios options;
int n=0;
int fd; /* File descriptor for the port */
char buf[50];
int valueBytes;
int x = 0;
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY ); //| O_NDELAY);
if (fd == -1)
{//could not open port
fprintf(stderr, "open_port: Unable to open /dev/ - %s\n",
strerror(errno));
}
printf("%d",fd);
tcgetattr(fd, &options); //get current options for port
puts("test1");
cfsetispeed(&options, B9600);//set baud rate
cfsetospeed(&options, B9600);//set baud rate
puts("\n2");
options.c_cflag |= (CLOCAL | CREAD | CRTSCTS);//enable the receiver and set local mode
puts("\n3");
options.c_cflag &= ~PARENB;//disable parity generation and detection
options.c_cflag &= ~CSTOPB;//Use one stop bit per character
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;//use 8 bits to send or receive characters
options.c_lflag &= ~(ICANON | /* Enable canonical input (else raw) */
ECHO | /*Enable echoing of input characters */
ECHOE | /*Echo erase character as BS-SP-BS*/
ISIG); /*Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals*/
tcsetattr(fd, TCSANOW, &options);//TCSANOW change values immediately
puts("4");
n = write(fd, "AT\r", 4); // n = write(fd, "AT+CMGF=1\r", 10);
if (n < 0){
puts("write() of 4 bytes failed!");}
if(read(fd, buf, 2) < 0){
puts("it doesnt work"); }
fcntl(fd, F_SETFL, FNDELAY);
valueBytes=read(fd, buf,50);
printf("%d",valueBytes);
if(valueBytes < 0){
printf(strerror(errno));}
puts("6.3");
for(x; x<50;x++){
printf("%c",buf[x]);
}
close(fd);
puts("7");
return (fd);
puts("8");
}

Serial communication between linux and 8051 microcontroller board using rs-232 in c

I have problem in sending data serially using RS-232 from linux OS to uc 8051.
8051 setting:
baudrate = 9600;
comport = port1.
parity = none
stop bit = one
// my code for receiving data on 8051 uc
#include <reg51.h>
unsigned char value;
int i,j;
void ini()
{
TMOD=0x20; //Timer1, mode 2, baud rate 9600 bps
TH1=0XFD;
SCON=0x50;
TR1=1;
}
void delay()
{
for(i=0;i<=1000;i++)
for (j=0;j<=300;j++);
}
void recieve()
{
unsigned char value;
while(RI==0);
value=SBUF;
P1=value;
RI=0;
}
void main()
{
while(1)
{
ini();
recieve();
}
}
// and code which run on linux is as following
#include <stdio.h> // standard input / output functions
#include <string.h> // string function definitions
#include <unistd.h> // UNIX standard function definitions
#include <fcntl.h> // File control definitions
#include <errno.h> // Error number definitions
#include <termios.h> // POSIX terminal control definitionss
#include <time.h> // time calls
int open_port(void)
{
int fd; // file description for the serial port
fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) // if open is unsucessful
{
//perror("open_port: Unable to open /dev/ttyS0 - ");
printf("open_port: Unable to open /dev/ttyS0. \n");
}
else
{
fcntl(fd, F_SETFL, 0);
printf("port is open.\n");
}
return(fd);
} //open_port
int configure_port(int fd) // configure the port
{
struct termios port_settings; // structure to store the port settings in
cfsetispeed(&port_settings, B9600); // set baud rates
cfsetospeed(&port_settings, B9600);
port_settings.c_cflag &= ~PARENB; // set no parity, stop bits, data bits
port_settings.c_cflag &= ~CSTOPB;
port_settings.c_cflag &= ~CSIZE;
port_settings.c_cflag |= CS8;
tcsetattr(fd, TCSANOW, &port_settings); // apply the settings to the port
return(fd);
} //configure_port
int query_modem(int fd) // query modem with an AT command
{
char n;
fd_set rdfs;
struct timeval timeout;
// initialise the timeout structure
timeout.tv_sec = 10; // ten second timeout
timeout.tv_usec = 0;
// Create byte array
unsigned char send_bytes[] = { 0x00, 0xff};
write(fd, send_bytes, 2); //Send data
printf("Wrote the bytes. \n");
// do the select
n = select(fd + 1, &rdfs, NULL, NULL, &timeout);
// check if an error has occured
if(n < 0)
{
perror("select failed\n");
}
else if (n == 0)
{
puts("Timeout!");
}
else
{
printf("\nBytes detected on the port!\n");
}
return 0;
} //query_modem
int main(void)
{
int fd = open_port();
configure_port(fd);
query_modem(fd);
return(0);
} // main
But I have problem .. so plz help me out and tell me in which format linux sends data through rs-232. Also receiving format of 8051 micro-controller.
Need sample code in C.
I recomment basic fault finding
make sure you use correct cables (2 & 3 crossed out, no handshake lines to start with)
seperate the devices, i.e. connect your Linux machine to a PC running a terminal
program and try to establish 2-way communication - work on the Linux / C code until you achieve
connect your 8051 to a PC running a terminal program ....
connect Linux and 8051 only if they both work against a known and proven device
If your 8051 code sample represents all code .... how would the 8051 respond to the AT .... it can only receive.
good luck

Reading and writing from serial port,through loopback in c in linux

I am on a program to read and write from and to serial port using serial crosscable and loopback by soring 2nd and 3rd pin of cable. I am able to write but not able to read.
in read output it is showing 0 as the no. of bytes read by it. It is not showing error as -1 .
#include <stdio.h> // standard input / output functions
#include <string.h> // string function definitions
#include <unistd.h> // UNIX standard function definitions
#include <fcntl.h> // File control definitions
#include <errno.h> // Error number definitions
#include <termios.h> // POSIX terminal control definitionss
#include <time.h> // time calls
#include <sys/ioctl.h>
int open_port(void)
{
int fd; // file description for the serial port
fd = open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
if(fd == -1) // if open is unsucessful
{
perror("open_port: Unable to open /dev/ttyS0 - ");
}
else
{
fcntl(fd, F_SETFL, 0);
}
printf("%d",fd);
return(fd);
}
int configure_port(int fd) // configure the port
{
struct termios port_settings; // structure to store the port settings in
cfsetispeed(&port_settings, B9600); // set baud rates
cfsetospeed(&port_settings, B9600);
port_settings.c_cflag |= ( CLOCAL | CREAD );
port_settings.c_cflag &= ~PARENB; // set no parity, stop bits, data bits
port_settings.c_cflag &= ~CSTOPB;
port_settings.c_cflag &= ~CSIZE;
port_settings.c_cflag |= CS8;
tcflush( fd, TCIOFLUSH );
tcsetattr(fd, TCSANOW, &port_settings); // apply the settings to the port
return(fd);
}
int main()
{
int fd= open_port();
int d=configure_port(fd);
printf("%d",d);
int bytes;
char mk[10];
scanf("%s",&mk);
int w=write(fd,mk,strlen(mk));
int y=ioctl(fd,FIONREAD,&bytes);
printf("%d\n",w);
perror("write");
printf("%d",y);
char buffer[80];
char *data;
int nbytes;
data=buffer;
nbytes=read(fd,data,5);
printf("the outputis \n%d\n\n",nbytes);
perror("read");
while(nbytes > 0)
{printf("datmukun %d\n\n",nbytes);
data+=nbytes;
if (data[-1]=='\n'||data[-1]=='\r')
break;
}
return 0;
}
Depending on your TTY-driver the O_NDELAY and O_NONBLOCK can cause read to behave in a non-blocking manner. Therefore, it is very likely that the data has not been received by the time you call read. If you remove those flags, you should block in read until at least one character is available.
you must wait for a while for the data to become available. you can do this like the following:
very simply and just for test purpose by creating a small delay using a while loop and a sleep() function inside it and possibly a counter to try for a number of times.
you can use select() function on your file descriptor to let your process go to sleep for a while and get notified when the data is available or the timeout has reached.

Resources