Skip to content

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.

ChannelDirRole
MODEM_OUTPUT (7000)OUT64 bytes of command, terminated by the first zero
MODEM_INPUT (7001)IN128-byte window over the response
MODEM_INPUT_LEN (7002)INtotal bytes received
MODEM_SEND (7003)OUTrising edge: 1 = append and transmit, 2 = append only
MODEM_RESET_INPUT (7004)OUT1 clears reception
MODEM_INPUT_OFFSET (7005)OUTmoves 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.

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_VAR

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_PRG
VAR
nStep : INT;
sResp : STRING(127);
END_VAR
CASE nStep OF
0: 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_IF
END_CASE

MODEM_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.

Append with SEND = 2 as many times as needed, then close with SEND = 1:

// chunk 1..n-1
GVL.sOut := '<first 63 characters>';
GVL.wSend := 2; // append only, nothing is transmitted
// ... pulse back to 0, load the next chunk, repeat ...
// last chunk
GVL.sOut := '<tail>';
GVL.wSend := 1; // append, add CR LF, transmit the whole thing

This 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.

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.

Before the bus cycle starts, the driver:

  1. Probes with AT up 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.
  2. Sends AT+CFUN=4 (radio off). Mandatory: %XSYSTEMMODE cannot be changed with the radio on.
  3. Sends AT+CPIN if a SIM PIN parameter is set. The PIN never reaches the log — only “accepted” or “rejected”.
  4. Sends AT%XSYSTEMMODE according to the system-mode parameter.
  5. Sends AT+CGDCONT if an APN is configured.
  6. Asks the modem firmware version with AT#XSMVER — the board’s modem runs Nordic’s Serial Modem — falling back to AT#XSLMVER if there is no answer. The version goes to the log as Modem: firmware ….
  7. Sends AT%CMNG=1,5433,0 to 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=1

Going online is your first AT command:

GVL.sOut := 'AT+CFUN=1';
GVL.wSend := 1;
SignalGPIO
UART TX / RXGP16 / GP17 (UART0)
CTS / RTSGP18 / GP19
Reset (active low)GP29

Because the modem owns UART0, COM2 does not exist on this board.

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.