SIM900 AT Commands response parsing

2019-02-20 16:32发布

问题:

i am using sim900 gps/gprs module shield connected to an Arduino Uno, how will i be able to parse the response of my AT commands? Or how will i be able to remove the 1st line printed in the serial after sending an AT command?

AT+CMGL="ALL"

+CMGL: 1,"REC READ","+XXXXXXXXXX","","16/04/25,15:20:59+32"
Hilp akp si ralphh the pogi one mmalit mi pizza hehehehehe

+CMGL: 2,"REC READ","+XXXXXXXXXX","","16/04/25,21:51:33+32"
Yow!!!

OK

example on the output above, i want to get rid of the AT+CMGL="ALL" and then parse the data left, what is the best way in parsing

回答1:

How will i be able to parse the response of my AT commands?

Yes, this is the right question to ask.

How will i be able to remove the 1st line printed in the serial after sending an AT command?

No, this is the wrong question to ask, because if you care about whether echo is on or not you are doing it wrong.

The correct strategy for parsing AT command output is as follows:

  • Send the AT command line (correctly terminated with "\r").
  • Read one and one character received from the modem until you have a complete line terminated with "\r\n" and then parse that line.
    • If the line equals a final result code, then all output from the command line is finished (and the modem is ready to receive new commands). This must be the first thing you test for!
    • If the AT command running has a prefix for its information text response lines (almost all have) check if the line starts with that, and if so process the line else ignore it.
    • If the AT command running does not have a prefix you probably want to print everything until the final result code is received. This applies only for legacy commands like ATI, and for parsing these you might legitimately care about echo or not.

Now for the AT+CMGL command it is a little bit more work since the responses are split on multiple lines.

First of all, the best source of information should be the manufacturer specific AT documentation, the second best being the official 3GPP 27.005 specification that standardize the AT+CMGL command.

The response for AT+CMGL in text mode is specified as

+CMGL: <index>,<stat>,<oa/da>,[<alpha>],[<scts>][,<tooa/toda>,
<length>]<CR><LF><data>[<CR><LF>
+CMGL: <index>,<stat>,<da/oa>,[<alpha>],[<scts>][,<tooa/toda>,
<length>]<CR><LF><data>[...]]

hence after receiving a line starting with "+CMGL: " all the lines following until you read a blank line ("\r\n") belongs to this.

See this answer on the general code structure and flow, although as written above the multi-line property of the response needs a bit more handling. I would have used something like the following (untested code):

enum CMGL_state {
    CMGL_NONE,
    CMGL_PREFIX,
    CMGL_DATA
};

// Extra prototype needed because of Arduino's auto-prototype generation which often breaks compilation when enums are used.
enum CMGL_state parse_CMGL(enum CMGL_state state, String line);
enum CMGL_state parse_CMGL(enum CMGL_state state, String line)
{
    if (line.equals("\r\n") {
        return CMGL_NONE;
    }
    if (line.startsWith("+CMGL: ") {
        return CMGL_PREFIX;
    }
    if (state == CMGL_PREFIX || state == CMGL_DATA) {
        return CMGL_DATA;
    }
    return CMGL_NONE;
}

...

write_to_modem("AT+CMGL=\"ALL\"\r");
CMGL_state = CMGL_NONE;
goto start;
do {
    CMGL_state = parse_CMGL(CMGL_state, line);
    switch (CMGL_state) {
    case CMGL_PREFIX:
        process_prefix(line); // or whatever you want to do with this line
        break;
    case CMGL_DATA:
        process_data(line); // or whatever you want to do with this line
        break;
    case CMGL_NONE:
    default:
        break;
    }
start:
    line = read_line_from_modem();
} while (! is_final_result_code(line))


回答2:

The first line AT+CMGL="ALL" seems to be the echo. You can disable it by sending ATE0 to your module in your setup function.

As for the rest of the data, it all have the same format. You can easily write your parser using different string manipulation functions.



回答3:

If you are using arduino I would recommend to use a good library! You don't need to deal about these stuff. Try http://www.gsmlib.org/ or you can find any other you like.

I will include one example here.

#include "SIM900.h"
#include <SoftwareSerial.h>
//If not used, is better to exclude the HTTP library,
//for RAM saving.
//If your sketch reboots itself proprably you have finished,
//your memory available.
//#include "inetGSM.h"

//If you want to use the Arduino functions to manage SMS, uncomment the lines below.
#include "sms.h"
SMSGSM sms;

//To change pins for Software Serial, use the two lines in GSM.cpp.

//GSM Shield for Arduino
//www.open-electronics.org
//this code is based on the example of Arduino Labs.

//Simple sketch to send and receive SMS.

int numdata;
boolean started=false;
char smsbuffer[160];
char n[20];

void setup() 
{
  //Serial connection.
  Serial.begin(9600);
  Serial.println("GSM Shield testing.");
  //Start configuration of shield with baudrate.
  //For http uses is raccomanded to use 4800 or slower.
  if (gsm.begin(2400)){
    Serial.println("\nstatus=READY");
    started=true;  
  }
  else Serial.println("\nstatus=IDLE");

  if(started){
    //Enable this two lines if you want to send an SMS.
    //if (sms.SendSMS("3471234567", "Arduino SMS"))
      //Serial.println("\nSMS sent OK");
  }

};

void loop() 
{
  if(started){
    //Read if there are messages on SIM card and print them.
    if(gsm.readSMS(smsbuffer, 160, n, 20))
    {
      Serial.println(n);
      Serial.println(smsbuffer);
    }
    delay(1000);
  }
};