Recommendation Needed to handle serial timeout

Hi All

I've gone through the Serial Input Basics thread and a couple of others, still have not find the optimal solution for my problem. Hope some guru out there can give some guidance

here is the problem:
I have an Arduino board (ESP32) talking to a LTE modem via serial,
I would like to send command to the modem and wait for a response or a timeout.

right now, if I use the simple serial.read(), it does not handle a timeout and since it is single thread system, I can't stop it using another thread when timeout reached.

if I use serial.readstring() or readbytes(), it insist that read until the timeout is reached, which I would like to exit as soon as response is obtained.

if I use serial.readstringutil, or readbytesuntil, it require a specific end char to stop, in my case, the response is not quite unified with the same end char, the ending condition differs depends on the command I send to the modem. so whenever the response comes in, I need to trigger another matching function (varies depends on the initial command) to determine if a proper response is received.

I am sure some of you have met this issue before, kindly share your thoughts on this, or refer me to some other post/page which I may not have read .

Only read if Serial.available indicates that there is something to read. Use a global (or static) variable to keep track when the last character was received. Something like

static uint32_t lastReceiveTime;

if(Serial.available() > 0)
{
  // read and add to buffer
  ...
  ...

  // update the last time that a byte was received
  lastReceiveTime = millis();
}

if(millis() - lastReceiveTime >= yourTimeoutInMilliseconds)
{
  // handle the time out
}
else
{
}

The if can be a while as in the Serial Input Basics thread.

Along similar lines:

#define MSG_TIMEOUT     1000ul      //message character timeout of 1-sec
#define BUFFER_SIZE     64          //or whatever...

byte
    rxBuffer[BUFFER_SIZE];

void loop()
{
    static int
        idx = 0;
    static bool
        bMessageInProgress = false;
    static unsigned long
        ulMsgTimeout;

    //if a message is in progress...
    if( bMessageInProgress )
    {
        //check to see if we've timed-out waiting for the next char
        if( millis() - ulMsgTimeout >= MSG_TIMEOUT )
        {            
            //timeout occurred; handle message timeout
            //...
            
            //and reset the progress flag and buffer index
            bMessageInProgress = false;
            idx = 0;
             
        }//if
        
    }//if

    //if there are any serial characters available process them now
    if( Serial.available() )
    {
        do
        {
            byte ch = Serial.read();

            //each character we receive we set the message in progress flag, and
            bMessageInProgress = true;
            //reset the message timeout timer
            ulMsgTimeout = millis();

            //receive the character into the receive buffer and bump the index
            rxBuffer[idx] = ch;
            if( idx < (BUFFER_SIZE-1) )
                idx++;
                
            //if this is the end of the message...
            if( ch == END_OF_MESSAGE )
            {
                //process message
                //...

                //clear the progress flag and zero the index ready for the next message
                bMessageInProgress = false;
                idx = 0;
                
            }//if
            
        }while( Serial.available() );
        
    }//if

}//loop

yes Arduino is single thread, but if you code properly you should be able still handle many things at once.
when serial info reaches the arduino it is very fast to read from the registers. if you have other things your sketch is doing then you should never make your main loop wait. instead push your reads into a buffer or byte array untill it is complete and carry on in your main loop. your coding system should be structured so that the wait between request and responce does not cripple your program.

if you are sending data that is important then there are several simple techniques for confirmation of your data depending on how important the responce is, how long the data is and how reliable the connection is. Blackfin mentioned one techinique of an ending signature for your data that would trigger your code to read the buffer for as a complete response. if you also add a beggining signature as well that resets the index then you can create even more reliality. if thats not good enough then there is data redundance checks and headers that include length.

but one way or another your code should keep on keeping on untill it gets a confirmed complete responce. the guys above gave wonderull example to not freeze your thread between transmitions and how to record millis() to inform the user that they didnt get anything.

anyways the more info you give the more people can help you

Hi sterretje, Blackfin, taterking

thanks for the suggestion and sample codes, this is a much better start,
however, I do have another follow up question though,
I actually want my code to wait for the response or timeout before it move to the next step.
how do I make the main code wait for the response?

Justin

Sounds like you might be looking at a state machine to handle the different states (steps) of your application.

Show your current code so we can understand what is needed.

Hi Sterretje,

I've managed to fix it, by having a while loop outside of the serial.avaialble loop and timeout checks to check if the state is completed.

thanks :slight_smile:

Justin

zjustin:
here is the problem:
I have an Arduino board (ESP32) talking to a LTE modem via serial,
I would like to send command to the modem and wait for a response or a timeout.

Please, be specific:

Say, you are sending this command byte (0x03) from your ESP32 to your Modem. What response (in the form of a data byte -- say: 0x06) do you expect to receive from the Modem? You must know the time (the timeout period) within which the response from the Modem must arrive to ESP32. The control will go to the next label under the satisfaction of one of the following two conditions:

  1. The response (correct or incorrect) is received before the set timeout;
  2. The timeout period expires before receiving the response.

Codes could be created to implement the above strategy.

Hi GolamMostafa

thanks for the reply, yes, taken the exact approach now as you mentioned.

zjustin:
thanks for the reply, yes, taken the exact approach now as you mentioned.

Please, tell me the value of the response data and the timeout period; also, post your codes.

don’t quite understand what you are asking for for the value of response data and timeout number since it is different depends on the situation

// below code will end whenever expected result is received or timeout is reached.

while (waitReply==true){
      while (serial.available()) {
         char c = (char)serial.read();
         // reading the response save it to result

       }
       
       // code to handle the result, check it expected response is received. 
       // if expected result received, 
       //       waitReply = false;


       // below to check time out reached, if so, 
       if (millis() >= QueryTimeout){
         waitReply=false;
       }
    }

zjustin:
I've managed to fix it, by having a while loop outside of the serial.avaialble loop and timeout checks to check if

while-loops are hardly ever the correct solution to a problem in Arduino land. It becomes a problem once you want to do things while waiting. E.g. blink a led, monitor a sensor or button etc. Remember that loop() is called very often so basically provides the while; the condition can be implemented in an if statement inside loop().

It might not be a problem now but once you have to extend your code with other stuff, you will have to rewrite it all unless you want to end up with an incomprehensible mess.

i would never reccomend this but to answer your question…if you really want to wait…

while(Serial.available()<lengthOfExpectedResponce){}
// continue here