[solved] MySerial.find()

Hey!

I'm having trouble with function MySerial.find(). I'm trying to parse output of the module ESP 01, after sending AT-commands to it. Programm doesn't seem to meet condition mySerial.find("OK"), though I clearly see A T CR CR LF CR LF O K CR LF characters on the line after sending "AT" to the module.

#include <SoftwareSerial.h>
SoftwareSerial mySerial(10, 11);  // Rx, Tx 

void setup() 
{
  pinMode(LED_BUILTIN, OUTPUT);

  mySerial.begin(115200);
}

void loop() 
{
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  mySerial.flush();
  mySerial.println("AT");

  if (mySerial.find("OK"))
  {
    digitalWrite(LED_BUILTIN, HIGH);
  }

}

Probably I will use workaround with using recieve buffer as in example in topic "Serial Basics" (Serial Input Basics - Programming Questions - Arduino Forum) and find the desired sequence of characters ("OK") in the string. But I want to know why that code doesn't work.
Any help appreciated.

Ok, here's my crappy bloated solution:

void loop() 
{
  delay(500);
  digitalWrite(LED_BUILTIN, LOW);
  mySerial.flush();
  mySerial.println("AT");

  if (findSubstring("OK")) digitalWrite(LED_BUILTIN, HIGH);
  else digitalWrite(LED_BUILTIN, LOW);
}

boolean findSubstring (const char * sub_str)
{
  String str = mySerial.readString(); // read all incoming data 
  int str_len = str.length() + 1;
  Serial.println(str); // display all received data back in serial monitor.
   
  char char_array[str_len];
  str.toCharArray(char_array, str_len); // convert string to array
  
  if (strstr(char_array, sub_str)) return 1; // check if char_array contains sub_str sequence
  else return 0;
}

Now programm do find "OK" sequence in the incoming data from the module. But I personally still want to work with MySerial.find() function. Any luck with it anyone?

It is not a good idea to use the String (capital S) class on an Arduino as it can cause memory corruption in the small memory on an Arduino. This can happen after the program has been running perfectly for some time. Just use cstrings - char arrays terminated with '\0' (NULL).

...R
Serial Input Basics - simple reliable non-blocking ways to receive data.

SoftwareSerial is not reliable at 115200 baud. use 9600 baud

I'm using this code to wait an answer finishing with a specific end marker (a cString not just a char) for a maximum amount of time. (ESPSEPRIAL is whatever stream you are listening from, could be your SoftwareSerial port or if you are on a mega one of the Hardware Serial Port.)

// --------------------------------------
// waitForString wait max for duration ms whilst checking if endMarker string is received
// on the ESPSEPRIAL port returns a boolean stating if the marker was found
// --------------------------------------

boolean waitForString(const char * endMarker, unsigned long duration)
{
  int localBufferSize = strlen(endMarker); // we won't need an \0 at the end
  char localBuffer[localBufferSize];
  int index = 0;
  boolean endMarkerFound = false;
  unsigned long currentTime;

  memset(localBuffer, '\0', localBufferSize); // clear buffer

  currentTime = millis();
  while (millis() - currentTime <= duration) {
    if (ESPSEPRIAL.available() > 0) {
      if (index == localBufferSize) index = 0;
      localBuffer[index] = (uint8_t) ESPSEPRIAL.read();
      deepDebugMessage(localBuffer[index]);
      endMarkerFound = true;
      for (int i = 0; i < localBufferSize; i++) {
        if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
          endMarkerFound = false;
          break;
        }
      }
      index++;
    }
    if (endMarkerFound) break;
  }
  return endMarkerFound;
}

the code uses a circular buffer to compare what is received over the ESPSEPRIAL line with what we expect. The full answer is ignored though, we only care to know if we got the endMarker message.

I use that to see if I get correctly an "OK\r\n" from AT commands for example.

I define those functions:

// --------------------------------------
// espPrintlnATCommand sends an AT command by adding at the end a CR LF
// then it checks if an endMarker string is received on the ESP Serial port
// for max duration ms returns a boolean stating if the marker was found
// --------------------------------------

boolean espPrintlnATCommand(const char * command, const char * endMarker, unsigned long duration)
{
  debugMessage(DOT_STR);
  ESPSEPRIAL.println(command);
  return waitForString(endMarker, duration);
}

for example I would do

const char * OK_STR = "OK\r\n";
....
// connect to my WiFi network and create the TCP server
  if (!espPrintlnATCommand("AT+RESTORE", "ready", LONG_PAUSE)) dontGoFurther("RESTORE failed"); // reset

  if (!espPrintlnATCommand("AT+CWMODE=1", OK_STR, SHORT_PAUSE)) dontGoFurther("CWMODE failed"); //Set the wireless mode to station

  if (!espPrintlnATCommand("AT+CWQAP", OK_STR, SHORT_PAUSE)) dontGoFurther("CWQAP failed");   //disconnect  - it shouldn't be but just to make sure

where LONG_PAUSE, SHORT_PAUSE are defined this way

#define SHORT_PAUSE (1000ul)
#define LONG_PAUSE  (10000ul)

and I have a dontGoFurther() function that ends the program with an error message

// Comment out this line to remove all Debug information
#define debugFlag

#ifdef debugFlag
#define debugMessage(...) Serial.print(__VA_ARGS__)
#else
#define debugMessage(...) {}
#endif

// --------------------------------------
// this will lock your arduino, watchdog might reset
// --------------------------------------
void dontGoFurther(const char * errorMessage)
{
  debugMessage(F("Can't continue: "));
  debugMessage(errorMessage);
  debugMessage(F("\n"));
  while (1); // die here
}

hope this helps

1 Like

Thanks all!!

Juraj:
SoftwareSerial is not reliable at 115200 baud. use 9600 baud

Yup, after setting speed down to 9600 on my ESP 01 MySerial.find() worked as expected.

J-M-L:

// --------------------------------------

// waitForString wait max for duration ms whilst checking if endMarker string is received
// on the ESPSEPRIAL port returns a boolean stating if the marker was found
// --------------------------------------

boolean waitForString(const char * endMarker, unsigned long duration)
{
 int localBufferSize = strlen(endMarker); // we won't need an \0 at the end
 char localBuffer[localBufferSize];
 int index = 0;
 boolean endMarkerFound = false;
 unsigned long currentTime;

memset(localBuffer, '\0', localBufferSize); // clear buffer

currentTime = millis();
 while (millis() - currentTime <= duration) {
   if (ESPSEPRIAL.available() > 0) {
     if (index == localBufferSize) index = 0;
     localBuffer[index] = (uint8_t) ESPSEPRIAL.read();
     deepDebugMessage(localBuffer[index]);
     endMarkerFound = true;
     for (int i = 0; i < localBufferSize; i++) {
       if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
         endMarkerFound = false;
         break;
       }
     }
     index++;
   }
   if (endMarkerFound) break;
 }
 return endMarkerFound;
}



the code uses a circular buffer to compare what is received over the ESPSEPRIAL line with what we expect. The full answer is ignored though, we only care to know if we got the endMarker message.

I use that to see if I get correctly an `"OK\r\n"` from AT commands for example.

I successfully used you functions, works great, but I failed to see contents of localBuffer[]. Should study your code deeper.

void loop() 
{
  digitalWrite(LED_BUILTIN, LOW);
  delay(500);
  mySerial.flush();
  mySerial.println("AT");

  if (waitForString("OK\r\n", LONG_PAUSE))
  {
    digitalWrite(LED_BUILTIN, HIGH);
  }
  delay(500);
}

Shumnee:
I successfully used you functions, works great, but I failed to see contents of localBuffer[]. Should study your code deeper.

localBuffer is a char array, not a cString, meaning there is no trailing '\0' (NULL) char, so you can't print it using traditional ways. if you want to see what's being accumulated in localBuffer, just use a for loop and print each character

 Serial.print(F("circular buffer: {");
for( byte idx = 0; idx < localBufferSize; idx++) Serial.write(localBuffer[idx]);
Serial.println(F("}"));

This is a circular buffer, holding whatever bytes coming in and when you reach the end of that buffer we restart at the beginning.
the length of this buffer is the same as the length of the endMarker string you expect so that when the localBuffer is full I can compare it with the endMarker - byte against byte and taking into account the offset in the circular buffer.

J-M-L:
localBuffer is a char array, not a cString, meaning there is no trailing '\0' (NULL) char, so you can't print it using traditional ways. if you want to see what's being accumulated in localBuffer, just use a for loop and print each character

This is a circular buffer, holding whatever bytes coming in and when you reach the end of that buffer we restart at the beginning.
the length of this buffer is the same as the length of the endMarker string you expect so that when the localBuffer is full I can compare it with the endMarker - byte against byte and taking into account the offset in the circular buffer.

Yeah, got it! Thanks for help!