Modem
The runtime gives you a transparent pipe to the nRF9151: bytes you write go to the modem’s UART, bytes the modem sends come back. The runtime adds only the trailing \r\n. AT commands and response parsing are yours.
This is a deliberate design. The WiFi model — a runtime that connects by itself — does not transfer to cellular: airtime costs power and data, and when to attach, connect or publish is an application decision, not a firmware one.
Practically, it means the full nRF9151 AT command set is available to you, including the Nordic Serial Modem commands for TCP, TLS, MQTT and GNSS, without the runtime standing in the way.
An IEC library for the modem (AT commands, network attach, MQTT) is in development: today, from ST, you use the pipe described on this page.
The six channels
Section titled “The six channels”| Channel | Dir | Role |
|---|---|---|
MODEM_OUTPUT (7000) | OUT | 64 bytes of command, terminated by the first zero |
MODEM_INPUT (7001) | IN | 128-byte window over the response |
MODEM_INPUT_LEN (7002) | IN | total bytes received |
MODEM_SEND (7003) | OUT | rising edge: 1 = append and transmit, 2 = append only |
MODEM_RESET_INPUT (7004) | OUT | 1 clears reception |
MODEM_INPUT_OFFSET (7005) | OUT | moves the read window |
The driver’s internal buffers are 2 KB per direction, sized for certificates: a PEM runs 600–1700 bytes, twenty times the channel width. Hence the two piecewise mechanisms — SEND=2 to compose a long command, and the window to read a long response.
Reaching the channels from ST
Section titled “Reaching the channels from ST”The buffers are declared in the device description as structures of single bytes, which ST cannot index by name. The way in is a located variable: declare it at the channel’s address, and you may overlay several views on the same memory.
{attribute 'qualified_only'}VAR_GLOBAL (* Addresses: read the real ones in the IO Mapping tab of the "IO e Modem" node. They shift if channels are added. *) sOut AT %QB8 : STRING(63); (* MODEM_OUTPUT, text view *) abOut AT %QB8 : ARRAY[0..63] OF BYTE; (* same bytes, byte view *) sIn AT %IB5 : STRING(127); (* MODEM_INPUT, text view *) wInLen AT %IW69 : WORD; (* MODEM_INPUT_LEN *) wSend AT %QW36 : WORD; (* MODEM_SEND *) wReset AT %QW37 : WORD; (* MODEM_RESET_INPUT *) wInOff AT %QW38 : WORD; (* MODEM_INPUT_OFFSET *)END_VARSending a command
Section titled “Sending a command”Write the text, pulse MODEM_SEND to 1, then bring it back to 0. The runtime appends \r\n — do not add it yourself.
PROGRAM PLC_PRGVAR nStep : INT; sResp : STRING(127);END_VAR
CASE nStep OF0: GVL.sOut := 'AT+CFUN?'; GVL.wSend := 1; // rising edge: clears RX, sends, adds CR LF nStep := 1;
1: GVL.wSend := 0; // release the trigger nStep := 2;
2: IF GVL.wInLen > 0 THEN // something came back sResp := GVL.sIn; nStep := 3; END_IFEND_CASEMODEM_SEND is edge-triggered: a level of 1 held across cycles transmits once, not every cycle. Bring it back to 0 before the next command.
SEND = 1 also clears the receive buffer and resets MODEM_INPUT_OFFSET first, so what arrives is the answer to this command and not the tail of the previous one.
Commands longer than 64 bytes
Section titled “Commands longer than 64 bytes”Append with SEND = 2 as many times as needed, then close with SEND = 1:
// chunk 1..n-1GVL.sOut := '<first 63 characters>';GVL.wSend := 2; // append only, nothing is transmitted// ... pulse back to 0, load the next chunk, repeat ...
// last chunkGVL.sOut := '<tail>';GVL.wSend := 1; // append, add CR LF, transmit the whole thingThis is how a PEM certificate gets into an AT%CMNG command — see X.509, where a 602-byte command is assembled in 48-byte pieces.
Reading a response longer than 128 bytes
Section titled “Reading a response longer than 128 bytes”MODEM_INPUT_LEN tells you how many bytes arrived in total; MODEM_INPUT shows 128 of them starting at MODEM_INPUT_OFFSET. Walk the window:
GVL.wInOff := 0; // first 128 bytes// read GVL.sIn ...GVL.wInOff := 128; // next 128// read GVL.sIn ...The window applies from the next bus cycle, so read GVL.sIn one cycle after changing the offset.
MODEM_RESET_INPUT := 1 clears the buffer when you want to start clean without sending anything.
What the runtime does at startup
Section titled “What the runtime does at startup”Before the bus cycle starts, the driver:
- Probes with
ATup to eight times, 500 ms apart. Cold, the nRF9151 needs over a second before its AT interpreter answers — without this wait, configuration only worked on warm restarts. - Sends
AT+CFUN=4(radio off). Mandatory:%XSYSTEMMODEcannot be changed with the radio on. - Sends
AT+CPINif a SIM PIN parameter is set. The PIN never reaches the log — only “accepted” or “rejected”. - Sends
AT%XSYSTEMMODEaccording to the system-mode parameter. - Sends
AT+CGDCONTif an APN is configured. - Asks the modem firmware version with
AT#XSMVER— the board’s modem runs Nordic’s Serial Modem — falling back toAT#XSLMVERif there is no answer. The version goes to the log asModem: firmware …. - Sends
AT%CMNG=1,5433,0to find out whether the broker CA is already stored in the modem.
Then it stops, leaving the modem offline:
Modem configurato e OFFLINE: per andare online usare AT+CFUN=1Going online is your first AT command:
GVL.sOut := 'AT+CFUN=1';GVL.wSend := 1;Hardware notes
Section titled “Hardware notes”| Signal | GPIO |
|---|---|
| UART TX / RX | GP16 / GP17 (UART0) |
| CTS / RTS | GP18 / GP19 |
| Reset (active low) | GP29 |
Because the modem owns UART0, COM2 does not exist on this board.
Status
Section titled “Status”Validated on hardware: full bidirectional AT dialogue, chunked transmission, windowed reception, startup configuration, and X.509 certificate storage in the modem.
Not yet validated: network attach and MQTT over the modem — both need a live SIM.