Flashing Firmware

The last step is to flash compiled firmware to your microcontroller. RMK supports flashing the firmware via uf2 bootloader or debug probe.

Use uf2 bootloader

Flashing using uf2 bootloader is easy: set your board to bootloader mode, then a USB drive should appear in your computer. Copy the uf2 firmware to the USB drive, and that's it!

If you're using macOS, an error might appear, you can safely ignore it.

Tips for nRF52840

For nRF52840, you need to check whether your have a uf2 bootloader flashed to your board. If you can enter bootloader mode, there will be an USB drive shown in your computer. If there's INFO_UF2.TXT in the USB drive, you have it!

The Adafruit_nRF52_Bootloader for nRF52840 has a special MBR section at the beginning of the flash, ensure that the flash origin in memory.x starts with 0x00001000:

MEMORY
{
  /* These values correspond to the nRF52840 WITH Adafruit nRF52 bootloader */
  FLASH : ORIGIN = 0x00001000, LENGTH = 1020K
  RAM : ORIGIN = 0x20000008, LENGTH = 255K
}

If you have a debug probe and don't want to use the bootloader, use the following memory.x config:

{
  /* These values correspond to the nRF52840 WITHOUT Adafruit nRF52 bootloader */
  FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
  RAM : ORIGIN = 0x20000000, LENGTH = 256K
}

Tips for the STM32F411 Blackpill / F401 Bluepill

For STM32 MCUs the memory.x file us usually created by the embassy-stm32 crate and a memory.x file doesnt have to be created. This will result in a file similar to this: (you can find that in the target directory)

MEMORY
{
    FLASH : ORIGIN = 0x08000000, LENGTH =  512K /* BANK_1_REGION_1 + BANK_1_REGION_2 + BANK_1_REGION_3 */
    RAM   : ORIGIN = 0x20000000, LENGTH =  128K /* SRAM */
}

Where the LENGTH value may vary, depending on the chip. This setup works fine for the default stm32-dfu bootloader and the debug probe.

Use tinyuf2 bootloader for stm32

For some STM32 there is a tinyuf2 bootloader available. Using that you can just drop the finished uf2 file onto the device like it's an USB stick.

Because the bootloader takes up 64K at the beginnning of the first sector, we need to shift the flash address accordingly. To do this first run cargo build --release at least once. get your generated memory.x file out of the target directory:

# pwd in project root
find ./target -name "memory.x"
# example output:
# ./target/thumbv7em-none-eabi/release/build/embassy-stm32-588e747b0f59bbf9/out/memory.x

(The exact path may vary, because of the hash) copy this to your project root:

cp ./target/thumbv7em-none-eabi/release/build/embassy-stm32-dbac6b60f6194464/out/memory.x ./

The file will look something like this:

MEMORY
{
    FLASH : ORIGIN = 0x08000000, LENGTH =  512K /* BANK_1_REGION_1 + BANK_1_REGION_2 + BANK_1_REGION_3 */
    RAM   : ORIGIN = 0x20000000, LENGTH =  128K /* SRAM */
}

For tinyuf2 shift the FLASH region by 64K and adjust the length accordingly:

MEMORY
{
    FLASH : ORIGIN = 0x08000000 + 64K, LENGTH =  512K - 64K  /* tinyuf2 on stm32 is 64K */
    RAM   : ORIGIN = 0x20000000, LENGTH =  128K /* SRAM */
}

Then build the firmware using

cargo make uf2 --release

To flash, double tap NRST an the board, the board will go into bootloader mode and appear as a USB flash drive. Drop the *.uf2 files into the drive, the board will then reboot into the firmware.

Use debug probe

If you have a debug probe like daplink, jlink or stlink(stm32 only), things become much easier: connect it with your board and host, make sure you have installed probe-rs, then just run

cargo run --release

Then the command configured in .cargo/config.toml will be executed. The firmware will be flashed to your microcontroller and run automatically, yay!

Use esptool

Many of the Espressif ESP32 based controllers have a physical USB port, but cannot be used as a USB keyboard. The USB port should support USB JTAG/serial debug, and this can be used to flash the RMK firmware.

There are several tools able to do this, but we recommend espflash Serial flasher utility for Espressif SoCs and modules. This knows about the typical secondary boot loader and appropriate address within the flash to save the RMK application.

This can be installed with cargo (recommended if you are compiling your RMK firmware locally) or get the pre-compiled binary from their latest release (recommended if you are not compiling your RMK firmware locally and don't have cargo setup).

If you plug in the controller the operating system should recognise the USB serial device, and on Linux or macOS it should appear under /dev/ - like /dev/cu.usbmodem31201 in this example:

$ espflash flash --monitor <keyboard-name>-central.elf --port /dev/cu.usbmodem31201
[2026-07-07T08:11:58Z INFO ] Serial port: '/dev/cu.usbmodem31201'
[2026-07-07T08:11:58Z INFO ] Connecting...
[2026-07-07T08:11:58Z INFO ] Using flash stub
Chip type:         esp32h2 (revision v0.1)
Crystal frequency: 32 MHz
Flash size:        4MB
Features:          BLE
MAC address:       48:31:b7:c2:9a:ca
App/part. size:    1,098,048/4,128,768 bytes, 26.60%
[00:00:07] [========================================]      38/38      0x10000  Verifying... OK!                                                         [2026-07-07T08:12:07Z INFO ] Flashing has completed!
Commands:
    CTRL+R    Reset chip
    CTRL+C    Exit
...

The tool can often infer the USB port automatically, but it is wise to be explicit and extra careful if you have more than one such device connected! You can also set the chip type etc explicitly, but that shoud be detected automatically.

Assuming it flashes, the controller should reset and start running RMK and advertise on Bluetooth. The espflash tool will stay connected over USB serial, and monitor the keyboard. You should see additional INFO (and perhaps WARN or ERROR) logging at the terminal until you press control+c to exit from espflash.

You can also run the monitoring directly, which can be useful during testing/debugging:

 espflash monitor --port /dev/cu.usbmodem31201
...

Next Steps

Now you've finished the RMK's user guide. For the next steps, you can checkout features that RMK provides or explore how to configure RMK keyboard: