Import
Methods
The Net-based API provides methods for embedded debugging operations.Method Reference
Net.get(name, type=NetType.Debug)
Get a debug net by name.
Returns: Debug Net instance
Note: The debug net must be configured with the target device name stored in the
channel field (e.g., ‘NRF52840_XXAA’, ‘R7FA0E107’).
connect(speed=None, transport=None, *, force=False, ignore_if_connected=False, script=None, openocd_config=None, jlink_script=None, halt=False)
Connect to the target device (start the gdbserver for this probe). The backend
(J-Link or OpenOCD) is chosen automatically from the probe.
connect() classifies script before it applies openocd_config or jlink_script. If you give script and one of those two, the explicit parameter wins only when script is for the same backend. A script for the other backend raises ValueError first.
Returns: dict - Status dictionary with connection information
Raises: If a gdbserver already runs for the probe and you pass neither force nor ignore_if_connected, connect() raises. The error is RuntimeError on OpenOCD and JLinkAlreadyRunningError (from lager.debug) on J-Link.
A script override takes effect only when the gdbserver relaunches. If a server is
already up, pass
force=True to restart it with the new script.
ignore_if_connected=True returns early and does not relaunch. On J-Link, it
still repoints the script for later operations. On OpenOCD, the override cfg
stays in effect for each relaunch, including a self-heal relaunch, until
disconnect(). Invalid input means
a missing path that is not valid base64, or an empty string. The net ignores such
input silently, and the script that it materialized earlier stays in effect.The override is scoped to this net and this session. Lager writes it to a
per-net path rather than to the box-wide config that the net record and the HTTP
debug service share. disconnect() clears the override. Two debug nets that
connect with different scripts therefore no longer clobber one another.disconnect()
Disconnect from the target device.
dict - Status dictionary
reset(halt=False)
Reset the device.
Returns:
str - Combined output from reset operation
Self-heal (both backends): Right after a flash() there is a short window
where the debug server is not reachable yet. On J-Link the cause is that the
restarted GDB server’s PID is not observable yet. On OpenOCD the cause is a
transient daemon or RPC fault. A bare call raises inside that window. reset()
retries with bounded backoff on both the J-Link and OpenOCD backends, and it
restarts a server only when one is genuinely down. It never tears down a server
that is already up, so an attached RTT session stays intact. Callers no longer
need their own retry wrappers.
DA1469x exception: On DA1469x, flash() deliberately leaves the server down.
The flash ends in a software reset rather than a server restart, and the
documented flow is an explicit, halt-aware reconnect. So the self-heal still
retries on DA1469x, but it never auto-starts a server. A server that is genuinely
down still surfaces the original error, exactly as before. The self-heal never
brings a server up unhalted, because an unhalted server can return garbage
QSPI-XIP reads.
halt()
Halt the target where it is, without a reset. OpenOCD only.
flash() needs a running daemon. Without one, it raises RuntimeError and tells you to call connect() first.
Returns: str - Combined output from the halt operation
Like reset(), halt() retries across the short window after flash() when the server is not reachable yet.
This is not the same as reset(halt=True). That runs OpenOCD’s reset halt,
which pulses nRESET and re-enters through the reset vector. halt() issues a bare
halt instead, so the core stops where it is, and nRESET is never touched.
The distinction matters on parts that execute in place out of QSPI. After you
program such a part, reset(halt=True) re-runs the bootloader rather than
stopping on the image you just wrote. An unhalted re-attach risks garbage XIP
reads. Halting in place is how you attach after a program without disturbing
the image.
J-Link has no standalone halt-in-place primitive, because
reset_device and
gdb_reset both reset first. On that backend halt() raises
NotImplementedError, and the error names the halt-first .JLinkScript as the
supported route.flash(firmware_path, flash_address=None)
Flash firmware to the device.
Returns:
str - Combined output from flash operation
Raises: RuntimeError when the probe reports that the flash failed, on both
backends. On J-Link, that is a flash that programmed nothing or a failed read-back
compare, the same lines that fail lager debug <net> flash. The message holds the
failing line and the output.
Note: A .bin has no embedded address, so flash() raises without
flash_address rather than defaulting to 0x0. Pass the target’s flash base:
STM32 0x08000000, nRF52 0x00000000, DA1469x QSPI 0x16000000.
DA1469x on OpenOCD: mainline OpenOCD has no flash driver for the DA1469x’s
external QSPI. On that family, flash() and erase() drive the RAM-resident
flash_loader instead, the same path lager debug <net> flash takes. Pass the
absolute XIP address (0x16000000), exactly as on J-Link. A missing loader
raises rather than falling back to OpenOCD’s program, which cannot reach QSPI.
- An address outside
0x16000000–0x17FFFFFFraises before the box touches the probe. The error saysflash address 0x... is outside the DA1469x QSPI XIP window. - Flash a
.bin. The loader writes the bytes of the file unchanged, so it does not decode a.hexor.elffile. - The loader needs
flash_loader.elfandflash_loader.elf.binin/home/www-data/customer-binaries/openocd/flash-loaders/da1469x/in the container. On the box host, that directory is~/third_party/customer-binaries/openocd/flash-loaders/da1469x/.LAGER_FLASH_LOADERS_DIRreplaces the parent directory. - OpenOCD has no built-in target config for this family, so the net needs an
openocd_config. - A dropped debug read does not stop the loader. The loader retries the read until the deadline of its step.
- A failure raises with the last progress line of the loader. If the erase stage already ran, the message says that the board can be blank. Flash it again.
erase(start=None, length=None)
Erase the flash of the target. With no arguments, on most targets this is a full
chip erase, which erases ALL flash memory including protection settings.
On a DA1469x, erase() erases only the first 1 MiB of the external QSPI, from
0x16000000. This applies on both backends. The OpenOCD backend uses the
flash_loader. The J-Link backend uses a range erase, and a LAGER_ERASE_RANGE
line in the JLinkScript of the net sets a different range.
start and length set the range instead, on either backend and any target.
Pass both or neither. start is an absolute address, and on a DA1469x the XIP
address, exactly as flash() takes it. On a DA1469x the range must lie inside
0x16000000–0x17FFFFFF, and it replaces both the 1 MiB default and the
LAGER_ERASE_RANGE line. On any other target the range replaces the full chip
erase. A range the target cannot erase raises ValueError before the box touches
the probe. When a range applies, the first line of the output names it, for
example Erasing 0x16000000-0x161FFFFF (2 MiB).
start(int, optional): First address to erase, given together withlengthlength(int, optional): Number of bytes to erase fromstart
str - Combined output from erase operation
Raises: RuntimeError when J-Link erased nothing: it did not attach, or it
did not confirm the erase. The message holds the failing line and the output.
The self-heal does not run the erase again. On OpenOCD, a failed erase raises as
before.
read_memory(address, length)
Read memory from the target device.
Returns:
bytes - Memory data
Self-heal: like reset(), read_memory() retries with bounded backoff across
the brief post-flash() settling window on both backends. It reconnects only when
no server is up, and it never disturbs a live session. erase() behaves the same
way. The same DA1469x exception applies. No server is auto-started, so a
post-flash DA1469x read raises clearly rather than returning unhalted-XIP garbage.
status()
Get the current connection status.
dict - running (bool), pid and backend.
This reports whether a gdbserver process is up for the probe. It is not a
statement about the target: a server can outlive the part it was attached to.
The CLI’s lager debug <net> status reports both states separately.
session(speed=None, transport=None, connect=True, ignore_if_connected=True, disconnect_on_exit=True)
Scoped debug session. It connects on entry and guarantees teardown on exit. The
safe flash → attach-RTT → reset ordering is therefore encoded once, rather than
rediscovered in every script. The with target is the net itself, so the full
surface (flash, rtt_defmt, reset, read_memory, …) is available inside the
block.
Returns: a context manager yielding the debug net.
Why it pairs with RTT: the in-process RTT reader is reconnect-aware (see
below). A
flash() or reset() inside the session can bounce the GDB server.
Such a bounce does not kill a log stream that you opened in the same block.
rtt(channel=0, search_addr=None, search_size=None, chunk_size=None)
Create an RTT (Real-Time Transfer) session for bidirectional communication with the target device.
Returns: RTT context manager with methods:
read_some(timeout)- Read available data with timeout (returns bytes or None)write(data)- Write data to target (accepts bytes or str)
connect() first.
Reconnect-aware (both backends): the RTT reader re-attaches by itself when the
GDB server or daemon restarts under it.
-
J-Link. A
flash()briefly frees the probe’s USB and restarts the GDB server on the same ports, which drops the RTT socket. Areset()does the same through its Commander grab. The reader re-attaches to the same RTT telnet port instead of going silent. A long-livedread_some()orrtt_defmt()loop therefore keeps producing across a flash. -
OpenOCD. The daemon stays up across an ordinary flash, so the socket rarely
drops. If it does drop, from a daemon force-restart or an rtt-server bounce, the
reader re-runs
rtt setupandrtt server startand re-attaches.
rtt() and rtt_defmt() take no reconnect argument, so
reconnection is always on.
Examples
Flash Firmware and Reset
Chip Erase Before Programming
Read Memory
CLI Commands (Recommended)
For most use cases, the CLI provides a simpler interface:RTT Streaming
SEGGER Real-Time Transfer (RTT) enables high-speed bidirectional communication with embedded devices during debugging (faster than UART, no timing impact).Decoding defmt logs with rtt_defmt()
rtt_defmt(elf, channel=0) opens an RTT session and pipes it through defmt-print (preinstalled on the Lager Box), yielding decoded log lines instead of raw bytes. The elf must be the exact firmware flashed on the target — defmt needs its symbol metadata to decode.
rtt_defmt() returns a context manager exposing:
Like the CLI pipe, the RTT stream never ends on its own. Bound your read loop with
a time budget or a line count, then exit the
with block.
Driving the firmware while decoding its logs
write() makes a decoding session bi-directional, so a script can send a command
and assert on the decoded response. Decoding is one-way — defmt-print only sees
the up-channel — so writes bypass it and go straight to the target. This is the
only way to do both at once. The RTT telnet port accepts a single client, so you
cannot open a raw rtt() alongside a rtt_defmt().
CLI Alternative: For interactive tailing, pipe the CLI directly:
lager debug <net> gdbserver --box <box> --rtt 2>/dev/null | defmt-print -e build/app.elf. See the CLI Debug Reference. Use rtt_defmt() when you need to assert on log content inside a test script; use the pipe when you just want to watch logs.
Supported Devices
J-Link supports a wide range of ARM Cortex-M and other microcontrollers. Common device names:
For a complete list, see SEGGER’s supported devices.
Supported Hardware
Notes
- Debug nets must be configured with the target device name in the
channelfield - The CLI (
lager debug) is recommended for most use cases - Python Net API is intended for advanced automation scripts running on the Lager Box
- Always call
disconnect()when finished to release the debug probe - Use
erase()for a full chip erase that also clears protection settings (on DA1469x, a 1 MiB QSPI range).erase(start, length)erases one range. - RTT requires an active debug connection (see RTT Streaming section above)

