Skip to content

Instantly share code, notes, and snippets.

@phalski
Last active June 30, 2024 13:46
Show Gist options
  • Save phalski/2bb38d2e12a0fadfa1f4d9dc0ee7d51d to your computer and use it in GitHub Desktop.
Save phalski/2bb38d2e12a0fadfa1f4d9dc0ee7d51d to your computer and use it in GitHub Desktop.
Personal notes to Hayes/AT command interface understanding

Hayes/AT command interface understanding

This document describes my personal notes of understanding and implementing AT command transmission to a modem via serial line.

References

Hardware

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

Receiving responses

Response types

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)

Example request/response communication:

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.

Possible solutions

The identified problem can be solved in two ways:

  1. Disable unsolicited responses (AT^CURC=0) and treat everything returned as the response. This does not solves the problem of detecting EOT.
  2. 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.

Response structures

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

Response structure examples

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.

Issues

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

Regular expressions to find responses in recieved data stream

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

Code to detect command response

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")))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment