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):
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.
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.
#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);
}
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.
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;
}
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.
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)