Skip to main content
Read what your firmware prints over SEGGER RTT, and — with the rtt feature — write back into its down-channel so a cargo test can drive an interactive console. There are two paths, and they behave differently.
Prefer the interactive session. The one-way stream yields raw HTTP chunked-transfer framing rather than clean payload — see the warning below.

Interactive sessions

Enabling the feature

The rtt feature implies blocking. There is no async equivalent.

Opening a session

The gdbserver must already be running. Calling rtt_interactive() without connecting first fails with Error::Stream and the message No debugger connection found for net 'debug1'. Start one first.

RttOptions

channel selects the channel in both directions. chunk_size applies only to interactive sessions; the one-way HTTP stream ignores it.

Methods

Method Reference

read(timeout: Duration) -> Result<Vec<u8>>

Whatever up-channel bytes arrive within timeout.
read() waits out the full timeout when the target is idle. In a poll loop use try_read() instead, or every iteration costs the whole timeout.

try_read() -> Result<Vec<u8>>

Bytes already received, without waiting. Returns an empty vector when nothing is buffered.

wait_for(needle: &[u8], timeout: Duration) -> Result<Vec<u8>>

Accumulate output until needle appears. Returns: everything up to and including the needle. Bytes after it stay buffered for the next read. An empty needle returns immediately; a miss is Error::Timeout.

write(data: &[u8]) -> Result<()> and write_str(s: &str) -> Result<()>

Write into the target’s RTT down-channel.
Writing needs a firmware-declared down buffer on that channel. defmt-rtt alone provides only the up buffer, and without a down buffer the target silently discards what you write — the call still returns Ok(()). That is a target-side fact, not a transport failure, so there is nothing the crate can report.

stop() -> Result<()>

Stop cleanly. Consumes the session. Dropping it also stops the session, but stop() surfaces errors rather than swallowing them.

The one-way stream

debug.rtt() and debug.rtt_with(&RttOptions) return an RttStream, which implements std::io::Read. It needs no cargo feature and no Socket.IO.
RttStream yields raw HTTP chunked-transfer framing, not clean payload. A read returns bytes like 384\r\nblink 46823 period=500ms\n..., where 384 is a hex chunk length. Wrapping it in a BufReader and iterating lines produces "64", "384" and empty strings interleaved with real firmware output — and a hex chunk length is indistinguishable from a line your firmware printed.This is tracked as lager-rs#5. Until it is fixed, use rtt_interactive() where you need parseable output. The interactive path is clean.

Examples

Drive a firmware console and assert on the reply

Collect boot output without blocking on an idle target

Notes

  • The box’s RTT telnet port takes a single client. A second session on the same probe and channel is refused with Error::Stream and RTT port 9090 is already in use by another session. Two different channels on one probe are separate ports and can run at once.
  • Bytes are raw. defmt output is compressed binary and must be piped through defmt-print -e <elf>; wait_for only helps against a plain-text console.
  • The connect confirmation timeout is 30 seconds, wider than UART’s 15, because the box may search RAM for the RTT control block and retry the telnet attach while the gdbserver settles.
  • Sessions carry the gateway bearer token on the Socket.IO handshake, so a gated box works with no extra setup.
  • Requires box software 0.35.0 or newer. An older box gives Error::UnsupportedByBox.