Handling series of AT commands to GSM shield

Hello,

Certain tasks (e.g. SMS sending) require a series of AT commands towards the GSM shield. Because of the serial communincation is not that fast, delay should be inserted between the commands, and if longer answer time is expected, that is also have to be taken into consideration, so we won’t overwhelm the GSM shield. If something needs to be done with the received bytes later, I usually empty the serial buffer to get rid of the useless data before the actual important data:

void emptySerialBuffer()
{
  whileStartedMillis = millis();
  while ( gsmSerial.available() && ( (millis() - whileStartedMillis) < 1000 ) )
  {
    char dummyChar = gsmSerial.read();
    delay(1);
  }
}

The first and simplest solution to insert some delay between the AT commands.
(longer command means more byte → more delay to be sure) For example:

void getGsmDateTime()
{
gsmSerial.write("AT+CGATT=1\r\n");
delay(50);
emptySerialBuffer();
gsmSerial.write("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"\r\n");
delay(100);
emptySerialBuffer();
gsmSerial.write("AT+SAPBR=3,1,\"APN\",\"YOUR PROVIDER\"\r\n");
delay(100);
...

This has its downside, because if the AT command triggers the GSM shield to communicate with the network, random delays can occur, so it’s hard to determine the delay time.

As I experienced, the AT command itself is sent back from the GSM shield before the response. So we know the length of the command, what if we wait until more bytes are arrived than that, meaning the GSM shield processed the command. So we send 14 bytes to the shield, if more than that arrives, the command is processed.

gsmSerial.write("AT+SAPBR=1,1\r\n");			//the response for this command usually takes at least 2-3 seconds, but can be more
whileStartedMillis = millis();
while ( ( gsmSerial.available() < 15 ) && ( (millis() - whileStartedMillis) < 15000 ) )
{
   delay(10);
}
emptySerialBuffer();
...

Another idea is that actually we know what answer is expected, so for example we can wait for a certain character which indicates that the command is processed. Sticking to the earlier example, we know that receiving “OK” means that the command is processed, and as far as the command doesn’t contain these characters this method should work.

char expectedChar = 'O';
gsmSerial.write("AT+SAPBR=1,1\r\n");			//the response for this command usually takes at least 2-3 seconds, but can be more
whileStartedMillis = millis();
while ( (millis() - whileStartedMillis) < 15000 ) )
{
	char dummyChar = gsmSerial.read();
	if (dummyChar == expectedChar)
	{
		break;
	}
   delay(10);
}
emptySerialBuffer();
...

Any ideas to make it better?

Thanks in advance.

Because of the serial communincation is not that fast, delay should be inserted between the commands

That is absolute nonsense. Putting a delay() between sending commands is NOT the solution.

If you are going to force asynchronous communications to be synchronous, you must wait until you get a response to the last command AND the response is “Hey, I did that” before you send another command, REGARDLESS OF HOW LONG THAT TAKES.

I usually empty the serial buffer to get rid of the useless data before the actual important data:

The GPRS shield does NOT send back useless data. You have chosen (not very smart) to ignore some responses, so when you do get around to reading a response, there is data that you don’t NOW care about. But, you should have, when the response was sent.

The first and simplest solution to insert some delay between the AT commands.

That might be OK for simpletons. It is NOT even close to a reasonable thing to do.

while ( ( gsmSerial.available() < 15 ) && ( (millis() - whileStartedMillis) < 15000 ) )
{
   delay(10);
}

Are you being billed every time that loop iterates? If not (and I seriously doubt that you are) GET RID OF THE STUPID DELAY.

Read all incoming data to a buffer till end marker or endphrase then process. No delays as PaulS and others will mention. No useless data , all data mist be processed and only processing part must decide which is useful which not.

Read Robin2’s tutorial about that. I implemented that method after 5th time i reread whole tutorial.

PaulS: Thanks for the ideas.

surepic: can you post the link to that tutorial? I can't seem to find it.

Robin2's tutorial:

Thanks for the link, finally I had time to examine the methods.
The second example confuses me a little.

// Example 2 - Receive with an end-marker

const byte numChars = 32;
char receivedChars[numChars];   // an array to store the received data

boolean newData = false;

void setup() {
    Serial.begin(9600);
    Serial.println("<Arduino is ready>");
}

void loop() {
    recvWithEndMarker();
    showNewData();
}

void recvWithEndMarker() {
    static byte ndx = 0;
    char endMarker = '\n';
    char rc;
   
    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

        if (rc != endMarker) {
            receivedChars[ndx] = rc;
            ndx++;
            if (ndx >= numChars) {
                ndx = numChars - 1;
            }
        }
        else {
            receivedChars[ndx] = '\0'; // terminate the string
            ndx = 0;
            newData = true;
        }
    }
}

void showNewData() {
    if (newData == true) {
        Serial.print("This just in ... ");
        Serial.println(receivedChars);
        newData = false;
    }
}

I understand that this code doesn’t block the LOOP while all characters+endMarker are not received.
But what if before the endMarker is in, the program exits the while loop because all the characters are read from the buffer (Serial.available() = 0, but further receiving is in progress) and the next call of recvWithEndMarker() sets the ndx back to 0. It seems to me that, in case of slow serial communication, the characters will be written repeatedly in receivedChars[0].

But what if before the endMarker is in, the program exits the while loop because all the characters are read from the buffer (Serial.available() = 0, but further receiving is in progress) and the next call of recvWithEndMarker() sets the ndx back to 0. It seems to me that, in case of slow serial communication, the characters will be written repeatedly in receivedChars[0].

Because ndx is declared static, it is only initialized ONCE, the first time recvWithEndMarker() is called.

After that first call, ndx is not reset to 0 when recvWithEndMarker() is called again.

This is the way i used it in my project and it wont miss anything till endmarker or timout.

void listener(char a[]) {

  char serial_stream = 0;
  unsigned int  i = 0;
  unsigned long _time = 0;
  bool flag = false;
  const unsigned int timeout = 5000;

  _time = millis();

  do {
    if (mySerial.available()) {
      serial_stream = mySerial.read();
      if (serial_stream >= 32 && serial_stream <= 127) {
        flag = true;
        a[i] = serial_stream;
        i++;
      }
    }
  } while ((serial_stream != '\n') && (millis() - _time) <= timeout);

  if (flag) a[i + 1] = '\0';

  return;
}

PaulS:
Because ndx is declared static, it is only initialized ONCE, the first time recvWithEndMarker() is called.

After that first call, ndx is not reset to 0 when recvWithEndMarker() is called again.

Ahh okay, my incompetence.

arpadjanos:
Ahh okay, my incompetence.

I prefer to think of it as lack-of-awareness. That you are asking questions, and trying to understand, indicates that you are not incompetent.

Thanks for the support. In this case, I feel free to ask more questions. :slight_smile:

Using Robin2 kind of recvWithEndMarker() function, the receivedChars char array is constructed, what should be a good method for example looking for "OK" in the array?

void recvWithEndMarker() {
    static byte ndx = 0;
    char endMarker = '\n';
    char rc;
   
    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

        if (rc != endMarker) {
            receivedChars[ndx] = rc;
            ndx++;
            if (ndx >= numChars) {
                ndx = numChars - 1;
            }
        }
        else {
            receivedChars[ndx] = '\0'; // terminate the string
            ndx = 0;
            newData = true;
        }
    }
}

String.indexOf function seems to answer my upper question, this needs a String type approach (which is actually not advised over char arrays) for receiving serial communication. What if the String is constructed in the loop and then further processed if carriage return is in?

String msg = String("");
void loop()
{
if (gsmSerial.available())
{
	receivedChar = (unsigned char)gsmSerial.read();
	if( receivedChar == '\r' )
	{
        processResponse();
        }
        if( receivedChar == '\n' )
	{}
        else
	{
        msg += String(receivedChar);
        }
}
}

processResponse() could be a series of if condition+statements. For example to check OK as a response:

void processResponse()
{
if(msg.indexOf("OK") >= 0)
{
//do something
}
msg = "";
}

what should be a good method for example looking for "OK" in the array?

strstr().

Okay, unlike strcmp() which returns 0/1, strstr() returns a pointer.
Does pointer to NULL mean false result in an IF test?

if ( strstr(receivedChars,"OK") )
{
// OK in.
}
else
{
// OK not in.
}

What about getting an integer from receivedChars char array?
Now I'm using Serial.parseInt to get my number directly from the buffer.

Does pointer to NULL mean false result in an IF test?

A NULL pointer means that the string being looked for wasn't found.

I get that, but what's the result if NULL pointer gets evaluated with an IF?
Source of my confusion is, that I've read "Dereferencing a null pointer is undefined behavior in C".

Is there a built in function to get an integer from a character array?
Like Serial.parseInt directly from the serial buffer.

Thanks in advance.

Yes you can treat it as false in if statement.
If(strstr(char array,”string to find”))
Will evaluate true if string is found and if not found will evaluate false.

Source of my confusion is, that I've read "Dereferencing a null pointer is undefined behavior in C".

Dereferencing means using a pointer to accomplish something. You should only do that after testing that the pointer is not NULL. You can ALWAYS compare a pointer to NULL (explicitly or implicitly).

Explicit:

char *haystack = "Needles all over the place";
if(strstr("needle", haystack) == NULL)
{
   // No needle in that haystack
}

Implicit:

char *haystack = "Needles all over the place";
if(!strstr("needle", haystack))
{
   // No needle in that haystack
}

Strstr(actual string, string to search for)
So

If(!strstr(“haystack”,needle));

surepic:
Strstr(actual string, string to search for)
So

If(!strstr(“haystack”,needle));

Yeah, but that doesn't make any sense. 8)

Looking for a haystack in a needle?

Actual string is haystack. Needle is a string to be searched for. not opposite.

Char string=“hello world”;
Char needle=“world”;

Strstr(string,needle);

Thats what i meant.

But not strstr(needle,haystack).