How exactly does Serial.setTimeout() work?

Folks:

Can somebody please help me to understand how Serial.setTimeout() is supposed to work? I thought that the following code should time out without user input after 100ms, but apparently that's not the case.

Thanks!

void setup() {
  char byteRead[1];

  Serial.begin(115200);
  Serial.setTimeout(100);

  int numBytes = 0;

  Serial.println("Please enter a character (100ms timeout)");

  while (!Serial.available());
  numBytes = Serial.readBytes(byteRead, 1);

  Serial.print("|"); Serial.print(byteRead[0]); Serial.print("| Number of bytes read: "); Serial.print(numBytes);
}

void loop() {
}

How do you get past

  while (!Serial.available());

?

According to https://www.arduino.cc/en/Serial/SetTimeout

Serial.setTimeout() sets the maximum milliseconds to wait for serial data when using Serial.readBytesUntil(), Serial.readBytes(), Serial.parseInt() or Serial.parseFloat(). It defaults to 1000 milliseconds.

... but of course you have to get to one of these calls first.

Thank you for the incredibly snide and useless comment. Here is the same code which does not use a one-byte character array and reads three bytes, just in case the cr/lf matters. It still does not work.

#include <Arduino.h>

void setup() {
  char byteRead[10];

  Serial.begin(9600);
  Serial.setTimeout(100);

  int numBytes = 0;

  Serial.println("Please enter a character (100ms timeout)");

  while (!Serial.available());
  numBytes = Serial.readBytes(byteRead, 3);

  Serial.print("|"); Serial.print(byteRead[0]); Serial.print("| Number of bytes read: "); Serial.print(numBytes);
}

void loop() {
}

@vaj408, that chunk of code basically gets rid of whatever random noise is hanging out on the Serial bus when it's initialized. Without it, random spew is read by the first attempt to read from Serial.

Nonsense. It just hangs there waiting for a character to arrive. It does not get rid off anything.

I suggest that you search for the serial input basics thread to get some ideas how to properly read serial input.

To answer the original question

Every time a byte is received, a timing is (re)started. If no new byte is received within the specified time, the functions mentioned in the quote in reply#1 end and return to the calling function.

@sterretje

I appreciate your help with this issue. I know how to properly read serial input.

I'm sorry if my explanation of what that line of code above did is wrong. My description of it is based on empirical evidence of a problem that it solved based on help I received on the #arduino IRC channel on freenode. It is not based on reading the underlying implementation of anything. If I'm wrong, I apologize for providing incorrect information.

Based on the tone of the replies, I think it would be best to close this thread.

So you make me curious what the problem was that can be solved with while (!Serial.available());

Reviving this old thread because I have the same question, which was not really answered.

When using a Serial.something function affected by the Serial.setTimeout setting, what result is returned or what action occurs if the timeout happens before a character is received?

Does the timeout cause an exception which can divert to a function made to handle it, or does it simply return something like -1 if no character was received during the timeout period?

1 Like

Let's say that you send hello followed by world with a 5 second gap. If the timeout is set to 2 seconds and you use a function that uses the timeout, you will only receive hello.
That is, if you called the function before sending the data.

The next time you use the function, you will receive world plus anything else that was possibly received in the meantime.

No, there is no exception that you can catch; just don't use those functions if you have to rely on solid communication.

1 Like

ButchAnton:
Folks:

Can somebody please help me to understand how Serial.setTimeout() is supposed to work? I thought that the following code should time out without user input after 100ms, but apparently that's not the case.

Why not look in the code - seriously this is an open source project, you have all the source at your disposal.

From what I remember the timeout is used by some, not all, of the stream functions - checkout Stream.cpp
in your distribution.

Maybe I'm going at this the wrong way.

In the scenario described, the code is receiving single raw bytes one at a time. Even if there are more in the buffer, it only reads one at a time. The code will initially look forever for the 'start' character and if it receives any other character in the meantime then it will discard that character and carry on looking for the start character.

Once the start character has been detected the code then needs to carry on receiving single raw bytes for as long as they continue to arrive within a set time interval. If the time since the last received byte exceeds that set interval, that is how the code determines that the sender is not going to send any more.

I want to do it this way because the material being received contains no information about its length / size and no special 'end of stream' marker character - the final byte of incoming information is equally likely to have any value from 0x00 to 0XFF. The only thing which makes the last character unique is that it will not be followed by any more characters, so I want to determine that by timing the interval since the last character was received. If no more appear to be coming, then the receive phase of the operation ends

If I were going direct to the hardware on Arduino or on any other processor like a PIC I would do something like:

(Pseudocode)

uiTimeOutValue=500mS

uiAddress=0

Read serial input bytes until 'start' character is received.
Store it in RAM location 'uiAddress'
uiAddress=uiAddress+1

Load hardware timer 'HardwareTimer' with value equivalent to uiTimeOutValue      // (assume it is a downcounter)

while(HardwareTimer >= 0)
{
if there is a serial byte waiting to be received
    {    
    Receive and store it in RAM location 'uiAddress'
    uiAddress=uiAddress+1
    Re-load HardwareTimer with uiTimeOutValue
    }
}

All data now received. Begin working through the RAM from address 0 and processing the received data

You can assume for the purposes of the Pseudocode above that the RAM storage into which the received characters is being placed is limitless. It nearly is, because it is a 32K * 8 SRAM. The input data will never get close to filling all of that up. Several Kbytes at the most.

Unless things have changed, I'm not sure that the feature set of Arduino provides for the loading, running and reading of an independent hardware timer such that you can start a timer running, go away and do a few other things and then come back and interrogate the timer to see how much time has elapsed since you started it. Am I wrong about that? Can I do this using 'pure' Arduino or will I need to go directly to the timer registers? I would rather not do that as it will make the code specific to whatever platform I write it for.

I should say that I have no control over the format of the data being received, it is what it is, so I can't change it to make it easier to handle.

Let us study the following codes to see how the Serial.setTimeout(); instruction works:

char myData[5];
void setup()
{
   Serial.begin(9600);   //1:
   Serial.setTimeout(5000);    //2: 5000 ms = 5 sec timeout period; test purpose
}

void loop()
{
     byte n = Serial.available();  //3:
     if(n != 0) //4:
     {           
         byte m = Serial.readBytesUntil('\n', myData, 5);  //5:
         myData[m] = '\0';  //6:
         Serial.print(myData); //7:
     }
}

1. After uploading the above sketch, set the 'Line ending tab' of the Serial Monitor (Fig-1) at 'No line ending' option. Bring the focus of the cursor at the InputBox; but, do not enter any character and do not click on the Send button.


Figure-1:

2. As no character has arrived at the UNO, the codes of lines-5 to 7 of the loop() function are not executed. The MCU is just looping in the loop() function.

3. Enter the character A in the InputBox and then click on the Send Button and then do not enter any character/click on Send button within next 5-sec period. Check that A has appeared on the OutputBox of the Serial Monitor at about 5-sec later. Why is 5-sec delay? The MCU/sketch has waited to see if any character is yet to come from the Serial Monitor within the next 5-sec period (the timeout period).

4. Close the Serial Monitor and re-open it. Enter A and click on the Send button. Wait for about 1 or 2 sec period; enter B and click on the Send button and then do not enter any character/click the Send button. Check that AB has appeared on the Serial Monitor at about 5-sec later from the entry point of character B.

5. In Step-4 when the character A has arrived/retrieved/saved, a 5-sec timer is automatically triggered. Say, B has arrived after 2-sec time; then, the same timer is triggered again to last for next 5-sec. The timer behaves like an electronic re-triggerable one shot timer.

Hope, the working mechanism of the setTimeout() method is understood.

3 Likes

GolamMostafa, I thank you for that graceful explanation and for taking the time to illustrate the features of the serial monitor so clearly. I will try this experiment and report back.

Because of the indeterminate message size and the transfer to external SRAM, I would not use readBytes(), but rather set a millis() timer for a timeout on the reading. Use a boolean control variable receiveUntilTimeout initialized to false.

Something like this

if (Serial.available() > 0 && !receiveUntilTimeout)
  {
    receivedChar = Serial.read();
    if (receivedChar == startMarker)
    {
      receiveUntilTimeout = true;
      timeReceived = millis();
    }
  }

  if (receiveUnilTimeout)
  {
    if (Serial.available() > 0)
    {
      receivedChar = Serial.read();
      //store it in RAM location 'uiAddress'
      //uiAddress=uiAddress+1
      timeReceived = millis();
    }
    else //Serial.available == 0 and nothing to read
    {
      if (millis() - timeReceived >= timeOut)
      {
        receiveUntilTimeout = false;
        //terminate reading process
      }
    }
  }

Sterretje: Thank you also for your contribution.

cattledog, I think I've come to a similar conclusion having only just discovered the millis() function / feature.

Strictly speaking, it needs a bit more work to deal with the possibility that the millis() timer could possibly overflow and go back to zero between reads, but as I understand it that is only likely if the Arduino has been powered for 40+ days. Your code example illustrates the basic method perfectly though, thank you.

A thought: Is it possible to write a value to the millis() timer? To zero it, for example?

siriushardware:
A thought: Is it possible to write a value to the millis() timer? To zero it, for example?

After uploading a sketch in the UNO, a 4-byte wide space is allocated to register the elapsed time at 1-ms interval since uploading. If anybody knows a variable that holds the beginning address of that space, then it might be possible to reset the timer on the fly. But, I think it is a system feature and must not be disclosed to public except the developers.

siriushardware:
Strictly speaking, it needs a bit more work to deal with the possibility that the millis() timer could possibly overflow and go back to zero between reads, but as I understand it that is only likely if the Arduino has been powered for 40+ days. Your code example illustrates the basic method perfectly though, thank you.

If you use unsigned variables, there will be no issue when millis() overflows.

GolamMostafa:
After uploading a sketch in the UNO, a 4-byte wide space is allocated to register the elapsed time at 1-ms interval since uploading. If anybody knows a variable that holds the beginning address of that space, then it might be possible to reset the timer on the fly. But, I think it is a system feature and must not be disclosed to public except the developers.

Yes, they must never disclose the contents of wiring.c to anyone. :slight_smile:

I have explored the contents of wiring.c file. Is it possible to get help from this file in order to write a simple subroutine that may be called upon to reload the system timer whose current content is readable by millis() function? Would appreciate to receive some directions.

It should NEVER be necessary to zero the millis() timer. As long as ALL of your time-related variables are declared as unsigned long, and as long as you use subtraction (NEVER addition), you can find out how much time has elapsed by taking the difference between the current millis() and the value that was saved at the beginning of the elapsed time.

This works EVEN IF millis() has wrapped around because almost 50 days have elapsed.