2

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?

dandan78
  • 13,328
  • 13
  • 64
  • 78
Mike -- No longer here
  • 2,064
  • 1
  • 15
  • 37
  • I think you check RTS/CTS pins on the modem. I've never had to do it, so I don't have the terminal-based code to offer, though. See questions like [What is the difference between DTR/DSR and RTS/CTS flow control?](https://stackoverflow.com/q/957337/608639) – jww Jun 06 '19 at 03:32
  • The sad truth is in my setup there is no hardware flow control available in the wireless modules. For reference, the modules I'm using are the model HM-TRP – Mike -- No longer here Jun 06 '19 at 15:02
  • Well, earlier searches for the at89S52 said folks were using flow control. You have not referenced the manual. It looks like your next step is, go to the manual and see what the manufacturer recommends. You might also do some reading on similar protocols, like Carrier Sense Multiple Access with Collision Detection (CSMA/CD) used in ethernet. – jww Jun 06 '19 at 15:07
  • Yes people can invent flow control with the at89S52 microcontroller, but I'm connecting my UART on that microcontroller to a wireless module which supports no hardware flow control so therefore hardware flow control is not an option to me. – Mike -- No longer here Jun 06 '19 at 15:56
  • I'm assuming you don't have the option to modify the protocol on both sides, otherwise it would be easy to add a character at the end of each message to signal the end of the frame and probe each byte on the other side until you get a match on that control character... – Marcos G. Jun 06 '19 at 16:22
  • Does your remote device transmit on its own? It sounds like it does, but the protocol has something to do with it, e.g. if the protocol is a request-response then it is half-duplex and you don't have to worry about this. – rm5248 Jun 07 '19 at 01:10
  • I can modify both sides but I have the hardware side to continuously transmit data with only a 30mS break in between transmissions. It's the opposite of request-response. Basically the unit acts as a central station to ping all wirelessly connected devices with new data, and if the wireless device being addressed needs to make a request, then it can within the 30mS window in which the transmitter isnt transmitting.. – Mike -- No longer here Jun 08 '19 at 02:13

0 Answers0