Skip to main content
Read what your firmware prints over SEGGER RTT. With the rtt feature you can also 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 the crate has no error to 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. A hex chunk length is indistinguishable from a line that 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. The extra time covers a search of RAM for the RTT control block, and a retry of 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.