I want to read serial COM port and to write the data to a file in LINUX. Actually I'm sending data from hyperterminal from other PC.
The problem is without while loop I can write only one line.
But with while(1) loop I can't write anything to file.
Or else I have to send BIG file, then application exits/terminates and writes to the file.
My application should write the data (it may 2 lines or any thing); after that it has to wait for next data.
So please help me out.....
Here is my Code
=========================================
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#define BAUDRATE B115200
#define MODEMDEVICE "/dev/ttyS0"
#define _POSIX_SOURCE 1 /* POSIX compliant source */
#define FALSE 0
#define TRUE 1
volatile int STOP=FALSE;
void signal_handler_IO (int status); /* definition of signal handler */
int wait_flag=TRUE; /* TRUE while no signal received */
struct timeval timeout;
char n;
fd_set rdfs;
/*Some variables*/
int num, offset = 0, bytes_expected = 255;
main()
{
int fd,c, res,i;
char In1;
struct termios oldtio,newtio;
struct sigaction saio; /* definition of signal action */
char buf[255];
FILE *fp;
/* open the device to be non-blocking (read will return immediatly) */
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd == -1)
{
perror("open_port: Unable to open /dev/ttyS0 - ");
return 1;
}
else
{
fcntl(fd, F_SETFL, 0);
n = select(fd + 1, &rdfs, NULL, NULL, &timeout);
}
fp = fopen("/root/Desktop/gccAkash/OutputLOG.txt","a+");
assert(fp != NULL);
/* install the signal handler before making the device asynchronous */
saio.sa_handler = signal_handler_IO;
sigemptyset(&saio.sa_mask);
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction(SIGIO,&saio,NULL);
/* allow the process to receive SIGIO */
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, FASYNC);
tcgetattr(fd,&oldtio); /* save current port settings */
/* set new port settings for canonical input processing */
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR | ICRNL;
newtio.c_oflag = 0;
newtio.c_lflag = ICANON;
newtio.c_cc[VMIN]=0; //it will wait for one byte at a time.
newtio.c_cc[VTIME]=10; // it will wait for 0.1s at a time.
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
while (STOP==FALSE)
{
if (wait_flag==FALSE) //if input is available
{
do {
res = read(fd,buf+offset,255);
offset += res;
} while (offset < bytes_expected);
if (offset!=0)
{
for (i=0; i<offset; i++) //for all chars in string
{
In1 = buf[i];
fputc ((int) In1, fp);
printf("charecter:%c\n\r",In1);
} //EOFor
}//EOIf
buf[res]=0;
printf("Received Data is:%s\n\r",buf);
if (res==0)
STOP=TRUE; //stop loop if only a CR was input
wait_flag = TRUE; // wait for new input
}
} //while stop==FALSE
while(readchar)
/* restore old port settings */
tcsetattr(fd,TCSANOW,&oldtio);
close(fd);
}
/************************************************** *************************
* signal handler. sets wait_flag to FALSE, to indicate above loop that *
* characters have been received. *
************************************************** *************************/
void signal_handler_IO (int status)
{
printf("received SIGIO signal.\n");
wait_flag = FALSE;
}
You shouldn't be using SIGIO. I've never found any good documentation for using it to do anything, and there are better ways. There's poll, select, and epoll families of functions that would work much better for this. It looks to me like your program is busy waiting on data to become available, which means it's eating up CPU time. The alternatives let your program go to sleep when there's nothing to do. You could use pause to get this effect though, and the program would go to sleep until it received a signal (like SIGIO). You could also just use have the tty file opened in blocking mode and let read block you until there is something read.
You shouldn't be using
printf
from both a signal handler and regular code. Usually you shouldn't ever be using stdio operations from a signal handler at all because it can sleep, but you can often get away with it during testing. Using it from both a signal handler and regular code (or in multiple signal handlers if one signal isn't blocking others) is bad because then the functions will be accessing the same data. Essentiallyprintf
in your regular code might be interrupted by SIGIO and then theprintf
in there is called and bothprintf
s are wanting access toFILE stdout
's internal data -- either that or the locks (which are intended to stop to block different threads, not the same thread calling a second time) will block the SIGIO handler's call toprintf
, which will block the whole program. The problem here is called a race condition and may or may not happen depending on timing.In the code:
_
_
You are continually passing 255 as the length you want, even though you may already have filled part of the buffer. If you the first time through the loop you get 254 bytes of data and then call again asking for 255 bytes again and get more than 1 byte you have overrun your buffer. Additionally you never drop
offset
back to0
so it just grows and grows, so so you have an overflow of the buffer if your program ever reads more than 255 bytes.You also aren't checking that the
read
return value isn't -1. If it were -1 then your program does very bad things.Also, there is no real reason (that I can see) to try to try to accumulate 255 bytes of data before you write the data you have already read back out. Even if you only want to handle the first 255 bytes that come in the serial port then you could go ahead and write the first ones as the last ones were coming in, and then exit the loop after the 255 limit had been reached.
struct sigaction saio
, but it has other fields. These should be set to something or you are just throwing random bits in as flags and masks._
_
newtio
totcsetattr
, but it is possible that you have not totally initialized it. You should have done:_
before you set the other flags. This way you can just use the values that were already there for any fields that you weren't setting explicitly.
Additionally, you seem to be setting all of the BAUDRATE flags.
This isn't correct, most likely.
BAUDRATE
is used for masking out the baud code like this : tcflag_t baud_code = tio.c_cflag & BAUDRATE;And now
baud_code
has just the bits that correspond to the baud set, without theCRTSCTS
andCLOCAL
stuff set. You should be using one of the baud codes that look likeB9600
andB115200
here instead of BAUDRATE, unless you want to:which would only change the baud rate and leave the rest of the flags alone. This is what
int cfsetspeed(struct termios *termios_p, speed_t speed);
is for._
I don't know where you got this code, but it looks over-complicated for what you actually want to achieve. It looks like copy pasting to me.
What is the problem with a loop like this ?
What exactly is your question ? How to find the good test to exit from the loop ? Do you want to exit when you receive some char, after a certain time has elapsed ?
For answer on the possible value of len, you can look at the
tcgetattr
man page. Maybe your tty options are not suited to what you want to achieve ? Sorry but neither your question nor your code is clear, maybe you could first describe whet you want to achieve with your program.