Get string from serial port with timeout

Hi. I need to read a string from the serial port into a buffer. I intended for the function to time out after a set length of time in case there is no data available and if so, it returns false.
When there is a string in the buffer, it returns true.
The problem is, the function is blocking my main loop, but I want to be able to check buttons, display things, etc. while the serial function is running if it returns false.

Here's what I have right now:

bool readResponse(char *buffer, size_t bufferSize, uint32_t timeout) {
  size_t idx = 0;
  uint32_t startTime = millis();

  while (idx < bufferSize - 1 && (millis() - startTime) <= timeout) {
    while (Serial.available() > 0) {
      char c = Serial.read();
      if (c == '\r') {
      buffer[idx] = '\0';
      return true;
      } else if (idx < bufferSize - 1) {
      buffer[idx] = c;
      idx++;
      }
    }
  }
  buffer[idx] = '\0';
  return false;
}

The main loop of my program uses this function for input from the serial port and based on which state the main switch statement is in, the string will be used as input to the program.

So I'm basically doing:

void loop() {
    checkButtons();

    if (readResponse(buffer, 64, SERIAL_TIMEOUT)) {
        // do something with the input
    }
}

There must be a better way than those two while loops in the readResponse function.
Can anyone suggest an alternative?
The existing code is written for use with char arrays. I didn't want to use String classes for memory fragmentation reasons.

As you point out, the function is blocking, so move the body of the function code into the main loop.

Most people choose to terminate the input string and take action when a CR or other suitable line terminator is reached, rather than use a timeout. But use a timeout if you wish.

The Serial Input Basics tutorial might be helpful.

Another approach is to rewrite the function so that it returns immediately if no character is waiting to be added to the input string, or continues, then returns a value that indicates whether an entire line was successfully input, or a timeout occurred. An example is the TinyGPS++ library, which reads and interprets a GPS serial stream using non-blocking code.

1 Like
bool readResponse(char *buffer, size_t bufferSize) {
  char c;
  if (Serial.available() > 0) {
    for (int i = 0; i < bufferSize-1; i++) {
      c = Serial.read();
      if (c == '\r'||c == '\n') {
        if (Serial.available() > 0)Serial.read();
        buffer[i] = '\0';
        if (i)return true;
        else return false;
      }
      else {
        buffer[i] = c;
        buffer[i+1] = '\0';
      }
    }
  }
  return false;
}
2 Likes

@jremington: Thanks. I don't really want to add the body of the function to the main loop, though. I'm trying to keep the whole thing modular both for code re-use and ease of maintenance in the future.
I'll bookmark the link though.
Thank you for your advice. I'd forgotten about the TinyGPS++ library.

@koloha: Thanks. That looks like just what I was after.
You took out those two horrible while loops and made the whole thing non-blocking.
The function returns true if there's data or false if there's not. Perfect!
I'm not sure why I had a timeout really. It's not really needed is it?
The only thing that's a bit confusing is the additional Serial.read after you check for CR/LF is that to make sure those chars don't wind up in the buffer the next time the function is called?
Thanks again.
H

Hello horacewarblehat

Take a view into the IDE examples to find the SerialEvent example.

Check it - Try it.

exactly :+1:

Try it this way. serialStr runs the Serial port for you and calls a function you choose when a string arrives. Non blocking.

-jim lee

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.