Bootloader Configuration (embassy-boot)
embassy-boot is a library of the embassy framework that is used to build bootloaders. RMK supports DFU firmware updates via embassy-boot for RP2040 and nRF52840 / nRF52833. An embassy-boot based bootloader splits flash into ACTIVE and DFU slots, providing safe updates with automatic rollback on failure. This is an optional feature of RMK, the default bootloaders of the devices can still be used as usual without runtime updates via USB DFU.
DFU is experimental. Enabling it repartitions your flash, and the partition layout, the [dfu]
options on this page, and the Rust API can change in any release. A firmware built for one partition
layout cannot be flashed over a bootloader built for another, so treat a board with DFU enabled as
one you may have to re-flash with a probe.
An embassy-boot based bootloader for both platforms lives in rmk-boot. RMK integrates with it through a memory.x file that the bootloader generates: build rmk-boot for your chip and it writes its matching rmk-memory.x into the rmk-boot project directory.
Rename that file to memory.x and place it next to your project's Cargo.toml, so the firmware and the bootloader agree on the partition layout.
At runtime RMK reads partition offsets from linker symbols embedded in memory.x — you never compute or hardcode partition addresses yourself.
See the flashing guide for step-by-step instructions on getting the bootloader and RMK flashed.
RP2040
Add a [dfu] section to your keyboard.toml or use the Rust API directly.
nRF52840 / nRF52833
Add a [dfu] section to your keyboard.toml or use the Rust API directly.
When using FlashDfuHandler (in run_all!) or run_rmk_split_peripheral(), mark_booted() is called automatically — no manual call needed. You only need rmk::dfu::mark_booted(&mut state_partition) when using a embassy-boot based bootloader to flash the firmware that still provides safe firmware updates and rollback. E.g. rmk-boot's DFU flash feature without it's noswap feature and you don't use DFU flashing/ FlashDfuHandler in RMK itself.
Partition layout
The bootloader divides flash into regions. All offsets and sizes come from linker symbols in the memory.x file from rmk-boot. The default layout with rmk-boot (2MB RP2040, 32K storage) is:
The DFU partition size follows embassy-boot guidelines, the additional page is used for status information during flashing.
The [dfu] section is optional and configures only DFU behaviour (LED, unlock keys). Partition offsets are read at link time from memory.x — you do not set state_offset, dfu_offset, or flash_size in keyboard.toml.
Custom bootloader
If you built your own embassy-boot bootloader, add these eight symbols with matching values to your memory.x (all values are flash-relative offsets):
Make sure FLASH : ORIGIN in your MEMORY block starts at your ACTIVE partition. RMK's partitions_from_linkerscript() picks up the symbols at runtime.
DFU LED (optional)
A GPIO pin for the DFU LED.
See the LED behavior table for the full state machine.
DFU lock (optional, feature dfu_lock)
Physical key positions that unlock DFU firmware downloads. Requires the dfu_lock Cargo feature. Keys are identified by matrix position (row, col), not by keycode.
See the DFU lock section for the unlock workflow.
Choose keys that are easy to press simultaneously but not commonly pressed together accidentally.
External Flash DFU (optional, feature dfu_ext)
When your firmware grows too large for the internal DFU/ACTIVE partition split, you can move the DFU download slot to an external SPI NOR flash chip. With dfu_ext active, the internal DFU partition is removed entirely — the ACTIVE region expands to fill the freed space, giving you roughly double the space for your firmware. The DFU image is written to and read from the external chip.
Requirements:
- A 25-series SPI NOR flash chip with JEDEC‑standard commands (Winbond W25Q64JV, Macronix MX25R, ISSI IS25LP, etc.)
- A matching
dfu_extbuild of rmk-boot for your chip - The
dfu_extCargo feature in your RMK firmware
Partition layout with dfu_ext
All internal flash regions stay at fixed offsets. The DFU download slot moves entirely to the external chip.
The active size is flash_size - bootloader - state - storage (no DFU partition is carved out of the internal flash).
If you build your own bootloader, the ACTIVE partition and the external DFU partition must each be a multiple of 64 KB when using the built-in W25Q driver. The erase size is set to 64 KB to make the swap faster, so partitions that are not 64K-aligned will fail to swap correctly.
The default rmk-boot build already aligns ACTIVE this way — its build.rs rounds the ACTIVE size down to a 64 KB multiple (960 KB on a 1 MB nRF52840, 448 KB on a 512 KB nRF52833).
Default SPI pins
The bootloader hard‑codes default SPI pins. Match them in your keyboard.toml (or change the bootloader source for a custom board):
The SPI pins in keyboard.toml and in the bootloader source must agree. If you change the wiring, update both places.
Configuration
Enable the feature in Cargo.toml:
On a split keyboard, each side can have its own [dfu] section — [split.central.dfu] / [split.peripheral.dfu] — which completely replaces the global [dfu] for that side. This lets the central use an external flash DFU partition while the peripheral keeps an internal one (and vice versa), or use different SPI pins per side.
This requires two different bootloader builds and two different memory.x files — one per side: flash the central with a dfu_ext bootloader build and the peripheral with a regular one, and give each binary its matching linker script. The build.rs of the rp2040_dfu_split_dfu_ext examples (use_config and use_rust) shows how to select the right memory.x per binary (memory-central.x / memory-peripheral.x). See Split keyboard DFU configuration.
driver = "custom"
If you use a different flash chip or want to provide your own driver, set driver = "custom" and point init_fn to a function in your crate:
The function must have the signature:
RMK only requires NorFlash from your driver — the DFU download partition and its erase/size handling are built on top by RMK, so you don't need to slice the chip yourself.
Building the bootloader
Build rmk-boot with the dfu_ext feature and your board's flash size:
If you need different SPI pins, edit src/rp2040.rs or src/nrf52840.rs of rmk-boot and change the pin assignments before building. The EXT_FLASH_SIZE constant (default 8 MB) lives in src/main.rs.
No-swap bootloader (rmk-boot noswap)
If your firmware no longer fits into the ACTIVE partition but you don't need USB DFU updates, build rmk-boot with the noswap feature. It removes the DFU download partition entirely and grows ACTIVE to fill the whole remaining flash:
The bootloader then becomes a minimal "jump to ACTIVE" loader: no swap, no automatic rollback, and no USB DFU interface in RMK. (But for nRF528xx rmk-boot itself has a USB DFU mode in the bootloader)
Trade-offs:
- Firmware updates via DFU are not possible in RMK's runtime. (but the board can still be flashed via RP2040's built in UF2 bootloader or via USB DFU mode of
rmk-booton nRF528xx) - No rollback on a failed boot
Build:
Rename the generated rmk-memory.x to memory.x as usual and build your RMK firmware without the DFU features (dfu_rp / dfu_nrf) — no [dfu] section in keyboard.toml is needed.
Also activate the set-vtor feature of cortex-m-rt in your projects Cargo.toml.
noswap is mutually exclusive with dfu_ext.