I have an actual RS-232 based serial port that I use with Linux to wirelessly communicate with a microcontroller. (I use wireless modules on both ends of the connection instead of a serial cable)
There is one issue. The entire operation must be half-duplex because the wireless modules do not support full-duplex operation. In fact, they ignore incoming data while they send data.
So in C, I'm looking for the most-efficient and fastest code possible to detect when there is no data coming from the remote transmitter before beginning to sending code back to the remote.
Also, the remote device gives me only 30mS to send data back to it and the data itself is 47 bytes and the speed is 56kbps so the routine to detect silence on the line needs to execute fast.
I have provided some ideas but I'm trying to figure out which is best. Maybe there is a function I don't even know about that works even better. I have experimented with different VMIN and VTIME values and found myself not successful.
Here are my code ideas:
Idea 1:
char b[2]; //incoming character buffer
int nread=1; //number of bytes read
int fd=open(dev,O_NOCTTY | O_RDONLY | O_SYNC);
while (nread > 0){
nread=read(fd,b,1); //keep reading until no bytes read are reported
}
fclose(fd);
Idea 2:
int timeout=1; //1 second timeout
int fd=open(dev,O_NOCTTY | O_RDONLY | O_SYNC);
time_t then=time(NULL);
while (time(NULL) - then > timeout){
nread=read(fd,b,1);
if (nread > 0){then=time(NULL);} //reset timer if at least 1 byte is read
}
fclose(fd);
Idea 3:
char b[2]; //incoming character buffer
int nread=1; //number of bytes read
int numfail=0; //number of failed attempts to read bytes
int fd=open(dev,O_NOCTTY | O_RDONLY | O_SYNC);
while (numfail < 100){
nread=read(fd,b,1); //keep reading until no bytes read are reported 100 consecutive times
if (nread < 1){numfail++;}else{numfail=0;}
}
fclose(fd);
The first idea which probably wont work (unless I get lucky to find a special linux driver configuration here?) will keep checking the port for a single byte until none is found then exits.
The second idea sets a specific time in which characters must come in and if no character arrives in the given time, then the line is considered free. Otherwise the clock resets.
The last idea is an extension to the first idea, except that the program won't exit until several consecutive attempts to read one byte from the serial line have failed. The attempt counter resets if at least one byte is detected. I feel this might be my best choice along with using better VTIME and VMIN settings, but I'm not sure.
I just need a function that can spend very little time (many microseconds at most) to detect when data isn't being sent on the serial line by any device.
The only solution I feel I have at this time is to create more hardware which collects the serial data from the PC, buffers the data and then the hardware detects when the serial line is free before sending data to the remote unit but I don't want to waste more circuit boards and parts. I'd much rather have a software solution (in linux).
Any ideas?
And for clarity, the remote unit uses an 8052 based microcontroller (model at89S52) with a UART having only a 1-byte cache and my computer uses a 16550 UART and I think that has a 14 or 16 byte cache.
UPDATE
I tried another idea which is somewhat better. It seems to detect changes in when the line is free or not free in 2mS intervals but I'm trying to detect more at the microsecond level.
This is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
int main(int argc, char* argv[]){
int fd=open("/dev/ttyS0",O_NOCTTY | O_RDONLY | O_SYNC);
if (fd < 0){
printf("Error\n");
return -1;
}
long v=1;
if (argc > 1){
v=strtol(argv[1],NULL,10); //Custom inter-string timeout
}
struct timeval t={0,5000}; //set initial timeout to 5mS
int gotsz=0; //characters received total
int bpsz=100000; //Most # characters we want
unsigned char b[bpsz]; //characters
unsigned char* bp=b; //Active pointer to characters
bpsz--; //We don't want to wreck memory that isnt ours
fd_set f;
while(1){
FD_ZERO(&f);
FD_SET(fd,&f);
int ret=select(fd+1,&f,NULL,NULL,&t); //check at 5mS intervals for anything
if (ret < 0){
printf("Error\n");
return -1;
}
if (ret != 0){
break; //Stall until anything is received.
}
}
while(1){
FD_ZERO(&f);
FD_SET(fd,&f);
int ret=select(fd+1,&f,NULL,NULL,&t);
if (ret < 0){
printf("Error\n");
return -1;
}
if (ret == 0){
break; //Only exit loop when there are no more characters in a short time
}
int sz=read(fd,bp,bpsz); //fill up buffer advancing start pointer (saves use of strcpy)
gotsz+=sz;
bp+=sz;
bpsz-=sz;
t.tv_usec=v;
}
close(fd);
//print all that we got in hex
bp=b;
printf("Got: ");
int n;
for (n=0;n<gotsz;n++){
printf("%X",*bp);
bp++;
}
printf("\n");
printf("Intermediate Timeout %ld uS (%ld mS)\n",v,v/1000);
return 0;
}
I feel now I need to create some sort of global system lock each time I want to read data from the serial port in order to sense when data is read. By that, I mean if for example I use my computer for multiple purposes like web browsing and playing music in background or watching videos, and I run my program, I think by making my program freeze all of the above mentioned processes just to read serial port data will give the CPU enough time to process the serial port data. Then when my program ends, then the processes can resume.
Any idea how I can accomplish that?