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:
- Line buffering: Accumulates lines (bytes between
\r\ndelimiters) - Empty line skipping: Ignores lines containing only whitespace
- Terminator detection: Stops buffering when a terminator is received
- URC detection: Returns immediately for self-contained unsolicited result codes
- 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):
| Terminator | Meaning |
|---|---|
OK | Successful response |
ERROR | Generic error |
+CME ERROR: code | GSM/3GPP mobile equipment error |
+CMS ERROR: code | SMS (Mobile Service) error |
ERROR: message | Error with description |
NO CARRIER | Connection lost |
BUSY | Device busy |
NO ANSWER | Call unanswered |
NO DIALTONE | No dial tone |
> | Prompt for data input (bare character, no \r\n required) |
CONNECT or CONNECT speed | Connection established |
COMMAND NOT SUPPORT | ESP-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:
- Identifies known length-prefixed patterns (or custom ones you define)
- Parses the last numeric field before the final comma
- Extracts that many bytes as the payload
- 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:
- Payload unchanged: The codec sends your message payload as-is
- Terminator added: Appends
\r\nif not already present - 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
| Field | Type | Purpose |
|---|---|---|
at_codec_custom_urc_prefixes | Multiline text | Define custom self-contained URC prefixes (one per line) |
at_codec_custom_length_prefixed_urcs | Multiline text | Define 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:
- Identifies the length prefix (256)
- Waits for exactly 256 bytes
- 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:
- Buffers "OK" and returns it (terminator found)
- Parses
+MYRECV:0,"topic",12,and extracts length = 12 - Reads exactly 12 bytes: "hello world"
- 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
- Codec Layer — Codec trait and lifecycle
- Transport Layer — How codecs integrate with transports
- Lua Script API — Message processing with Lua