Skip to main content

AT Command Codec

The AT Command Codec is designed for debugging and communicating with devices that use AT command protocols, such as GSM/LTE modems (ESP32-AT, Quectel, SIMCom), cellular modules, and embedded systems following the AT command set specification.

This codec handles:

  • Commands: AT, AT+CMD, AT+CMD=params, AT+CMD?
  • Responses: OK, ERROR, status responses like +CMD: data
  • Error responses: +CME ERROR: code, +CMS ERROR: code, ERROR: message
  • Unsolicited Result Codes (URCs): Asynchronous notifications like +URC: data
  • Structured responses with length-prefixed payloads: Multi-line responses with embedded binary data

Framing Strategy​

The AT codec parses a byte stream into discrete messages using line-based framing with response terminator detection and support for length-prefixed binary payloads.

Basic Frame Format​

Each AT message consists of lines separated by \r\n:

AT+COMMAND=param1,param2\r\n

Response Format​

Responses are buffered until a terminator is received:

AT+CMD?\r\n
+CMD: value1, value2\r\n
OK\r\n

This is emitted as a single message with payload:

+CMD: value1, value2\r\n
OK

Message Lifecycle​

1. Decoding (RX Path)​

The codec processes the byte stream as follows:

  1. Line buffering: Accumulates lines (bytes between \r\n delimiters)
  2. Empty line skipping: Ignores lines containing only whitespace
  3. Terminator detection: Stops buffering when a terminator is received
  4. URC detection: Returns immediately for self-contained unsolicited result codes
  5. Length-prefixed payload handling: For responses like +MQTTSUBRECV:0,"topic",12,hello world, extracts and validates the length prefix

Response Terminators​

The codec recognizes these terminators (case-sensitive):

TerminatorMeaning
OKSuccessful response
ERRORGeneric error
+CME ERROR: codeGSM/3GPP mobile equipment error
+CMS ERROR: codeSMS (Mobile Service) error
ERROR: messageError with description
NO CARRIERConnection lost
BUSYDevice busy
NO ANSWERCall unanswered
NO DIALTONENo dial tone
>Prompt for data input (bare character, no \r\n required)
CONNECT or CONNECT speedConnection established
COMMAND NOT SUPPORTESP-AT specific: command not supported

2. Unsolicited Result Codes (URCs)​

URCs are asynchronous notifications that arrive independent of any command:

+RING\r\n
+CMT: "+1234567890",,"24/12/10,15:30:45+32"\r\n
Hello, this is a message!\r\n

The codec handles two types:

Self-contained URCs (single-line): Returned immediately without waiting for OK/ERROR

  • Configured via custom_urc_prefixes
  • Example: +RING, +CMT, +CREG

Length-prefixed URCs: Responses with embedded binary payloads

  • Built-in patterns: +MQTTSUBRECV:, +HTTPRECV:, +CIPRECV:, +IPD:
  • Custom patterns via custom_length_prefixed_urcs

3. Length-Prefixed Responses​

For messages containing binary data, the codec parses length prefixes to extract complete payloads:

+MQTTSUBRECV:0,"cycbox/rx",12,hello world\r\n
+HTTPRECV:100,<100 bytes of binary data>\r\n
+CIPRECV:0,50,<50 bytes of data>\r\n

The codec:

  1. Identifies known length-prefixed patterns (or custom ones you define)
  2. Parses the last numeric field before the final comma
  3. Extracts that many bytes as the payload
  4. Returns the complete message once the payload is received

Important: The payload length is exact — if the header specifies 12 bytes but only 11 arrive before the terminator, the codec holds the frame until 12 bytes are received or the timeout fires.

4. Encoding (TX Path)​

When sending a message:

  1. Payload unchanged: The codec sends your message payload as-is
  2. Terminator added: Appends \r\n if not already present
  3. No transformation: The message content is not modified

For example, sending AT+CMD=value results in the frame:

AT+CMD=value\r\n

Configuration​

The AT codec supports custom URC prefixes to handle device-specific unsolicited codes.

Configuration Fields​

FieldTypePurpose
at_codec_custom_urc_prefixesMultiline textDefine custom self-contained URC prefixes (one per line)
at_codec_custom_length_prefixed_urcsMultiline textDefine custom length-prefixed URC prefixes (one per line)

Examples​

Custom self-contained URCs (device sends these without expecting OK):

+RING
+CMT
+CREG
+CALL_STATUS

Each line defines a prefix. When the codec sees a line starting with +RING:, it returns that message immediately.

Custom length-prefixed URCs (device sends these with embedded binary data):

+MYDATA:
+EVENTLOG:
+BUFFER:

When the codec sees +MYDATA:0,256,<256 bytes>, it:

  1. Identifies the length prefix (256)
  2. Waits for exactly 256 bytes
  3. Returns the complete message

State Management​

The AT codec is stateful. It maintains:

  • Line buffer: Accumulates lines until a terminator is found
  • Pending payload state: Tracks incomplete length-prefixed responses
  • Custom URC configuration: User-defined patterns

Reset Behavior​

The codec resets (clears all internal state) when:

  • The connection is re-established
  • A session boundary is crossed
  • EOF is received

This ensures that partial frames from one session do not leak into the next.

Overflow Protection​

To prevent unbounded memory growth from malformed or noisy input:

  • Max line buffer: The codec buffers a maximum of 64 lines before flushing
  • Timeout flushing: On decode_timeout, incomplete payload waits are flushed

If 64 lines arrive without a terminator, the codec returns them as a single message and resets the buffer.

Practical Examples​

Simple Command/Response​

TX: AT\r\n
RX: AT\r\n
\r\n
OK\r\n

Message payload: "OK"

Query with Multi-Line Response​

TX: AT+CGSN?\r\n
RX: AT+CGSN?\r\n
123456789\r\n
OK\r\n

Message payload:
123456789\r\n
OK

Self-Contained URC (Incoming Call)​

(device sends without command)
+RING\r\n

Message payload: "+RING"
(returned immediately; no OK expected)

Length-Prefixed Response (MQTT Subscribe)​

Configuration: custom_length_prefixed_urcs = +MYRECV:

TX: AT+MQTT=SUBSCRIBE,"topic"\r\n
RX: AT+MQTT=SUBSCRIBE,"topic"\r\n
OK\r\n
+MYRECV:0,"topic",12,hello world\r\n

Message 1 payload:
OK

Message 2 payload:
+MYRECV:0,"topic",12,hello world

The codec:

  1. Buffers "OK" and returns it (terminator found)
  2. Parses +MYRECV:0,"topic",12, and extracts length = 12
  3. Reads exactly 12 bytes: "hello world"
  4. Returns as a self-contained URC (no additional terminator needed)

Partial Payload (Timeout Scenario)​

TX: AT+RECV\r\n
RX: AT+RECV\r\n
+HTTPRECV:100,<only 50 bytes arrive before timeout>\r\n

On timeout (decode_timeout called):
- Incomplete payload flushed
- Message returned with 50 bytes (incomplete)

Common Patterns​

AT Commands with Parameters​

AT+MQTTSUB="topic",0\r\n           ← Query command
AT+MQTTCONF="user","pass"\r\n ← Set command
AT+CPIN?\r\n ← Status check
AT+CSCA?\r\n ← Read from device

Multi-Line Info Responses​

AT+COPS?\r\n
+COPS: 0,0,"Carrier",2\r\n
OK\r\n

Error Handling​

AT+INVALID\r\n
ERROR\r\n

AT+QUOTA\r\n
+CME ERROR: 500\r\n

AT+SEND\r\n
> ← Prompt, no \r\n

Integration Notes​

With Lua Scripts​

Use the AT codec with Lua to parse specific response fields:

-- Message received via AT codec
function on_message(msg)
-- Parse +CPIN response
if msg.payload:match("^+CPIN:") then
local status = msg.payload:match("+CPIN: (.+)")
if status == "SIM PIN" then
-- Send PIN unlock command
send_message("AT+CPIN=\"1234\"")
end
end
end

With Data Converters​

The AT codec returns raw text lines. Use a Data Converter to extract structured fields:

+CREG: 0,1,"1234","5678"
↓
Converter extracts: [0, 1, "1234", "5678"]

With Frame Codec​

For devices supporting both AT commands and binary protocols, use separate transports/codecs for each mode.

Troubleshooting​

Issue: Responses arrive out of order​

Cause: Multi-line responses split across TCP packets or serial reads.
Solution: The codec buffers until a terminator. Ensure your device sends complete responses.

Issue: Partial payloads with length prefixes​

Cause: Device sends +MYDATA:256,<incomplete bytes> before all 256 bytes arrive.
Solution: Configure a longer read timeout, or implement retries in your application.

Issue: Custom URCs not recognized​

Cause: Prefix mismatch (e.g., configured +CUSTOM: but device sends +CUSTOM :).
Solution: Check the device AT manual for exact prefix spelling. The codec uses exact prefix matching.

Issue: Bare prompt (>) not recognized​

Cause: Device sends > without \r\n.
Solution: The codec detects bare > at the end of the buffer. Ensure no trailing whitespace.

Limitations​

  • Case-sensitive: All terminators and prefixes are case-sensitive per AT specification
  • Line-based only: The codec assumes messages are line-delimited; if your device uses a different delimiter, use the Frame Codec or a custom codec
  • No command/response pairing: The codec does not track which response belongs to which command; that's handled by the application
  • Single codec instance: All commands and responses flow through one codec instance; state is shared across all ongoing operations

See Also​