AT Command Guide for SIM 800/900 modules

Questions that come up related to SIM 800/900 modules often use single Serial.println("AT_COMMAND_EXAMPLE"); lines for AT command execution, usually delays inserted between them and not checking the response for each command at all!

As different commands execution time can vary greatly and each response from the module contains valuable information, this is far from reliable communication with such modules. Furthermore one particular command can take very different times, for example establishing a GPRS connection can vary between 2-8 seconds based on my experiences (or not connected at all, which situation also has to be handled).

If someone is aiming for a better solution, than just sending the commands blindly, here is a simple function (found at first a long-long time ago in the hidden depths of cyberspace) for response checking with timeout:

bool sendATcommand(const char* ATcommand, const char* expected_answer, const unsigned long timeout)
{
  const uint8_t RESPONSE_SIZE = 160;
  //response array could be made global
  char response[RESPONSE_SIZE];
  
  bool answer = false;
  uint8_t respx = 0;
  unsigned long previous = 0ul;
  
  //clear whole array
  memset(response, '\0', RESPONSE_SIZE);

  //clean the input buffer (with timeout)
  previous = millis();
  while ( (Serial.available() > 0) && ((millis() - previous) < 1000ul) )
  {
    Serial.read();    
  }
  
  //send the AT command
  Serial.println(ATcommand);    

  previous = millis();
  
  //this loop waits for the answer with timeout
  do      
  {
    if (Serial.available() != 0)
    {
      //if there is data in the UART input buffer, reads it and checks for the answer
      response[respx] = Serial.read();
      respx++;
      
      //prevent buffer overflow
      if (respx >= RESPONSE_SIZE)
      {
        respx = RESPONSE_SIZE - 1;
      }

      //check if the desired answer is in the response of the module
      if (strstr(response, expected_answer) != NULL)
      {
        answer = true;
      }
    }
  }
  while (!answer && ((millis() - previous) < timeout));

  //terminate the string, respx was limited to (RESPONSE_SIZE - 1)
  response[respx] = '\0';

  return answer;
}

This is intended to be just an example to encourage users to develop their AT commands usage.
Any location/service provider dependent stuff has to be solved by the users.

Another advice is to always check the module's AT Command Manual!
M2MSupport is also a good relevant page.

After having this (or any similar) function, for example a simple network registered check would look like:

if (sendATcommand("AT+CREG?", "+CREG: 0,1", 1000ul)
{
  //registered, home network
}
else if (sendATcommand("AT+CREG?", "+CREG: 0,5", 1000ul)
{
  //registered, roaming
}
else
{
  //not registered
}

:+1: :clap: :clap: :clap:

Indeed - this use of what I call "blind" delays is all too common.

Only yesterday:

https://www.avrfreaks.net/s/topic/a5C3l000000UYXnEAO/t146581?comment=P-1400752

https://www.avrfreaks.net/s/topic/a5C3l000000UaMLEA0/t153559?comment=P-1214692

Indeed: not least, the fact that your command may have failed!

If the command has failed, there is clearly no point in sending the next command - which relies upon its success!

With radio systems, it is inevitable that "calls" will fail sometimes - so you really do need to know when that happens, and be able to handle it.

Of course, this doesn't just apply to SIM 800/900 modules - it applies to anything & everything that uses AT Commands.

100%. agree.
I use a state-machine in my modem driver, so it can run asynchronously to the main code.

I was trying a couple of years ago to drum up support here to turn it into a library, but met zero interest.

It’s well overdue for someone to do it, but c’est la vie !

It’s quite a lot of work, but very logical.

:+1: :clap: :clap: :clap:

Yes! Another Top Tip!