Skip to main content
  1. Blogs/

EFR32 — Accessing Serial Output via RTT from the Command Line

·865 words·5 mins
Owen O'Hehir
Author
Owen O’Hehir
Dublin-based embedded Linux, IoT and Matter consultant. Helping startups and product teams design, prototype and ship connected devices.

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

Automated 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 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

Troubleshooting
#

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 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_WriteString or SEGGER_RTT_printf)
  • Call SEGGER_RTT_Init() early in main(), 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() 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

Other 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-square

Reading 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 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

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);
}