How do I detect if I'm receiving serial data.

Hi there.

I’ve googled and googled and haven’t found any solution for my problem. I hope someone in here knows how to do it the right way.

The problem is, that I need to know if somebody is sending me data, before I send data back. In other words, a simple kind of collision detection.

In psudo code:

#define LINE_CLEAR_SPAN 500

void sendData() {
  long timeSinceLastByteReceived = SomeMethodToGetTheTimeSinceLastByteReceived();
  while (timeSinceLastByteReceived < LINE_CLEAR_SPAN) 
  {
     delay(TIME_OUT);
     timeSinceLastByteReceived = SomeMethodToGetTheTimeSinceLastByteReceived();
  }
  // ok clear to send!
  Serial.write("yeah!");
}

But how do I get the information from Serial? How do I implement the “SomeMethodToGetTheTimeSinceLatByteReceived()”?

I thought about some kind of interrupt that resets a counter each time the Serial receives a byte. But how do I attach an interrupt handler to the serial device?

If nobody guessed it by now, I need to say, that i’m a totally newbie in embedded programming. So please be gentle!

Thanks!

Why an interrupt? Could you use "Serial.available"?

He - well yeah… I suppose I could. But then I can not know how long I need to wait before sending.

It’s okay that something is in the buffer as long as it’s received longer ago than “time between messages”. But then again - maybe it’s my design that sucks then. I’m used to use threads, mutex’es and managed garbage collection.

But then I can not know how long I need to wait before sending

Why not?

As it is now - the logic in my program looks like this.

void loop() 
{
  while (Serial.available() > 0) 
  {
    //handle the incoming data.
  }
  //do some plumbing
  //[...]
  sendMessage();
}

If I understand correctly - the uart will/could still receive data while I'm doing other stuff in my loop. When I send my message I need to be sure that I send the message at least let's say 30 ms after the last byte has been received/buffered by the uart. In the time span between "Serial.Available == 0" and I call "SendMessage()" data could have been received and if I send my message without waiting, contention occurs.

When I send my message I need to be sure that I send the message at least let's say 30 ms after the last byte has been received/buffered by the uart.

Why?

Data arrives on the serial port when it wants to. You have no way of knowing when the interrupt that processed the serial data occurred.

At best, you could periodically check the amount of data in the buffer. Store the time you do that. At some future time, if the amount of data is still the same, and your desired interval has elapsed, send the message.

PaulS:

When I send my message I need to be sure that I send the message at least let's say 30 ms after the last byte has been received/buffered by the uart.

Why?

Because my serial port is connected to a bus. On the bus datagrams between 4-8 bytes is sent. If somebody else is sending a datagram on the bus, I'd ruin it by starting sending my datagram when the other part has sent f.x. two bytes out of four. Thus I'll have to wait until the other part has finished sending his datagram to minimize the risk on contention.

PaulS: Data arrives on the serial port when it wants to. You have no way of knowing when the interrupt that processed the serial data occurred.

I just started all this electronics stuff so please forgive me. But isn't it somehow possible to connect the rx-line both to the rx-pin and to another pin (through some resister) on the arduino and detect when it goes low? Then I could attach an interrupt routine and reset some kind of timer each time the rx-line goes low.

volatile long lastByteReceived;

//called when the interrupt occurs.
void onRxGoesLow() 
{
  lastByteReceived = millis();
}

//called by my send routine.
byte timeSinceLastByteReceived() 
{
  return millis() - lastByteReceived;
}

PaulS: At best, you could periodically check the amount of data in the buffer. Store the time you do that. At some future time, if the amount of data is still the same, and your desired interval has elapsed, send the message.

Okay.. Do you know where I can read about accessing the serial buffer? (I don't know what I should search for).

Could you give a view of the bigger picture - what are you trying to do?

Do you know where I can read about accessing the serial buffer?

Serial.read will return the next character available, if there is one, or return -1 if there isn't. Serial.available will tell you how many characters are in the serial buffer (including zero if there aren't any)

What more access do you need?

AWOL:

Do you know where I can read about accessing the serial buffer?

Serial.read will return the next character available, if there is one, or return -1 if there isn't. Serial.available will tell you how many characters are in the serial buffer (including zero if there aren't any)

What more access do you need?

Not much more I guess. I'll check tonight if the following works. I'm not sure if I like the solution, but if it works it's okay for now. Thanks.

#define CONTENTION_TIMEOUT 30
void WaitForBusClear() 
{
  if (Serial.available() > 0) 
  {
    int count = Serial.available();
    delay(CONTENTION_TIMEOUT);
    while (Serial.available() > count) 
    {
      count = serial.available(); 
      delay(CONTENTION_TIMEOUT);
    }
  }
}

It sounds like you want to detect an idle bus. For my money the best way to do this is start a timer and reset it every time there is an edge on the RX pin. If the timer ever reaches 0 nobody has transmitted for N time and you can go.

BUT

If you have more than one node doing this (and you will or there's not point in the first place) you will have a race condition.

How will you know that another node hasn't done exactly the same thing? You have to detect bus clashes and try again after a random period.

This is a multiple access protocol and it needs a lot of deep thought.


Rob

I’m not sure if I like the solution, but if it works it’s okay for now.

I know I don’t like it. Something like this maybe:

int oldCount = 0;
int newCount = 0;
unsigned long lastSerial = 0;

void loop()
{
   newCount = Serial.available();
   if(newCount != oldCount)
   {
      oldCount = newCount;
      lastSerial = millis();
   }

   // Rest of loop
}

Then a function to send data

void sendFunc(/*whatever args are needed*/)
{
   unsigned long currTime = millis();
   // Diddle until enough time has elapsed
   // since last serial data received
   while(currTime - lastSerial < CONTENTION_TIMEOUT)
   {
      // Check for new serial data
      newCount = Serial.available();
      if(newCount != oldCount)
      {
         oldCount = newCount;
         lastSerial = millis();
      }
      currTime = millis();
   }
   // When we get to here, no serial data was 
   // received in the last CONTENTION_TIMEOUT
   // milliseconds

   // Send the data...
}

Graynomad: It sounds like you want to detect an idle bus. For my money the best way to do this is start a timer and reset it every time there is an edge on the RX pin. If the timer ever reaches 0 nobody has transmitted for N time and you can go.

Well that's exactly what I've tried to explain you all that I needed! :) If then somebody could tell me HOW I could detect an edge on the RX pin (how would I wire it up) and how to attach an interrupt handler method. (please)

Graynomad: BUT

If you have more than one node doing this (and you will or there's not point in the first place) you will have a race condition.

How will you know that another node hasn't done exactly the same thing? You have to detect bus clashes and try again after a random period.

This is a multiple access protocol and it needs a lot of deep thought.

I'm aware of the risks. Garbage packages will be discarded (and is expected). The timeout value will be arbitrary, and I think this would solve most clashes.

PaulS:

I'm not sure if I like the solution, but if it works it's okay for now.

I know I don't like it. Something like this maybe:

Thanks! If I can't get the idle detection working, I'll try to work something out based on both mine and your code examples.

PaulS: I know I don't like it. Something like this maybe:

After a second thought.. Would you mind explain to me why you prefer your own solution and don't like mine?

Your solution checks if there is serial data. If not, it returns immediately. You have no idea how long the serial buffer has been empty. Maybe that's OK. Maybe not.

If there is as-yet-unread serial data, it records how much, and waits the entire amount of time defined.

If any data arrives during that time, it again waits the entire amount of time before checking whether new data has arrived.

Between when that function ends and the next function gets called, to actually send the data, more data could have arrived.

Nothing else can happen during those delays (and, yes I recognize that they are short).

Mine, on the other hand, checks on every pass through loop, and records when there is a change in the amount of data.

The delays only occur when there is data to send. My function waits only if there has been activity on the bus, and as soon as the bus has been idle long enough starts to send data.

tell me HOW I could detect an edge on the RX pin (how would I wire it up) and how to attach an interrupt handler method.

You can wire one of the INT pins to RXD and use attachInterrupt().


Rob

Hi Paul.

Thanks for your thorough answer!

PaulS: Your solution checks if there is serial data. If not, it returns immediately. You have no idea how long the serial buffer has been empty. Maybe that's OK. Maybe not. If there is as-yet-unread serial data, it records how much, and waits the entire amount of time defined.

That's okay. As I empty the Serial as the first thing in my loop() (see reply #4 in this thread).

PaulS: If any data arrives during that time, it again waits the entire amount of time before checking whether new data has arrived.

Between when that function ends and the next function gets called, to actually send the data, more data could have arrived.

That's true - but your method wouldn't catch that either. That's why I choose to wait and then check again, before start sending if new data was received since the buffer was emptied.

PaulS: Nothing else can happen during those delays (and, yes I recognize that they are short).

Mine, on the other hand, checks on every pass through loop, and records when there is a change in the amount of data.

The delays only occur when there is data to send. My function waits only if there has been activity on the bus, and as soon as the bus has been idle long enough starts to send data.

You cannot know if data has been received between the first check in the loop() function and the call to the sendmessage() function (the lastSerial variable would still contain the time stamp we polled for serial data, not the time the last data was received). And that's the issue here! Both yours and my solution are workarounds. The right way would be to subscribe to a interrupt that fires when a byte is received - and then update a time stamp. That way a method as shown in my opening post could be used, which is a mix between yours and my attempt to make the workaround :)

Thanks for your contribution so far! When I find the right solution (for me that is) I'll post an update to this thread!

Graynomad: You can wire one of the INT pins to RXD and use attachInterrupt().

Thanks - I will try to work it out..