defmt-persist
A persistent defmt logger that survives resets.
This crate provides a defmt::global_logger that stores logged messages in a ring buffer,
similar to defmt-brtt. Unlike defmt-brtt, the buffer state persists across resets, so panic
messages and logs leading up to them can be transmitted after the device restarts. Basically
the combination of panic-persist (see Panic Handler section) and defmt-brtt.
Setup
Reserve a memory region in your linker script for the persist buffer. This region must be outside any section to prevent program initialization from zeroing or modifying it on boot.
Define __defmt_persist_start and __defmt_persist_end symbols pointing to this region.
Example memory.x before modification:
MEMORY
{
FLASH : ORIGIN = 0x00000000, LENGTH = 512K
RAM : ORIGIN = 0x20000000, LENGTH = 64K
}
After modification to reserve a 1K region:
MEMORY
{
FLASH : ORIGIN = 0x00000000, LENGTH = 512K
RAM : ORIGIN = 0x20000000, LENGTH = 63K
DEFMT_PERSIST : ORIGIN = 0x2000FC00, LENGTH = 1K
}
__defmt_persist_start = ORIGIN(DEFMT_PERSIST);
__defmt_persist_end = ORIGIN(DEFMT_PERSIST) + LENGTH(DEFMT_PERSIST);
Then call init early in your program:
let Ok = init else ;
Use the returned Consumer to read and transmit buffered logs:
#
#
Panic Handler
To capture panic messages that survive resets, define a panic handler that logs via defmt before resetting:
!
After reset, the panic message will be available in the persist buffer and can be read using
the Consumer from init(). See panic_test.rs for a
complete example.
Alternatively, panic-probe can be used for
hardfault-on-panic behavior.
Bootloader Considerations
If your system uses a bootloader, the bootloader's linker script must also reserve/don't touch
the same memory region. Otherwise, the bootloader may use this memory for its stack or heap,
corrupting the persisted logs before the application starts. The bootloader can also call
init to read and transmit persisted logs from the application before launching it.
Features
rtt: Also output logs via RTT (default: enabled)async-await: Enable async API for waiting on new data (default: enabled)ecc: Add ECC cache flush for MCUs with ECC-protected RAM (32-bit or 64-bit), e.g. STM32H7/H5 (default: enabled)
Testing
Run the library unit tests:
Run the full QEMU-based integration testsuite (requires qemu-system-arm):
This runs tests for persistence across resets, buffer corruption recovery, async API, and ring buffer wraparound.
To run a single example in QEMU during development:
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.