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).
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.
To stream RTT output to your terminal:
commander rtt connect --device EFR32MG22C224F512GN32
You should see something like..
Connecting to J-Link...
Found RTT block at 0x20000000
Connected to RTT channel 0
Hello from EFR32!
Sensor reading: 23.5
Press `Ctrl+C` to disconnect.
If you have multiple J-Links connected, specify which one using `--serialno`:
commander rtt connect --device EFR32MG22C224F512GN32 --serialno 440123456
Find your serial number with:
commander adapter probe
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 30
Option 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 1
After enabling RTT in your firmware you may find the connection fails with:
RTT block not found in device memory
This is normally due to the RTT control block being optimized 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_WriteString or SEGGER_RTT_printf).
Call SEGGER_RTT_Init() early in main() or to 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()` or `SEGGER_RTT_printf()`
You're reading the correct channel (default is 0, use `--terminal N` for 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 EFR32MG22C224F512GN32
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.
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-square
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()
rtt_start(): begin RTT session (call after connect())
rtt_read(channel, num_bytes): read up to N bytes from an up-buffer
rtt_write(channel, data): write bytes to a down-buffer
rtt_get_num_up_buffers(): number of device→host channels
rtt_get_num_down_buffers(): number of host→device channels
rtt_stop(): end RTT session
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);
}