SIM900 AT Commands response parsing

Ralph picture Ralph · Apr 25, 2016 · Viewed 9.3k times · Source

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

Answer

hlovdal picture hlovdal · Apr 26, 2016

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))