EFR32 — Accessing Serial Output via RTT from the Command Line#
Setting up Segger RTT for fast debug output on Silicon Labs EFR32 microcontrollers — practical configuration notes from an embedded engineering consultancy.
Who Is This For?#
This post is for embedded developers working with EFR32 devices and a J-Link–based debugger (e.g. WSTK) who already have SEGGER RTT compiled into their firmware and want practical ways to connect, view logs, and script RTT access using Simplicity Commander and Python (pylink).
Introduction#
The EFR32 series from Silicon Labs supports SEGGER Real-Time Transfer (RTT) for debug output. Unlike UART, RTT requires no dedicated pins and has virtually zero timing impact on your firmware. It works whenever a J-Link debugger is connected — which includes the built-in debugger on WSTK development kits.
The method to capture this output from the command line is using Simplicity Commander, Silicon Labs’ CLI tool for device programming and debugging.
Basic Connection#
To stream RTT output to your terminal:
commander rtt connect --device EFR32MG22C224F512GN32You should see something like:
Connecting to J-Link...
Found RTT block at 0x20000000
Connected to RTT channel 0
Hello from EFR32!
Sensor reading: 23.5Press Ctrl+C to disconnect.
If you have multiple J-Links connected, specify which one using --serialno:
commander rtt connect --device EFR32MG22C224F512GN32 --serialno 440123456Find your serial number with:
commander adapter probeAutomated Capture for Testing#
For automated test harnesses or CI pipelines, two options are particularly useful:
- Timeout — exit after N seconds of no data received
- End marker — exit when a specific string appears in the output
Using option 1 allows a script to capture output for a fixed duration:
commander rtt connect --device EFR32MG22C224F512GN32 --timeout 30Option 2 allows the capture to stop precisely when tests complete. If your firmware prints a marker like === TEST END === after running tests:
commander rtt connect --device EFR32MG22C224F512GN32 --endmarker "=== TEST END ==="A typical test automation script combines flashing, reset, and capture:
#!/bin/bash
DEVICE=EFR32MG22C224F512GN32
# Flash and reset
commander flash test_firmware.bin --device $DEVICE
commander device reset --device $DEVICE
# Capture until tests complete
commander rtt connect --device $DEVICE --endmarker "=== TEST END ===" | tee results.log
# Check for failures
grep -q "0 Failures" results.log && exit 0 || exit 1Troubleshooting#
After enabling RTT in your firmware you may find the connection fails with:
RTT block not found in device memoryThis is normally due to the RTT control block being optimised out by the linker because nothing references it at startup. The solution is to ensure:
- SEGGER RTT sources added to the project (
SEGGER_RTT.c, config headers, etc.) - At least one up-buffer configured and used (e.g.
SEGGER_RTT_WriteStringorSEGGER_RTT_printf) - Call
SEGGER_RTT_Init()early inmain(), or mark the RTT block as used in your linker script:
__attribute__((used)) SEGGER_RTT_CB _SEGGER_RTT;Another common issue is no output appearing despite a successful connection. Check that:
- Your firmware is actually calling
SEGGER_RTT_Write()orSEGGER_RTT_printf() - You’re reading the correct channel (default is 0, use
--terminal Nfor others) - The device hasn’t crashed before reaching your print statements
To verify the J-Link and device connection are working:
commander device info --device EFR32MG22C224F512GN32Other Useful Options#
A few additional flags worth knowing:
--noreset— attach without resetting the device (useful for catching output from already-running code)--ip 192.168.1.100— connect to a J-Link over the network--blockaddress 0x20000000— manually specify RTT block location if auto-detection fails
Once working, RTT provides a clean, scriptable way to capture device output without any IDE dependency — perfect for automated testing or production line diagnostics.
Bidirectional RTT with Python#
Simplicity Commander can send and receive RTT data via commander rtt connect, but its interaction model is terminal-like and less scriptable than using the pylink Python API directly.
Install it with:
pip install pylink-squareReading and Writing#
Here’s a complete example that connects to an EFR32, reads RTT output, and sends commands:
import pylink
import time
import threading
def rtt_reader(jlink, stop_event):
"""Background thread to continuously read RTT output."""
while not stop_event.is_set():
data = jlink.rtt_read(0, 1024) # list of ints
if data:
chunk = bytes(data).decode("utf-8", errors="replace")
print(chunk, end="", flush=True)
def main():
jlink = pylink.JLink()
# Connect to J-Link
jlink.open()
jlink.set_tif(pylink.enums.JLinkInterfaces.SWD)
jlink.connect('EFR32MG22C224F512GN32')
# Start RTT
jlink.rtt_start()
# Wait for RTT control block to be found
while True:
try:
num_up = jlink.rtt_get_num_up_buffers()
num_down = jlink.rtt_get_num_down_buffers()
print(f"RTT started: {num_up} up buffer(s), {num_down} down buffer(s)")
break
except pylink.errors.JLinkRTTException:
time.sleep(0.1)
# Start reader thread
stop_event = threading.Event()
reader = threading.Thread(target=rtt_reader, args=(jlink, stop_event))
reader.start()
# Interactive loop - send user input to device
try:
while True:
cmd = input()
if cmd:
jlink.rtt_write(0, list(cmd.encode() + b'\n'))
except KeyboardInterrupt:
pass
finally:
stop_event.set()
reader.join()
jlink.rtt_stop()
jlink.close()
if __name__ == '__main__':
main()Key Functions#
rtt_start()— begin RTT session (call afterconnect())rtt_read(channel, num_bytes)— read up to N bytes from an up-bufferrtt_write(channel, data)— write bytes to a down-bufferrtt_get_num_up_buffers()— number of device→host channelsrtt_get_num_down_buffers()— number of host→device channelsrtt_stop()— end RTT session
Firmware Side#
To receive data on the device, your firmware needs to read from the RTT down-buffer:
char cmd_buffer[64];
unsigned num_bytes = SEGGER_RTT_Read(0, cmd_buffer, sizeof(cmd_buffer));
if (num_bytes > 0) {
// Process received command
handle_command(cmd_buffer, num_bytes);
}

