Skip to content

feat(dfu_split): Embassy boot flash peripheral from central - #886

Merged
HaoboGu merged 1 commit into
rmk-rs:mainfrom
Schievel1:embassy-boot-flash-peripheral
Jul 15, 2026
Merged

feat(dfu_split): Embassy boot flash peripheral from central #886
HaoboGu merged 1 commit into
rmk-rs:mainfrom
Schievel1:embassy-boot-flash-peripheral

Conversation

@Schievel1

@Schievel1 Schievel1 commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

This is a follow up PR for #877 which uses embassy-boot to flash the peripheral through the central.

The idea is to minimize the effort of flashing. #877 ensures, that we don't need to open up the keyboard and press the boot0 button to get the RP2040 into bootloader. It is enough to run dfu-util on the host to flash it.
This PR takes this idea further. We embed the peripheral's firmware into the centrals firmware, then during startup of the central, we ask peripheral for it's current firmware CRC. Central calculates the CRC of it's embedded peripheral firmware. If they don't match, peripheral has an old firmware and central needs to flash it.
It then proceeds sending chunks which peripheral puts into its DFU partition, when done central makes this known to the peripheral. Then peripheral resets.
After that this is normal embassy-boot bootloader flash, it copies DFU to ACTIVE, boots it, if it booted, good, if not embassy-boot does a rollback. (That rollback thing is important, because it means we can try again flashing a working firmware without the need to take the keyboard apart).
After boot the peripherals firmware CRCs on central and peripheral (hopefully) match, and we don't flash the peripheral again.

If peripheral misses the central asking for its firmwares CRC (e.g. starts after central) it would not get an update. Therefore we let peripheral send it's firmares CRC proactively at every start and central reacts to it accordingly.

This obviously needs enough flash space on the central to hold the peripherals firmware.

additionally we can use the central as a relay for updating the peripheral via dfu. This uses the altsetting of DFU. (usually this is used to flash different areas of the flash of a chip instead of different chips)
When we run dfu-util -D ./central.bin -a 0 we flash the central. If we run dfu-util -D ./peripheral.bin -a 1 we can flash the peripheral even though only the central is plugged in the host's USB.
For several peripherals we can specify -a 2, -a 3 etc.

The diagram below shows that we check the CRC of every chunk we send, send it to central and wait for an ack before continuing. Of course this could be optimized (for example with a window of 20 chunks that we check the CRC of and so on) but this flashes the examples peripheral firmware (~34kB) via the central as a relay in <4s. I think this is good enough for now.

Currently only supports serial connection. I experiment with bluetooth still, but couldn't get in to work properly.

Example logs:
Peripheral running, central starts up and has newer peripheral firmware:

[INFO ] RMK start! (central RMKRP2040embassy-split/src/central.rs:44)
[INFO ] Flash layout: state @ 0x6000 (4K), active @ 0x7000 (944K), dfu @ 0xF3000 (948K), storage @ 0x1E0000 (128K) (central RMKRP2040embassy-split/src/central.rs:83)
[INFO ] Flash capacity 128 KB, RMK use 128 KB(32 sectors) starting from 0x0 as storage (rmk src/storage/mod.rs:434)
[INFO ] Device enabled (rmk src/usb/mod.rs:451)
[INFO ] Running peripheral manager 0 (rmk split/serial/mod.rs:27)
[INFO ] dfu_split: checking peripheral firmware... (rmk src/split/driver.rs:201)
[INFO ] Device suspended, the Vbus current limit is 500µA (or 2.5mA for high-power devices with remote wakeup enabled). (rmk src/usb/mod.rs:490)
[INFO ] dfu_split: firmware hash mismatch (peripheral=0x2112eded, expected=0x6938687c), starting update (31360 bytes) (rmk src/split/driver.rs:245) <-- new peripheral firmware in central, needs to flash 
[INFO ] dfu_split: sending chunk at offset 0 (256 bytes) (rmk src/split/driver.rs:276)
[INFO ] dfu_split: sending chunk at offset 256 (256 bytes) (rmk src/split/driver.rs:276)
[INFO ] dfu_split: sending chunk at offset 512 (256 bytes) (rmk src/split/driver.rs:276)
... chunks keep coming
[INFO ] dfu_split: sending chunk at offset 30976 (256 bytes) (rmk src/split/driver.rs:276)
[INFO ] dfu_split: sending chunk at offset 31232 (64 bytes) (rmk src/split/driver.rs:276)
[INFO ] dfu_split: all chunks sent, signaling update complete (rmk src/split/driver.rs:312)
[INFO ] dfu_split: peripheral confirmed, update complete (rmk src/split/driver.rs:323)
[WARN ] Framing error (embassy_rp src/uart/buffered.rs:538) <- Peripheral resets itself
[WARN ] Break error (embassy_rp src/uart/buffered.rs:544)
[ERROR] Peripheral message read error: SerialError (rmk src/split/driver.rs:146)
[INFO ] dfu_split: proactive hash from peripheral (0x2112eded), checking... (rmk src/split/driver.rs:171) <- peripheral starts up with new firmware
[INFO ] dfu_split: peripheral firmware hash matches (0x2112eded), no update needed (rmk src/split/driver.rs:176) <- firmware CRCs match now, no update. 

Central runnnig, peripheral starts up:

[INFO ] Device suspended, the Vbus current limit is 500µA (or 2.5mA for high-power devices with remote wakeup enabled). (rmk src/usb/mod.rs:490)
[ERROR] Peripheral message read error: SerialError (rmk src/split/driver.rs:146)
[INFO ] dfu_split: proactive hash from peripheral (0x6938687c), checking... (rmk src/split/driver.rs:171) <- peripheral starts up
[INFO ] dfu_split: firmware hash mismatch (peripheral=0x6938687c, expected=0x67363949), starting update (31264 bytes) (rmk src/split/driver.rs:181) <- firmware hashes don't match, update peripheral with embedded firmware in central
[INFO ] dfu_split: sending chunk at offset 0 (256 bytes) (rmk src/split/driver.rs:276)
[INFO ] dfu_split: sending chunk at offset 256 (256 bytes) (rmk src/split/driver.rs:276)
[INFO ] dfu_split: sending chunk at offset 512 (256 bytes) (rmk src/split/driver.rs:276)
... chunks keep coming
[INFO ] dfu_split: sending chunk at offset 30976 (256 bytes) (rmk src/split/driver.rs:276)
[INFO ] dfu_split: sending chunk at offset 31232 (32 bytes) (rmk src/split/driver.rs:276)
[INFO ] dfu_split: all chunks sent, signaling update complete (rmk src/split/driver.rs:312)
[INFO ] dfu_split: peripheral confirmed, update complete (rmk src/split/driver.rs:323)
[WARN ] Framing error (embassy_rp src/uart/buffered.rs:538) <- peripheral restart
[WARN ] Break error (embassy_rp src/uart/buffered.rs:544)
[ERROR] Peripheral message read error: SerialError (rmk src/split/driver.rs:146)
[INFO ] dfu_split: proactive hash from peripheral (0x67363949), checking... (rmk src/split/driver.rs:171) <- peripheral starts up, sends its firmwares CRC
[INFO ] dfu_split: peripheral firmware hash matches (0x67363949), no update needed (rmk src/split/driver.rs:176) <- firmware flashed successfully, no update needed

The AI overlords drew this for me, all hail to the AI overlords:

┌─────────────────────────────────────────────────────────────────────┐
│                      SPLIT DFU UPDATE                               │
│   (peripheral connects / is connected)                              │
└─────────────────────────────────────────────────────────────────────┘


═══ PHASE 0: HANDSHAKE ── hash comparison ───────────────────────────
Do we need to update the peripheral? 

  Central                           Peripheral
    │                                   │
    │                                   ├── (on boot) FirmwareHashResponse(hash)
    │                                   │   <- read_embedded_firmware_hash()
    │                                   │   = CRC32(__vector_table..__veneer_limit)
    │                                   │
    │  ── or ──                         │
    │                                   │
    ├── FirmwareHashQuery ─────────────>│
    │<── FirmwareHashResponse(hash) ────┤
    │                                   │
    │  hash == expected_hash?           │
    │    ├─ Yes → STOP (up-to-date)     │
    │    └─ No  → ⬇                     │
    │                                   │


═══ PHASE 1: CHUNK TRANSFER ── per-chunk CRC ────────────────────────
During transfer central calculates CRC of every chunk it sends, peripheral does as well and 
sends the CRC back in a FirmwareChunkAck. If they match, central sends the next chunk. 
If they don't match, retry the chunk. 
During sending Central calculates the overall CRC over all chunks sent. (central_crc.update(chunk))

  (outer attempt loop: 1..3)

  Central                           Peripheral
    │                                   │
    │ LED ON                            │
    │ central_crc = Crc32::new()        │
    │                                   │
    │   for chunk in firmware[256]:     │
    │     chunk_crc = CRC32(chunk)      │
    │     central_crc.update(chunk)     │
    │                                   │
    │   retry = 0                       │
    │   ┌─ retry < 3 ─────────────────┐ │
    │   │                             │ │
    ├── FirmwareChunk{offset,data} ──>│ │
    │   │                             │ │
    │   │               handler.write_chunk() — full erase on 1st call
    │   │               chunk_crc = CRC32(chunk) │
    │   │                             │ │
    │   │<─ FirmwareChunkAck{offset,──┤ │
    │   │         crc: chunk_crc}     │ │
    │   │                             │ │
    │   CRC match:                    │ │
    │   ack_crc == chunk_crc?         │ │
    │     ├─ Yes → next chunk         │ │
    │     └─ No  → retry++ ────── ────┘ │
    │                                   │
    │   All chunks acked?               │
    │     ├─ Yes → ⬇                    │
    │     └─ No  → attempt++ → Phase 1  │


═══ PHASE 2: END-TO-END CRC ── flash readback ──────────────────────
After all chunks are done, central compares CRC from chunk updates with the firmare CRC from phase 0, and if ok, central sends FirmwareUpdateComplete to peripheral.
Peripheral calculates the CRC over the firmware in DFU partition, sends FirmwareCrcReport(dfu_crc)
to central. Central compares that dfu_crc to the firmware CRC from phase 0, if match sends  FirmwareCrcOk to peripheral, peripheral answers FirmwareUpdateConfirm, marks updated to embassy-boot (SWAP_MAGIC) and resets. 

  Central                           Peripheral
    │                                   │
    │ central_crc.finalize()            │
    │ == expected_hash?                 │
    │   ├─ No  → ABORT (binary bug!)    │
    │   └─ Yes → ⬇                      │
    │                                   │
    ├── FirmwareUpdateComplete ────────>│
    │                                   │
    │                     handler.compute_dfu_crc()
    │                     = CRC32(whole DFU partition)
    │                     via flash readback (256B blocks)
    │                                   │
    │<── FirmwareCrcReport(dfu_crc) ────┤
    │                                   │
    │  dfu_crc == expected_hash?        │
    │    ├─ Yes → ⬇                     │
    │    └─ No  → send CrcFail ─────┐   │
    │                               │   │
    │                               │   attempt++ → Phase 1
    │                               │   │
    │    ├── FirmwareCrcOk ────────>│   │
    │    │                          │   │
    │    │                handler.mark_updated_and_reset()
    │    │               (only after CrcOk — never into corrupt FW)
    │    │                          │   │
    │    │<─ FirmwareUpdateConfirm ─┤   │
    │    │                          │   │
    │    │  DONE ── LED OFF         │   │
    │    │                          │   │
    │    └── CrCok in 5s? ──────────│   │
    │         No → timeout → abort  │   │
    │                               │   │
    │    └── CrcFail in 5s? ───── ──┤   │
    │         No → timeout → abort ─┘   │


═══ RETRY SUMMARY ════════════════════════════════════════════════════

  Layer           Max     Trigger                    Consequence
  ─────           ───     ───────                    ──────────
  Per-chunk       3×      Ack CRC mismatch           Re-send same chunk
                          or 2s timeout

  Outer attempt   3×      Chunk never acked          Full restart of
                          or E2E CRC mismatch        Phase 1 + 2
                          or CRC timeout

  No retry        —       Central CRC != expected    Abort — binary
                          (central_crc.finalize())   mismatch, not TX


═══ SAFETY GATES ════════════════════════════════════════════════════

  Where               What                          Why
  ─────               ────                          ───
  Peripheral boot     mark_updated_and_reset()       Never boot into
                      only on FirmwareCrcOk          corrupt firmware

  After all chunks    central_crc == expected_hash   Catch binary bugs
                                                    before asking peripheral

  End-to-end          compute_dfu_crc()             Catch silent flash
                      flash readback                write errors

  Per-chunk           CRC32(data) in Ack            Catch bitflips,
                                                    packet loss,
                                                    wrong offset

@Schievel1
Schievel1 force-pushed the embassy-boot-flash-peripheral branch from 4fe7d4c to 5ce436b Compare June 20, 2026 21:22
@Schievel1 Schievel1 changed the title feat(dfu_split): Embassy boot flash peripheral from central (RP2040 only) feat(dfu_split): Embassy boot flash peripheral from central Jun 20, 2026
@Schievel1
Schievel1 force-pushed the embassy-boot-flash-peripheral branch from 82f55ac to f8cac52 Compare June 22, 2026 19:49
@Schievel1
Schievel1 force-pushed the embassy-boot-flash-peripheral branch 16 times, most recently from 396b095 to 6a9363b Compare July 4, 2026 23:14
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown

Size Report

Example main PR Diff .text .data .bss
use_config/nrf52832_ble 391.6 KiB 391.6 KiB +0.00% ⬆️ +4 0 0
use_config/nrf52840_ble 436.5 KiB 436.5 KiB +0.00% 0 0 0
use_config/nrf52840_ble_split (central) 490.4 KiB 490.4 KiB +0.00% ⬆️ -12 0 +16
use_config/nrf52840_ble_split (peripheral) 315.9 KiB 315.9 KiB +0.00% 0 0 0
use_config/pi_pico_w_ble 664.4 KiB 664.4 KiB +0.00% 0 0 0
use_config/rp2040 145.7 KiB 145.7 KiB +0.00% 0 0 0
use_config/rp2040_split (central) 159.2 KiB 159.2 KiB +0.02% ⬆️ +24 0 +16
use_config/rp2040_split (peripheral) 27.8 KiB 27.8 KiB +0.00% 0 0 0
use_config/stm32f1 61.9 KiB 61.9 KiB +0.00% 0 0 0
use_config/stm32h7 99.0 KiB 99.0 KiB +0.00% 0 0 0
use_rust/nrf52832_ble 379.5 KiB 379.5 KiB +0.00% ⬆️ +4 0 0
use_rust/nrf52840_ble 433.1 KiB 433.0 KiB +0.00% ⬇️ -4 0 0
use_rust/nrf52840_ble_split (central) 498.5 KiB 498.6 KiB +0.00% ⬆️ +16 0 +16
use_rust/nrf52840_ble_split (peripheral) 312.3 KiB 312.3 KiB +0.00% 0 0 0
use_rust/pi_pico_w_ble 664.7 KiB 664.7 KiB +0.00% 0 0 0
use_rust/rp2040 145.3 KiB 145.3 KiB +0.00% ⬆️ +4 0 0
use_rust/rp2040_split (central) 158.0 KiB 158.1 KiB +0.07% ⬆️ +112 0 +16
use_rust/rp2040_split (peripheral) 28.2 KiB 28.2 KiB +0.00% 0 0 0
use_rust/stm32f1 61.3 KiB 61.3 KiB +0.00% 0 0 0
use_rust/stm32h7 119.7 KiB 119.7 KiB +0.00% ⬆️ +4 0 0
use_config/nrf52832_ble — 391.6 KiB → 391.6 KiB (+0.00% ⬆️)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
 362352	   4588	  34016	 400956	  61e3c	rmk-nrf52832

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
 362348	   4588	  34016	 400952	  61e38	rmk-nrf52832

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%    +578  [ = ]       0    .debug_str
  +0.0%     +24  [ = ]       0    .strtab
  +0.0%     +10  [ = ]       0    .debug_loc
  +0.0%      +4  +0.0%      +4    .text
  -7.3%      -4  [ = ]       0    [Unmapped]
  -1.1%     -86  [ = ]       0    .debug_abbrev
  -0.1%    -229  [ = ]       0    .debug_line
  -0.0%    -705  [ = ]       0    .debug_info
  -0.0%    -408  +0.0%      +4    TOTAL
use_config/nrf52840_ble — 436.5 KiB → 436.5 KiB (+0.00%)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
 394820	   4596	  47576	 446992	  6d210	rmk-nrf52840

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
 394820	   4596	  47576	 446992	  6d210	rmk-nrf52840

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%    +116  [ = ]       0    .debug_info
  +0.5%     +39  [ = ]       0    .debug_abbrev
  +0.0%     +31  [ = ]       0    .strtab
  +0.0%      +6  [ = ]       0    .debug_str
  -0.0%     -40  [ = ]       0    .debug_line
  +0.0%    +152  [ = ]       0    TOTAL
use_config/nrf52840_ble_split (central) — 490.4 KiB → 490.4 KiB (+0.00% ⬆️)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
 451480	   4652	  46072	 502204	  7a9bc	central

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
 451492	   4652	  46056	 502200	  7a9b8	central

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.1% +2.36Ki  [ = ]       0    .debug_str
  +0.0%    +282  [ = ]       0    .debug_info
  [ = ]       0  +0.0%     +16    .bss
  -0.0%      -8  [ = ]       0    .debug_aranges
  -0.0%     -12  -0.0%     -12    .text
  -0.0%     -16  [ = ]       0    .debug_frame
 -29.4%     -20  [ = ]       0    [Unmapped]
  -0.0%     -28  [ = ]       0    .strtab
  -0.1%    -201  [ = ]       0    .debug_line
  -0.0%    -253  [ = ]       0    .debug_loc
  +0.0% +2.11Ki  +0.0%      +4    TOTAL
use_config/nrf52840_ble_split (peripheral) — 315.9 KiB → 315.9 KiB (+0.00%)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
 291812	   4468	  27152	 323432	  4ef68	peripheral

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
 291812	   4468	  27152	 323432	  4ef68	peripheral

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%     +19  [ = ]       0    .debug_line
  +0.0%      +1  [ = ]       0    .debug_str
  +1.8%      +1  [ = ]       0    [Unmapped]
  -0.0%      -3  [ = ]       0    .strtab
  -0.0%     -10  [ = ]       0    .debug_loc
  +0.0%      +8  [ = ]       0    TOTAL
use_config/pi_pico_w_ble — 664.4 KiB → 664.4 KiB (+0.00%)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
 626456	      0	  53860	 680316	  a617c	rmk-pi-pico-w

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
 626456	      0	  53860	 680316	  a617c	rmk-pi-pico-w

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%     +19  [ = ]       0    .debug_str
  +0.0%      +5  [ = ]       0    .debug_info
  +0.0%      +5  [ = ]       0    .strtab
  +3.8%      +2  [ = ]       0    [Unmapped]
  -0.0%      -1  [ = ]       0    .debug_line
  -0.0%      -2  [ = ]       0    .debug_loc
  +0.0%     +28  [ = ]       0    TOTAL
use_config/rp2040 — 145.7 KiB → 145.7 KiB (+0.00%)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
 134428	      0	  14788	 149216	  246e0	rmk-rp2040

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
 134428	      0	  14788	 149216	  246e0	rmk-rp2040

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%      +5  [ = ]       0    .debug_info
  +0.0%      +3  [ = ]       0    .strtab
  +3.9%      +2  [ = ]       0    [Unmapped]
  +0.0%      +1  [ = ]       0    .debug_line
  +0.0%      +1  [ = ]       0    .debug_str
  +0.0%     +12  [ = ]       0    TOTAL
use_config/rp2040_split (central) — 159.2 KiB → 159.2 KiB (+0.02% ⬆️)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
 147384	      0	  15652	 163036	  27cdc	central

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
 147360	      0	  15636	 162996	  27cb4	central

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%    +442  [ = ]       0    .debug_str
  +0.0%    +299  [ = ]       0    .debug_info
  +0.1%     +32  [ = ]       0    .symtab
  +0.0%     +24  +0.0%     +24    .text
  [ = ]       0  +0.1%     +16    .bss
   +26%     +12  [ = ]       0    [Unmapped]
  +0.0%      +3  [ = ]       0    .strtab
  -0.0%     -66  [ = ]       0    .debug_line
  -0.5%    -456  [ = ]       0    .debug_ranges
  -0.1%    -470  [ = ]       0    .debug_loc
  -0.0%    -180  +0.0%     +40    TOTAL
use_config/rp2040_split (peripheral) — 27.8 KiB → 27.8 KiB (+0.00%)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
  25612	     60	   2796	  28468	   6f34	peripheral

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
  25612	     60	   2796	  28468	   6f34	peripheral

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%      +9  [ = ]       0    .debug_line
  +0.0%      +5  [ = ]       0    .strtab
  +0.0%      +4  [ = ]       0    .debug_str
  -4.4%      -2  [ = ]       0    [Unmapped]
  +0.0%     +16  [ = ]       0    TOTAL
use_config/stm32f1 — 61.9 KiB → 61.9 KiB (+0.00%)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
  56516	     28	   6804	  63348	   f774	rmk-stm32f1

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
  56516	     28	   6804	  63348	   f774	rmk-stm32f1

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%      +4  [ = ]       0    .debug_info
  +0.0%      +2  [ = ]       0    .debug_line
  -4.3%      -2  [ = ]       0    [Unmapped]
  +0.0%      +4  [ = ]       0    TOTAL
use_config/stm32h7 — 99.0 KiB → 99.0 KiB (+0.00%)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
  92148	    264	   8992	 101404	  18c1c	rmk-stm32h7

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
  92148	    264	   8992	 101404	  18c1c	rmk-stm32h7

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.1%    +145  [ = ]       0    .debug_line
  +0.0%      +5  [ = ]       0    .debug_info
  -0.2%    -126  [ = ]       0    .strtab
  -0.1% -1.46Ki  [ = ]       0    .debug_str
  -0.0% -1.44Ki  [ = ]       0    TOTAL
use_rust/nrf52832_ble — 379.5 KiB → 379.5 KiB (+0.00% ⬆️)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
 351020	   4588	  33000	 388608	  5ee00	rmk-nrf52832

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
 351016	   4588	  33000	 388604	  5edfc	rmk-nrf52832

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%      +4  +0.0%      +4    .text
  -0.0%      -2  [ = ]       0    .strtab
 -11.5%      -7  [ = ]       0    [Unmapped]
  -0.0%     -16  [ = ]       0    .debug_ranges
  -0.2%     -18  [ = ]       0    .debug_abbrev
  -0.0%     -32  [ = ]       0    .debug_loc
  -0.0%    -137  [ = ]       0    .debug_str
  -0.1%    -337  [ = ]       0    .debug_line
  -0.0%    -439  [ = ]       0    .debug_info
  -0.0%    -984  +0.0%      +4    TOTAL
use_rust/nrf52840_ble — 433.1 KiB → 433.0 KiB (+0.00% ⬇️)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
 394988	   4588	  43864	 443440	  6c430	rmk-nrf52840

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
 394992	   4588	  43864	 443444	  6c434	rmk-nrf52840

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +5.8%      +3  [ = ]       0    [Unmapped]
  -0.0%      -4  -0.0%      -4    .text
  -0.0%    -123  [ = ]       0    .debug_line
  -0.0%    -134  [ = ]       0    .debug_info
  -0.1%    -483  [ = ]       0    .strtab
  -0.1% -4.50Ki  [ = ]       0    .debug_str
  -0.1% -5.22Ki  -0.0%      -4    TOTAL
use_rust/nrf52840_ble_split (central) — 498.5 KiB → 498.6 KiB (+0.00% ⬆️)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
 454924	   4652	  50952	 510528	  7ca40	central

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
 454908	   4652	  50936	 510496	  7ca20	central

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0% +2.29Ki  [ = ]       0    .debug_str
  +0.0%    +727  [ = ]       0    .debug_info
  +0.0%    +122  [ = ]       0    .debug_line
  +0.0%     +32  [ = ]       0    .symtab
  +0.0%     +25  [ = ]       0    .strtab
  [ = ]       0  +0.0%     +16    .bss
  +0.0%     +16  [ = ]       0    .debug_frame
  +0.0%     +16  +0.0%     +16    .text
   +38%     +15  [ = ]       0    [Unmapped]
  +0.0%      +8  [ = ]       0    .debug_aranges
  +0.0%      +8  [ = ]       0    .debug_ranges
  -0.1%    -656  [ = ]       0    .debug_loc
  +0.0% +2.59Ki  +0.0%     +32    TOTAL
use_rust/nrf52840_ble_split (peripheral) — 312.3 KiB → 312.3 KiB (+0.00%)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
 289892	   4468	  25400	 319760	  4e110	peripheral

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
 289892	   4468	  25400	 319760	  4e110	peripheral

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%     +33  [ = ]       0    .debug_str
  +0.0%     +18  [ = ]       0    .debug_line
  +0.0%     +13  [ = ]       0    .strtab
  +3.6%      +2  [ = ]       0    [Unmapped]
  -0.0%      -2  [ = ]       0    .debug_info
  +0.0%     +64  [ = ]       0    TOTAL
use_rust/pi_pico_w_ble — 664.7 KiB → 664.7 KiB (+0.00%)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
 626608	      0	  54004	 680612	  a62a4	rmk-pi-pico-w

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
 626608	      0	  54004	 680612	  a62a4	rmk-pi-pico-w

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%     +10  [ = ]       0    .debug_info
  +0.0%     +10  [ = ]       0    .debug_loc
  +4.7%      +3  [ = ]       0    [Unmapped]
  -0.0%      -9  [ = ]       0    .debug_line
  -0.0%     -17  [ = ]       0    .strtab
  -0.0%     -25  [ = ]       0    .debug_str
  -0.0%     -28  [ = ]       0    TOTAL
use_rust/rp2040 — 145.3 KiB → 145.3 KiB (+0.00% ⬆️)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
 134120	      0	  14660	 148780	  2452c	rmk-rp2040

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
 134116	      0	  14660	 148776	  24528	rmk-rp2040

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%     +18  [ = ]       0    .debug_line
  +0.0%     +14  [ = ]       0    .debug_info
  +0.0%      +4  +0.0%      +4    .text
  -0.0%      -5  [ = ]       0    .strtab
 -24.5%     -12  [ = ]       0    [Unmapped]
  -0.0%     -71  [ = ]       0    .debug_str
  -0.0%     -52  +0.0%      +4    TOTAL
use_rust/rp2040_split (central) — 158.0 KiB → 158.1 KiB (+0.07% ⬆️)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
 146484	      0	  15436	 161920	  27880	central

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
 146372	      0	  15420	 161792	  27800	central

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.1% +1.69Ki  [ = ]       0    .debug_str
  +0.0%    +549  [ = ]       0    .debug_info
  +0.1%    +112  +0.1%    +112    .text
  +0.1%    +105  [ = ]       0    .debug_line
  +0.1%     +64  [ = ]       0    .debug_ranges
  +0.1%     +32  [ = ]       0    .symtab
  [ = ]       0  +0.1%     +16    .bss
   +29%     +13  [ = ]       0    [Unmapped]
  +0.0%      +7  [ = ]       0    .strtab
  -0.1%    -230  [ = ]       0    .debug_loc
  +0.1% +2.33Ki  +0.1%    +128    TOTAL
use_rust/rp2040_split (peripheral) — 28.2 KiB → 28.2 KiB (+0.00%)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
  25712	     60	   3060	  28832	   70a0	peripheral

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
  25712	     60	   3060	  28832	   70a0	peripheral

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%      +9  [ = ]       0    .debug_line
  +0.0%      +4  [ = ]       0    .debug_str
  +0.0%      +4  [ = ]       0    .strtab
  -2.8%      -1  [ = ]       0    [Unmapped]
  +0.0%     +16  [ = ]       0    TOTAL
use_rust/stm32f1 — 61.3 KiB → 61.3 KiB (+0.00%)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
  55928	     28	   6772	  62728	   f508	rmk-stm32f1

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
  55928	     28	   6772	  62728	   f508	rmk-stm32f1

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%     +63  [ = ]       0    .debug_str
  +0.0%      +9  [ = ]       0    .debug_line
  +0.0%      +9  [ = ]       0    .strtab
  +0.0%      +4  [ = ]       0    .debug_info
  -1.7%      -1  [ = ]       0    [Unmapped]
  +0.0%     +84  [ = ]       0    TOTAL
use_rust/stm32h7 — 119.7 KiB → 119.7 KiB (+0.00% ⬆️)

cargo size (PR):

   text	   data	    bss	    dec	    hex	filename
 107288	    320	  14940	 122548	  1deb4	rmk-stm32h7

cargo size (main):

   text	   data	    bss	    dec	    hex	filename
 107284	    320	  14940	 122544	  1deb0	rmk-stm32h7

Bloaty diff (PR vs main):

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%     +69  [ = ]       0    .debug_str
  +0.0%     +17  [ = ]       0    .strtab
  +0.0%     +10  [ = ]       0    .debug_loc
  +0.0%      +4  +0.0%      +4    .text
  -0.0%      -4  [ = ]       0    .debug_info
  -0.1%    -156  [ = ]       0    .debug_line
  -0.0%     -60  +0.0%      +4    TOTAL

@Schievel1
Schievel1 force-pushed the embassy-boot-flash-peripheral branch from 6a9363b to f667f20 Compare July 4, 2026 23:24
@Schievel1
Schievel1 marked this pull request as ready for review July 4, 2026 23:24
@HaoboGu

HaoboGu commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

I think it's worth to support external nor flash as the storage used by dfu split. The remaining flash space is quite tight even for nRF52840(which has 1M flash)

@Schievel1

Schievel1 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

I think it's worth to support external nor flash as the storage used by dfu split. The remaining flash space is quite tight even for nRF52840(which has 1M flash)

For the embedded peripheral firmware in central or the flashing peripheral via central relay?
For the latter we can not do that, because the bootloader would need access to the external flash. This creates tons of problems with different chips with different flash types so you would have a bouquet of bootloaders supporting different boards.
For the first, iirc nrf52840 isn't supported for this anyway right now, since dfu_split only supports uart split and RMK does not support nrf uart splits. This works for RP2040 only at the moment, and RP2040 has 2M or more, which is plenty for firmware that isn't bloated by the BLE stack.

Comment thread rmk/src/split/driver.rs
@Schievel1
Schievel1 force-pushed the embassy-boot-flash-peripheral branch from f667f20 to 8fe2c60 Compare July 14, 2026 19:30
@Schievel1
Schievel1 marked this pull request as draft July 14, 2026 19:31
@Schievel1
Schievel1 force-pushed the embassy-boot-flash-peripheral branch from 8fe2c60 to 929a494 Compare July 15, 2026 07:54
@Schievel1
Schievel1 force-pushed the embassy-boot-flash-peripheral branch from 929a494 to ec8847f Compare July 15, 2026 07:56
… central

Signed-off-by: Pascal Jäger <pascal.jaeger@leimstift.de>
@Schievel1
Schievel1 force-pushed the embassy-boot-flash-peripheral branch from ec8847f to 87a5541 Compare July 15, 2026 08:04
@Schievel1
Schievel1 marked this pull request as ready for review July 15, 2026 14:21
@HaoboGu

HaoboGu commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Thanks a lot, this is really cool

@HaoboGu
HaoboGu merged commit f1031f1 into rmk-rs:main Jul 15, 2026
55 checks passed
@Schievel1

Copy link
Copy Markdown
Contributor Author

Thanks a lot, this is really cool

Thanks for merging this in.
I think this really is a unique feature of RMK and no one else has this. I've been dreaming about being able to do this since the very start of my split keyboard career. 😄
I have a keyboard with a display on the peripheral and no USB connector on the outside. Every time I wanted to try how something looks on the display I had to take the keyboard apart.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants