How to deal with software serial RX buffer overflow [optimisation][alternatives]

Board: Arduino UNO
Baud: 9600 For both Serial and SoftSerial


I cannot process the software serial input (AltSoftSerial) fast enough, such that my RX buffer overflows and I lose data.


This function can't compute fast enough to keep up with the serial input:

It flushes the RX buffer until <CR><LF>OK<CR><LF> (when stop_at_ok == true) or until has been seen.

serial_success is just {Serial.println("\u001b[32m\u001b[1m" + message + "\u001b[0m");}, which prints a green message.

int flush_buffer_until(String until, bool stop_at_ok = false)
{
  int u = 0;                              // Until message incremental counter
  int8_t o = 0;                         // OK message incremental counter
  const int until_len = until.length(); // Length of until string
  serial_success("FLUSH UNTIL DEBUG STARTED");
  while (true)
  {
    while (hat_port.available() > 0)
    {
      Serial.print(String(hat_port.available()) + ","); // debug message
      char in = hat_port.read(); // Incoming byte
      if (in == until[u])
      {
        u++; // End of message is approaching
        o = 0; // We can stop looking for OK if we were
      }
      else if ((in == ok_text[o]) && stop_at_ok)
      {
        o++; // We're aproaching the OK message
        u = 0; // We can stop looking for UNTIL if we were
      }
      else
      {
        // Neither OK or until were seen, so reset their counters
        u = 0;
        o = 0;
      }
      if (u >= until_len)
      {
        serial_success("Hit until");
        return 1;
      }
      else if (o >= ok_len)
      {
        serial_success("Hit ok");
        return 2;
      }
    }
  }
  return -1;
}

When running with the debug messages, i get the following output (with timestamps):

The buffer approaches it's max (80), and i lose data towards the end.

I lose the until or OK text, so the function will never terminate .


  • How do I improve this function to run faster, so I can keep up with the RX buffer
  • Or is there any alternative to my method of clearing the buffer with 2 exit strings?

Please state which Arduino you are using, and post ALL the code.

If you are using an AVR-based Arduino, avoid using Strings. They cause memory problems and program crashes. Strings also add considerably to the time required to process input, and are not necessary for this task.

The code is very long, Would you like me to upload a demo script?

Should I use char arrays instead?

We always appreciate a minimal but complete program that demonstrates the problem.
But "very long" code is also welcome.

By all means use zero-terminated character arrays (C-strings), and their associated functions in <string.h>. They are much, much more reliable and faster than the String class.

Nevertheless, C-strings must be used with care, paying special attention to the possibility of buffer overflow.

Hint: memcmp(), strstr(), etc. can be used to directly identify this character string <CR><LF>OK<CR><LF> in a buffer.

What’s your baud rate for both Serial and the AltSoftSerial ports

9600 for both

Demo:

#include <AltSoftSerial.h>
#include <Arduino.h>

#define baud 9600 // Must be same on mock and serial

const String ok_text = "\x0d\x0a\x4f\x4b\x0d\x0a"; // OK message
const int8_t ok_len = ok_text.length();

AltSoftSerial hat_port;

int flush_buffer_until(String until, bool stop_at_ok = false)
{
    int u = 0;                            // Until message incremental counter
    int8_t o = 0;                         // OK message incremental counter
    const int until_len = until.length(); // Length of until string
    Serial.println("FLUSH UNTIL DEBUG STARTED");
    while (true)
    {
        while (hat_port.available() > 0)
        {
            Serial.print(String(hat_port.available()) + ",");
            char in = hat_port.read(); // Incoming byte
            // Serial.print(in);
            if (in == until[u])
            {
                u++;   // End of message is approaching
                o = 0; // We can stop looking for OK if we were
            }
            else if ((in == ok_text[o]) && stop_at_ok)
            {
                o++;   // We're aproaching the OK message
                u = 0; // We can stop looking for UNTIL if we were
            }
            else
            {
                // Neither OK or until were seen, so reset their counters
                u = 0;
                o = 0;
            }
            if (u >= until_len)
            {
                Serial.println("Hit until");
                return 1;
            }
            else if (o >= ok_len)
            {
                Serial.println("Hit ok");
                return 2;
            }
        }
    }
    return -1;
}

void setup()
{
    Serial.begin(baud);
    while (!Serial)
    {
    } // Wait for serial to connect
    hat_port.begin(baud);
}

void loop()
{
    // Pretend we are flushing to something
    int response = flush_buffer_until("test", true);
}

I suspect the problem has to do with memory errors associated with the String class.

Get rid of them.

Not sure what you are expecting to happen, you are printing at a minimum two characters, and most of the time three characters, for each character read from the input. You will never be able to keep up with a continuous stream of input data.

^That too^, but the String processing won't work, either.

This seems to have fixed it. Thank you.

I replaced them with C-strings.

It happens without any printing as well

My code was also flawed, which I changed to:

(This is the working function I use now)

int flush_buffer_until(const char UNTIL[], const int UNTIL_LEN, bool stop_at_ok = false)
{
  // TODO Add timeout to return -1

  int u = 0;    // Until message incremental counter
  int8_t o = 0; // OK message incremental counter
  char in;      // Incoming byte

  if (UNTIL == OK_TEXT)
  {
    // Override for when you only want to stop on OK
    stop_at_ok = false;
  }

  // If UNTIL_LEN <= 0 we have an issue with passing arguments
  while (UNTIL_LEN > 0)
  {
    while (hat_port.available() > 0)
    {
      in = hat_port.read(); // Incoming byte
      if (in == UNTIL[u])
      {
        u++;   // End of message is approaching
        o = 0; // We can stop looking for OK if we were
      }
      else if ((in == OK_TEXT[o]) && stop_at_ok)
      {
        o++; // We're approaching the OK message
        u = 0; // We can stop looking for UNTIL if we were
      }
      else
      {
        if (in == OK_TEXT[0] && stop_at_ok)
        {
          // We stopped the last sequence of OK, but we may have started a new sequence
          o = 1;
          continue;
        }
        else if (in == UNTIL[0])
        {
          // We stopped the last sequence of UNTIL, but we may have started a new sequence
          // We don't continue here in case UNTIL is of length 1
          u = 1;
        }
        else
        {
          // Neither OK, UNTIL or their starting letters were seen, so reset their counters
          u = 0;
          o = 0;
          continue;
        }
      }
      if (u >= UNTIL_LEN)
      {
        return 1;
      }
      else if (o >= OK_LEN)
      {
        return 2;
      }
    }
  }
  return -1;
}

This was also an issue, but is now fixed.

I use this type of code to await for a particular message. Basically I store in a circular buffer what I get from Serial and at every new byte received, I check if the circular buffer matches the expected message.

may be that will give you a base to build from?

Thank you, but the code I have now works fine, I'll edit my message as it seems to give the impression that it doesn't work still.

The algorithm you use is not perfect

Say you are waiting for AABC
You get A ➜ OK
You get A ➜ OK
You get A ➜ not OK you restart waiting for A
You get B ➜ not OK you restart waiting for A
You get C ➜ not OK you restart waiting for A

But if you look closely the stream you got was AAABC and so you should have got a match

That’s why remembering the last N bytes if you are trying to match a pattern with length N is important. (May be it does not matter in your use case and so you are fine but you need to know it’s not foolproof)

1 Like

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