How to wait for serial port to send characters?

I am doing some work with an ESP8266 WiFi module and am trying to communicate with it via serial from a UNO board. The idea is for the ESP8266 to provide WiFi functionality to my Arduino project. For testing purposes I am using softwareserial and connecting the ESP8266 to standard GPIO pins. Ultimately, once everything is tested and working, the intention is to connect the ESP8266 to the serial hardware pins 0 and 1.

The difficulty is that the ESP8266 might send out several \r+\n in its response, so I can't just use a \r or \n character as a terminator. For example, the response to the AT command is:

\r\nAT\r\n\r\nOK\r\n

The submitted command is always echoed back followed by a \r\n. There may or may not be a blank line before the response is output. The answer I am looking for will be in there somewhere, but it cannot be predicted on which line or how many \r\n sequences precede it.

Furthermore, some commands can take 7 or 8 seconds to respond.

I have tried preceding the parsing code with something like:

while (!esp8266.available()) {
}

esp8266 is defined as a SoftwareSerial object with the GPIO pins I am using.

I had hoped that the process will idle while waiting for some output, but it still proceeds directly to the parser which then fails because there is no output to process yet.

From the code below it will be evident that for the search I am not buffering the incoming characters nor using the \r or \n as a termination character, just reading whatever output is available and parsing character by character "on the fly" as it were.

The parser works just fine once some characters arrive, but how do I implement a "wait until some characters arrive" function so that the sketch will wait before parsing, bearing in mind that the wait time might be several seconds?

bool replyContains(const char* reply) {

  // Clear buffer
  uint8_t rl = strlen(reply);
  uint8_t idx = 0;
  uint8_t c = 0;


  while (!esp8266.available()) {
  }

  Serial.print(F("Reply: "));

    while (esp8266.available()) {
      c = esp8266.read();
    
      Serial.print((char)c);
      if ((c == reply[idx]) && (idx < rl)) {
        idx++;
      }else{
        if (idx != rl) idx = 0;
      }
    }

  Serial.println();

  if (idx == rl) {Serial.print(F("Found: "));Serial.println(reply);};

  if (idx == rl) return true;

  return false;
}

Have a look at the examples in Serial Input Basics - simple reliable ways to receive data.

Don't worry about the multiple \r\n - just make your program check for the messages that are relevant, such as the "OK"

You can use the strstr() function to see if a message contains certain characters

...R

The parser works just fine once some characters arrive, but how do I implement a "wait until some characters arrive" function so that the sketch will wait

That code WILL wait. It waits until ONE character arrives. You then process that one character, and assume that it is a complete packet.

EVERY other communications protocol requires that the reply contain either a value that indicates how many characters will be in the reply or some special character that means end-of-reply. AT commands don't adhere to any useful protocol. The can return no value. They can return a value terminated with \r\n, \r only, \n only, or nothing.

That sucks, but that's life.

PaulS:
That code WILL wait. It waits until ONE character arrives. You then process that one character, and assume that it is a complete packet.

So if my code is proceeding to the next stage that must mean at least one character has arrived and OR has been left over from the previous read. Either way serial.available() is now true. Each received character should get printed in the following while (esp8266.available()) loop but since nothing is being printed, then either nothing has arrived or the character might be non-printable such as \r \n or null. I guess I could print out in hex to see if that gives me any clues.

If the character is being left over from the previous loop, then how do I flush it? Serial.flush now flushes the output rather than the input buffer. I have come across code such as this:

while (Serial.available()){
  Serial.read();
}

But that didn't seem to help. Probably better to properly process the incoming characters somehow but it certainly is tricky!

You don't say what your Arduino project is but could it perhaps run on the ESP8266 instead of the Uno ?

That might have been one option, but the project requires 16 GPIO pins. There are not enough on the ESP8266 module.

BitSeeker:
I guess I could print out in hex to see if that gives me any clues.

I recommend not to print bare variables. Add some descriptive text to it:

Serial.print("I received: ");
Serial.print(c);
Serial.print(" (0x");
Serial.print(c, HEX);
Serial.println(')');

Or better put that in a function:

void logReceivedChar(char rxChar)
{
    Serial.print("I received: ");
    Serial.print(c);
    Serial.print(" (0x");
    Serial.print(c, HEX);
    Serial.println(")");
}

void loop() 
{
    // ...
    logReceivedChar(c);
    // ...
}

I did some further testing and it seems that while this will stop and wait until I type something at the keyboard:

while (!Serial.available()){
}

This will not:

while (!esp8266.available()){
}

I added HEX printing of characters so that I could see if any spurious whitespace characters were being carried over, but there were none. It seems therefore that this works with hardware serial, but does not work with a serial port implemented in Softwareserial.

I was able to implement a wait and timeout as shown below. This works for all but the very last command being sent. The last command seems to get sent but does not get acted on by the ESP8266. It would have been nicer to just wait until the first character is received and then process the loop. I would not then have needed the second delay at the end of the timeout loop to receive any characters remaining in the serial buffer after the search string is found. As it is, there had to be some way to break out of the outer delay loop. There must be a neater way....

bool replyContains(const char* reply, unsigned int waitA, unsigned int waitB) {

 // Clear buffer
 uint8_t rl = strlen(reply);
 uint8_t idx = 0;
 uint8_t c = 0;
 bool ex = false;

 // Default timeouts
 if (!waitA) waitA = 1000;
 if (!waitB) waitB = 100;

 // Set timeout threshold
 unsigned long timeout = millis() + waitA;

 while (millis() < timeout) { 

   while (esp8266.available()) {
     
     // Read a character
     c = esp8266.read();
   
//      Serial.print(c, HEX);
//      Serial.print("[");
      Serial.print((char)c);
//      Serial.print("] ");

     // Break on LF
/* Is this required?
     if (c == 10) {
       if (idx < rl) idx = 0;
       break;
     }
*/
     // Match the character
     if ((c == reply[idx]) && (idx < rl)) {
       idx++;  // Matched, now look for the next one
     }else{
       if (idx != rl) idx = 0; // Not matched - reset matching process
     }
   }

   // If we have a match, allow some time to read remaining characters then exit loop
   if ((idx== rl) && (!ex)) {
     timeout = millis() + waitB;
     ex = true;
   }

 }

 Serial.println();

 if (idx == rl) {Serial.print(F("Found: "));Serial.println(reply);};

 // Reply was matched
 if (idx == rl) return true;

 // Reply was not matched
 return false;
}