This document describes my personal notes of understanding and implementing AT command transmission to a modem via serial line.
- HUAWEI ME909s Series LTE Module V100R001 AT Command Interface Specification http://consumer.huawei.com/en/solutions/m2m-solutions/products/support/user-guides/detail/me909s_120_en.htm?id=50243
Tag info:
Model: E3372h-153
IMEI: 86875702XXXXXXX
S/N: XXXXXXXXXXXXXXXX
ATI
output:
Manufacturer: huawei
Model: E3372
Revision: 21.200.07.01.22
IMEI: 86875702XXXXXXX
+GCAP: +CGSM,+DS,+ES
As it is possible for the device to send unsolicited results (configurable via AT^CURC
for my Huawei E3372), we have to differ between two different kinds of responses:
- Command response – Answering a previously sent command
- Unsolicited response – Informational content, sent either in periodic intervals (e.g. signal info) or event triggered (e.g. switched network)
First we send the command asking for available networks. As the modem needs to scan prior responding, this command takes a few seconds to execute.
AT+COPS=?\r\n
In the meantime we can read some data:
…
r\n^RSSI: 9\r\n
\r\n
^HCSQ:"WCDMA",36,26,45\r\n
\r\n
^RSSI: 9\r\n
\r\n
^HCSQ:"WCDMA",36,26,45\r\n
\r\n
^RSSI: 9\r\n
\r\n
^HCSQ:"WCDMA",35,26,47\r\n
\r\n
^RSSI: 9\r\n
\r\n
^HCSQ:"WCDMA",34,26,49\r\n
\r\n
^RSSI: 9\r\n
\r\n
^HCSQ:"WCDMA",34,27,51\r\n
\r\n
^RSSI: 9\r\n
\r\n
^HCSQ:"WCDMA",35,27,49\r\n
\r\n
+COPS: (2,"E-Plus","E-Plus","26203",2),(1,"o2 - de","o2 - de","26207",7),(1,"Telekom.de","TDG","26201",2),(1,"Vodafone.de","Vodafone","26202",7),(1,"Vodafone.de","Vodafone","26202",2),(1,"Telekom.de","TDG","26201",7),(1,"Vodafone.de","Vodafone","26202",0),(1,"Telekom.de","TDG","26201",0),(1,"E-Plus","E-Plus","26203",0),,(0,1,2,3,4),(0,1,2)\r\n
\r\n
OK\r\n
…
As you can see the output contains a mix of command and unsolicited responses. This leads to problems in matching received data to a corresponding command: Not all the received content belongs to the requests response. Therefore we need a reliable mechanism to match request and response and identify the end of a command execution.
The identified problem can be solved in two ways:
- Disable unsolicited responses (
AT^CURC=0
) and treat everything returned as the response. This does not solves the problem of detecting EOT. - Specifiy common response structure and implement a parser.
As you have to implement a parser anyways to detect EOT, it's straight forward to go for the second approach.
All the information below is based on own observations and considerations, not on an official specification.
A reoccuring pattern in the above response example is the following:
\r\n
<data>\r\n
All information returned is started and terminated with a \r\n
. This leads to the conclusion that all data is separaded by \r\n\r\n
implying that each chunk of data cannot include empty lines. Every command returns a status after execution and optionally some content before. Status and content are both returned in separate chunks. Status data is easily identifiable e.g OK
or ERROR
. This makes everything which is not a status chunk automatically a unsolicited response.
If the commands response contains some content data this is the data preceding the status data.
Summary of response characteristics:
- Data is always separated with one empty line (
\r\n\r\n
) - Data never contains empty lines (
\r\n\r\n
) - The response of each command contains result status data
- If the response of a command contains content, this is the data block preceding status data
- Every other data is unsolicited
Command response - status only:
\r\n
<status>\r\n
Command response - content + status:
\r\n
<content>\r\n
\r\n
<status>\r\n
Unsolicited response:
\r\n
<content>\r\n
With:
Param | Definition |
---|---|
<content> |
String of data not containing \r\n\r\n and not matching status pattern |
<status> |
String of data matching status pattern |
See below regex for status pattern. This pattern might not cover all allowed characteristics.
If a command can either respond with or without content, it's impossible to differ between the responses. This means if the response is just a status, the parser will maybe treat a previously received unsolicited response as content. Normally when a content requesting command is responded with just a status, the status indicates an error. So in most cases this issue won't be a problem if you follow one rule:
Check status before handling response
All regular expressions are python patterns
There is no use for explicit search of unsolicited responses, they will remain when you remove all command responses.
Name | Pattern |
---|---|
p_status |
`"(?POK |
p_content |
"(?P<content>(?:[^\r]+\r\n)*[^\r]+)" |
p_status_response |
"\r\n{p_status}\r\n" |
p_content_response |
"\r\n{p_content}\r\n\r\n{p_status}\r\n" |
p_generic_response |
"(?:\r\n{p_content}\r\n)*\r\n{p_status}\r\n" |
WARNING: STATUS PATTERN MAYBE INCOMPLETE
status_pattern = "(?P<status>OK|ERROR|COMMAND NOT SUPPORT|\\+CME ERROR: [^\r\n]+)"
content_pattern = "(?P<content>(?:[^\r]+\r\n)*[^\r]+)"
response_pattern = "(?:\r\n{0}\r\n)*\r\n{1}\r\n".format(content_pattern, status_pattern)
response = re.search(response_pattern, received_data, re.MULTILINE)
if response:
print("Status: {}".format(response.group("status")))
print("Content: {}".format(response.group("content")))