2

I'm sending AT commands to an ESP8266 from an Arduino Uno/Nano (ATmega328) and attempting to parse the end of the strings received in response to establish how the ESP reacted and whether it was successful (and whether it's ready to receive another command yet). I'm aware that parsing AT command responses has been discussed before here:

Get AT command response

But I have a specific issue not covered there that might also be of interest to other people on here...

First, a function is called which sends the AT command to the ESP to connect to ThingSpeak (datalogging server). This works fine in manual mode and also connects when trying to parse the response, but it only parses the first line that comes back. For example, the expected output in the serial monitor would be:

c
AT+CIPSTART="TCP","api.thingspeak.com",80
CONNECT

OK
Connected to ThingSpeak!

Where c is just the command character I type to initiate the connection.

The actual response, however, is as follows:

c
AT+CIPSTART="TCP","api.thingspeak.com",80
Cannot connect to ThingSpeak!


CONNECT

OK

This means that the parsing function is ending before it receives the response... As shown in the code below, there is a 10 second timeout currently specified. Even with a 20 second timeout, the same thing happens, despite the fact that when executed manually, the response arrives in around one second.

Just to test the parsing function, I tried searching for "80" and it returned true as this is found at the end of the first line of the response. Whether it searches for "OK" or "OK\r\n" the result is the same, it returns false and THEN the rest of the response is received.

Here's the code:

boolean waitForResponse(String target, unsigned long timeout)
{
  unsigned long startTime = millis();
  String responseBuffer;
  char charIn;

  // Keep checking for ESP response until timeout expires
  while ((millis() - startTime) < timeout)
  {
    if (ESP.available())
    {
      responseBuffer += ESP.read();
    }
  }
  Serial.println(responseBuffer);

  if (responseBuffer.endsWith(target))
  {
    return true;
  } else {
    return false;
  }
}

void openCxn()
{
  ESP.print("AT+CIPSTART=\"TCP\",\"api.thingspeak.com\",80");
  delay(500);
  if (waitForResponse("80", 10000L))
  {
    Serial.println("Connected to ThingSpeak!");
  } else {
    Serial.println("Cannot connect to ThingSpeak!");
  }
}

Why does it return before the full response is received (well within the timeout period)? Is it something to do with the endsWith() function?

Consequently, do you have any ideas of how to make it parse the entire response instead of just the first line?

To reiterate, I am only interested in the end of the response (e.g. "OK" or "OK\r\n").

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
AntInvent
  • 85
  • 6
  • 1
    Most probably what you receive does not "end with" (`String::endsWith`) "80", but instead with "80" – tofro Jun 22 '16 at 19:55

2 Answers2

0

Any idea why it returns before the full response is received (well within the timeout period)?

Yes, your main problem is the following

if (ESP.available())

This makes the waitForResponse function return whenever the UART (or some other serial IO buffer) is empty - which is not what you want. What you want is to read from the serial port until you have received a line terminated with "\r\n".

Is it something to do with the endsWith() function?

Yes, that is an additional problem combined with ESP.available because you are attempting to match the end of a response line from the modem with what ever random data chopping occurs in the serial path. If you are extremely lucky this will be on line boundaries, but most likely not and you should not rely on that.

This is a general protocol problem known as framing that applies to any kind of asynchronous serial communication. For modem communications the framing characters are \r and \n.

Do yourself a favour and implement a readline function that reads one by one character until it the previous character was \r and the current character is \n and then return everything it read so far.

Then use that function exclusively1 for reading modem response data. This applies both to Intermediate result codes like CONNECT as well as Final result codes (e.g. OK etc). "Parsing" the response lines can then be as simple as

if (responseLine.equals("CONNECT\r\n")) ...

or

if (isFinalResultCode(responseLine)) ...

As I said before, the only correct way to process modem output is to divide the output into complete lines and iterate one full line at the time.


1 The only exception is when parsing AT+CMGS response data.

Community
  • 1
  • 1
hlovdal
  • 26,565
  • 10
  • 94
  • 165
  • Many thanks for your response! I still don't understand why it returns so soon though... `"This makes the waitForResponse function return whenever the UART (or some other serial IO buffer) is empty."` If the buffer is empty, it does leave the loop BUT the loop is repeated again because it is within the timeout loop. Why wouldn't it catch the next characters that subsequently arrive in the timeout? – AntInvent Jun 22 '16 at 20:10
  • @AntInvent that part is incorrect. It will continue looping until the timeout. The `endsWith` problem is genuine though. If you read more or less than one line (which you almost always will), then your logic will fail. – hobbs Jun 22 '16 at 20:20
  • True, I missed that before starting to write an answer. You still should convert your code flow to parse everything line by line, otherwise your program will not handle cases where `AT+CIPSTART` returns `ERROR` etc. – hlovdal Jun 22 '16 at 20:21
  • Ah ok, this is where I reach the limits of my understanding. Will String functions not handle multiple lines or is this issue specific to endsWith? I had assumed that the "\r\n"s would simply form part of the string as a whole. Thanks again – AntInvent Jun 22 '16 at 20:48
  • @hlovdal I have amended the code to parse one line at a time in a function that is called as long as the timeout hasn't been reached. The ESP doesn't appear to send a carriage return after its echo of the AT command so to test it I searched for 8 and 0 as the final two characters of the line and it successfully detected and printed that line to the serial monitor: – AntInvent Jun 22 '16 at 22:03
  • `OUTPUT: c lineRcvd = AT+CIPSTART="TCP","api.thingspeak.com",80 // first readLine successful readLine timed out! // no further lines received so timed out Cannot connect to ThingSpeak! CONNECT OK` As you can see here the CONNECT and OK lines are still missed entirely. What could be blocking them from appearing in the software serial buffer before the timeout occurs? Sorry for the lack of line breaks and code, I can't figure out how to post it without splitting it over many comments... – AntInvent Jun 22 '16 at 22:05
  • @hlovdal Ok ignore all that, the code order mystery is solved... I had not appended \r\n to AT command I SENT to the ESP. I'm not sure why that mattered or how the ESP then managed to respond anyway but once that was in the parsing function worked a treat. Now there's another error coming from the ESP relating to the parsing function itself but such is life... – AntInvent Jun 22 '16 at 22:49
  • Notice that an AT command line should be terminated by `\r` only and not `\r\n`, see [this answer](http://stackoverflow.com/a/21503919/23118) for details. – hlovdal Jun 23 '16 at 08:56
  • @hlovdal [AT firmware on ESP8266 needs `\r\n` to end the command.](https://github.com/espressif/ESP8266_AT/wiki/AT_Description) – gre_gor Jun 23 '16 at 10:59
0

Your if after the while is indeed wrong because you might have read more stuff back and thus you don't end with the target string even though "ok" was received.

You should just test if responseBuffer ends with target and return true only when you receive a new character in the while loop and not after. After you have a timeout, just return false.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
J-M-L
  • 1