ADR-0024: Keyboard MCU Selection — RP2040-class (Adafruit KB2040)
Context
Section titled “Context”ADR-0018 accepted “any QMK-compatible board” as the keyboard controller family — Pro Micro (ATmega32U4), RP2040-class (KB2040, Sea-Picro), nice!nano, or equivalent. The exact part was deferred to the hardware agent’s bring-up call, with selection criteria left at “availability, USB-C preference, program space.”
ADR-0023 changed the calculus. The keyboard MCU now also hosts a 3-axis MEMS accelerometer (LIS3DH) on its I²C bus, runs a threshold-event state machine, and emits a vendor-defined USB HID report alongside its existing keyboard interface. The ADR-0023 §2 memory analysis projects ~14 KB flash and ~1.9 KB SRAM headroom on ATmega32U4 after the v1 sensor pipeline is folded in — workable for v1, but with no margin for the deferred v2 cart-FFI surface (ADR-0023 §9), HID vendor report polish, or ordinary firmware drift over the v0.1 → v0.2 bring-up cycle.
Concurrent with ADR-0023, the keyboard PCB design entered Flux. The Flux design conversation surfaced the same flash-budget concern from a different angle — the controller swap is cheaper now, before the PCB is fabbed, than at any later point. The Pro Micro and KB2040 share the same Pro Micro form factor, so the swap is electrically and mechanically a drop-in: same pinout footprint, same socket, same case clearance. The cost of pinning to RP2040-class is essentially zero in this window; the cost of not pinning is a re-spin if v2 work hits the flash ceiling.
This ADR closes that decision.
Forcing functions
Section titled “Forcing functions”- ADR-0023 sensor pipeline. The accelerometer driver, ring buffer, threshold detector, and HID vendor report add ~3.5 KB flash and ~600 B SRAM to the keyboard firmware. On ATmega32U4 (32 KB flash / 2.5 KB SRAM) this consumes ~56% of flash and ~40% of SRAM after the QMK base + 31-key matrix is in. Workable — but with no margin.
- v2 cart-FFI surface deferred but committed. ADR-0023 §9 names a future
(motion-vector)/(motion-event)FFI primitive set. That surface lives on the keyboard MCU side of the wire — additional report decoders, possibly a small calibration table, possibly per-cartridge motion thresholds. The flash for that work is uncomfortably tight on ATmega32U4 and trivially in budget on RP2040. - Flux PCB design active. The keyboard PCB is in placement/routing in Flux right now. The controller footprint is already on the board. Swapping the part now is a rename + re-import; swapping after the first fab run is a re-spin at $30–60 + 2 weeks lead time.
- USB-C is the path forward. Pro Micro variants ship with Micro-B; KB2040 ships with USB-C. Internal cable selection on the Pelican 1170 inset panel is easier with USB-C, and the Adafruit ecosystem standardizes on USB-C across the Feather / KB2040 / Sea-Picro line.
Constraints
Section titled “Constraints”- Pro Micro form factor compatibility. The PCB footprint is already designed around the Pro Micro 33 × 18 mm castellated outline. The selected part must be a drop-in mechanical replacement.
- Stock QMK support. No custom HAL or vendor-specific build chain. The QMK community port for RP2040 is mature as of 2026-Q1; this ADR rides that maturity.
- USB HID class compliance. The existing keyboard descriptor + ADR-0023’s vendor-defined report on the same interface must compose without composite-device gymnastics.
- Power envelope. The keyboard subsystem stays inside the ~50–60 mA typical from the keyboard-electrical-spec; RP2040 is comparable to ATmega32U4 in active draw at the polling loads we run.
- Sourcing redundancy. ≥2 sources at qty 1 and qty 100 for the selected part, plus an explicit fallback inside the same family for supply continuity.
Decision
Section titled “Decision”The KN-86 keyboard MCU is the Adafruit KB2040 (RP2040 in Pro Micro form factor) — preferred. Sea-Picro (RP2040, same form factor) is the named fallback. Pro Micro ATmega32U4 is no longer in the running for v0.1.
Concrete commitments:
1. Selected part — Adafruit KB2040
Section titled “1. Selected part — Adafruit KB2040”- Part: Adafruit KB2040 (PID 5302). RP2040 SoC (dual ARM Cortex-M0+ @ 133 MHz), 264 KB SRAM, 8 MB QSPI flash on-module, USB-C, 18 GPIO broken out, exposed SWD pads, Pro Micro castellated form factor (33 × 18 mm).
- Rationale (vs. Pro Micro ATmega32U4 and Sea-Picro, the other two finalists):
- Memory headroom. RP2040 + 8 MB flash is ~256× the ATmega32U4 flash budget and ~100× the SRAM. The ADR-0023 sensor pipeline consumes <0.1% of available flash. The v2 cart-FFI surface fits trivially. No flash-budget anxiety for the lifetime of the device.
- USB-C native. Internal cable routing on the Pelican 1170 inset panel uses a single USB-C cable to the internal hub IC (ADR-0018 §5), matching the Adafruit ecosystem default.
- SWD debugging exposed. Bring-up benefits from real on-chip debugging rather than
printfover USB. KB2040 breaks SWCLK and SWDIO out as test pads on the underside. - Stock QMK support. RP2040 is a first-class QMK target as of 2026-Q1. KB2040 specifically is in the QMK keyboards/handwired list with a working
info.json. - Sourcing. Adafruit (PID 5302, ~$8.95 qty 1), DigiKey (1528-5302-ND, ~$8.95 qty 1, ~$7.50 qty 100), Mouser (485-5302), and PiShop. Stock has been consistent through 2026.
- Datasheet: Adafruit KB2040 product page and RP2040 datasheet (Raspberry Pi).
- Sourcing options (≥2): Adafruit PID 5302, DigiKey 1528-5302-ND, Mouser 485-5302.
- Qty pricing: $8.95 qty 1 / ~$7.50 qty 100 (DigiKey April 2026 reference).
2. Named fallback — Sea-Picro
Section titled “2. Named fallback — Sea-Picro”If KB2040 supply contracts, the Sea-Picro (RP2040 in Pro Micro form factor, sourced through the mech-keeb community vendors — Beekeeb, KeyHive, Boardsource) is the named fallback. Same SoC, same form factor, same QMK support, slightly different USB-C connector geometry. Bring-up swap is a footprint check and a flash re-pour; no schematic change.
Pro Micro ATmega32U4 is explicitly not the v0.1 fallback. If both KB2040 and Sea-Picro fail simultaneously, the correct response is to delay v0.1 and re-evaluate the RP2040-class market, not to drop back to ATmega32U4 with a 56%-full flash budget.
3. Pin assignment (RP2040 → keyboard subsystem) — canonical as-built
Section titled “3. Pin assignment (RP2040 → keyboard subsystem) — canonical as-built”Update — amended by ADR-0031 §3 (2026-06-06): The custom-PCB pin map below is superseded by the Ferris Sweep adoption. Under ADR-0031 the keyboard is split into two halves, each with its own KB2040; matrix wiring is fixed by the Sweep PCB (not authored here). The LIS3DH I²C convention (D4/D5 = GP4/GP5) is preserved on the master (LEFT) half per ADR-0031 §3 + §5. The table below is retained as design history for the prior custom-PCB path. The MCU selection (KB2040), bootloader, build chain, and flash-budget conclusion (§5) carry forward unchanged — only the quantity (1 → 2) and the matrix-pin assignments (fixed-by-Sweep-PCB rather than authored) flip.
Second update — amended by ADR-0032 §3 (2026-06-07): With 2× trackpoint added (one per half), the master-half LIS3DH I²C peripheral moves from I²C0 (D4/D5 = GP4/GP5) to I²C1 (D6/D7 = GP6/GP7) — the trackpoint consumes I²C0 for PS/2 over the holykeebs adapter. The INT1 reservation on D6/GP6 from the table below is released (v1 LIS3DH is polling-only at 25 Hz per ADR-0023 §5; interrupt-driven motion deferred to v2 with a new pin allocation if ever wanted). The KB2040 silkscreen-default I²C0 convention is preserved for the trackpoint; the LIS3DH uses the second I²C peripheral instead. See ADR-0032 §3 for the authoritative per-half pin table.
The Flux PCB schematic (committed 2026-04-27) uses the following GPIO assignment on KB2040, expressed in both the KB2040 silkscreen labels (Adafruit’s Pro Micro-compatible D/A naming) and the underlying RP2040 GP-numbered pins:
| Function | KB2040 silkscreen | RP2040 pin | Notes |
|---|---|---|---|
| ROW0 | D0 | GP0 | Active-low input, internal pull-up (shares pin with UART0 TX — UART unused on this device) |
| ROW1 | D1 | GP1 | Active-low input, internal pull-up (shares pin with UART0 RX — UART unused) |
| ROW2 | D2 | GP2 | Active-low input, internal pull-up |
| ROW3 | D3 | GP3 | Active-low input, internal pull-up (also silkscreen-labeled “MOSI”; SPI alt unused) |
| LIS3DH SDA | D4 (silk: SDA) | GP4 | I²C0 SDA (per ADR-0023). KB2040 silkscreen-default I²C pin. |
| LIS3DH SCL | D5 (silk: SCL) | GP5 | I²C0 SCL (per ADR-0023). KB2040 silkscreen-default I²C pin. |
| LIS3DH INT1 | D6 | GP6 | Reserved for future interrupt-driven motion detection |
| COL0 | D7 | GP7 | Active-low output (push-pull during scan) |
| COL1 | D8 | GP8 | Active-low output |
| COL2 | D9 | GP9 | Active-low output |
| COL3 | A3 | GP29 | Active-low output (analog-capable pin used as digital) |
| COL4 | A2 | GP28 | Active-low output (analog-capable pin used as digital) |
| COL5 | A1 | GP27 | Active-low output (analog-capable pin used as digital) |
| COL6 | A0 | GP26 | Active-low output (analog-capable pin used as digital) |
| COL7 | D10 | GP10 | Active-low output |
| RESET | RUN | — | KB2040 onboard reset; mapped to PCB reset button |
| USB | USB-C | — | Native RP2040 USB |
| SWD | SWCLK / SWDIO | — | Test pads on PCB underside |
Pins explicitly marked NC on U1: MISO (GP20), SCK (GP18), VIN_RAW, USB_D+, USB_D-. The “MOSI” silkscreen alias is not separately marked NC because it is the same physical pin as D3, which is wired to ROW3.
History — drafted vs. as-built
Section titled “History — drafted vs. as-built”The v1 draft of this ADR specified ROW0–3 on GP2–GP5, COL0–7 on GP6–GP10/GP20/GP26/GP27, I²C on GP12/GP13, and INT1 on GP28. That mapping was internally consistent but did not align with the KB2040’s silkscreen-labeled defaults. When Flux applied the schematic migration on 2026-04-27, it remapped to the as-built table above for two reasons that improve the design:
- D4/D5 (GP4/GP5) is the silkscreen-labeled I²C0 default on KB2040. Adafruit prints SDA/SCL on the board itself at those pins. The Adafruit ecosystem (CircuitPython examples, Arduino tutorials, QMK community keyboards) uses this default. The drafted GP12/GP13 alternate is electrically valid but a less-conventional choice that would have created hand-wiring confusion against the silkscreen.
- Silkscreen alignment for the prototype builder.
docs/plans/2026-04-27-keyboard-prototype-build-guide.mdtells the builder to wire to physical labels on the KB2040 board. Aligning the as-built schematic with those labels minimizes the chance of a wiring error during hand-assembly.
The cost of accepting the as-built mapping: ROW0/ROW1 sit on GP0/GP1, which lose UART0 capability. The KN-86 keyboard subsystem has no UART use case (USB HID is the sole transport), so this is free. The drafted mapping is preserved here as the rejected alternative; the as-built table is canonical.
Final pin map is canonical above; downstream docs cite this ADR.
4. QMK firmware target
Section titled “4. QMK firmware target”- MCU:
rp2040 - Bootloader:
rp2040(drag-drop UF2 to the BOOTSEL volume; no Caterina, no avrdude) - USB: Native RP2040 hardware USB Full Speed
- Build chain: stock QMK with
qmk compile -kb handwired/kn86 -km default - Keymap: existing 31-key keymap from ADR-0022 ports unchanged — keymap layer is MCU-agnostic
5. Flash budget (post-decision)
Section titled “5. Flash budget (post-decision)”| Subsystem | Flash | SRAM |
|---|---|---|
| QMK base + 31-key matrix | ~14 KB | ~512 B |
| LIS3DH driver + ring buffer + threshold detector (ADR-0023) | ~3.5 KB | ~600 B |
| HID vendor report decoder | ~1 KB | ~256 B |
| Reserved for v2 cart-FFI surface (ADR-0023 §9) | up to ~8 KB | up to ~2 KB |
| Total estimated | ~26.5 KB | ~3.4 KB |
| Available (KB2040) | 8 MB | 264 KB |
| Headroom | >99% free | >98% free |
The flash-budget anxiety that drove this ADR is eliminated. The v0.1 firmware uses <1% of available KB2040 flash.
Options Considered
Section titled “Options Considered”Option A: KB2040 (RP2040, Adafruit) (ACCEPTED)
Section titled “Option A: KB2040 (RP2040, Adafruit) (ACCEPTED)”Described above. Pinned for memory headroom, USB-C, SWD debugging, sourcing reliability, and Adafruit ecosystem alignment.
Option B: Sea-Picro (RP2040, mech-keeb community)
Section titled “Option B: Sea-Picro (RP2040, mech-keeb community)”Same SoC, same form factor, same QMK support. Sourced through Beekeeb / KeyHive / Boardsource rather than Adafruit. Lower unit cost ($7–9) but smaller distribution footprint.
Rejected as primary because: distribution is concentrated in mech-keeb hobby vendors, which are sensitive to small inventory swings. Adafruit’s broader distribution (Adafruit + DigiKey + Mouser) is more durable for a launch slate. Promoted to the named fallback because the technical equivalence is exact.
Option C: Pro Micro ATmega32U4 (REJECTED — was the prior-spec default)
Section titled “Option C: Pro Micro ATmega32U4 (REJECTED — was the prior-spec default)”The original ADR-0018 default. ATmega32U4 with QMK over USB Micro-B.
Rejected because: ADR-0023 closes the headroom case. After v1 sensor pipeline + HID vendor report, ATmega32U4 sits at ~56% flash with no v2 margin. Any flash-budget pressure during bring-up would force a part swap mid-design — at higher cost than swapping now. Additionally: Micro-B vs USB-C creates internal-cable asymmetry with the rest of the Adafruit ecosystem in the BOM.
Option D: nice!nano (nRF52840, Pro Micro form factor)
Section titled “Option D: nice!nano (nRF52840, Pro Micro form factor)”Wireless-capable Pro Micro footprint board, used widely in the wireless-mech-keeb scene.
Rejected because: the KN-86 keyboard is wired-only per the canonical hardware spec. The nice!nano’s wireless stack is dead weight in flash and runtime power. RP2040 is the cleaner wired-only choice in the same form factor.
Option E: Bare RP2040 SoC + custom regulator/USB design
Section titled “Option E: Bare RP2040 SoC + custom regulator/USB design”Skip the breakout module entirely; reflow the bare RP2040 onto the keyboard PCB with a custom regulator, crystal, and USB-C front-end.
Rejected because: premature optimization for a single-digit-quantity v0.1 build. The breakout module is $8.95; the bare-SoC path costs PCB area, layout time, and a failure surface (regulator selection, crystal layout, USB-C ESD network) that buys nothing at this scale. Revisit at >100-unit production runs; not for v0.1 / v0.2.
Trade-off Analysis
Section titled “Trade-off Analysis”Against Option B (Sea-Picro), Option A wins on distribution durability — Adafruit + DigiKey + Mouser vs. mech-keeb hobby vendors. Net cost difference is ~$1.50 per board at qty 1; immaterial against the launch-slate BOM. Sea-Picro is preserved as the named fallback because the technical equivalence is exact and the only failure mode is supply.
Against Option C (Pro Micro), Option A wins on flash headroom (orders of magnitude), USB-C, SWD debugging, and Adafruit ecosystem alignment. The cost is +$5–6 per board over a Pro Micro clone; immaterial against the cost of a mid-bring-up part swap.
Against Option D (nice!nano), Option A wins on cost (~$8.95 vs ~$25), absence of unused wireless stack, and active power profile under wired USB. The cost is zero — nice!nano offers nothing the KN-86 keyboard subsystem needs.
Against Option E (bare SoC), Option A wins on time-to-prototype. The cost is ~$8 per board for the breakout module; trivially recovered by skipping a custom regulator/USB-C design cycle.
The cost of Option A — the chosen path:
- One PCB schematic update in Flux (controller footprint stays the same; net names update from D-style to GP-style). In progress.
- Pin assignment update propagates to the keyboard electrical spec and the QMK keymap. Already drafted in
keyboard-electrical-spec.txt. - BOM line item updates from “Pro Micro ATmega32U4 ($15)” to “Adafruit KB2040 ($8.95)”. Cost is lower than the previous default.
Consequences
Section titled “Consequences”Positive
Section titled “Positive”- Eliminates the flash-budget anxiety from ADR-0023. v0.1 firmware uses <1% of available flash. v2 cart-FFI surface lands trivially.
- USB-C internal cabling. Aligns with the rest of the Adafruit ecosystem in the BOM. Single internal cable type to the hub IC.
- SWD debugging exposed. Bring-up gets real on-chip debugging, not
printfover USB. - Cost decrease. KB2040 ($8.95) is cheaper than the prior-default Pro Micro clone ($15). Saves ~$6/board.
- Supersedes nothing in ADR-0018. ADR-0018 explicitly opened the controller family; this ADR exercises that openness. The architectural surface (USB HID over evdev to the Pi) is unchanged.
Negative / Accepted costs
Section titled “Negative / Accepted costs”- One in-flight Flux PCB design update. Controller footprint is already Pro Micro form factor; net names change from
D2/D3/...toGP2/GP3/.... ~30 min Flux task. - One in-flight keyboard-electrical-spec update. Pin assignment table needs to use the canonical pin map from §3 above. Update lands in this PR.
- Documentation propagation. ADR-0018 §2, CLAUDE.md Keys row, build-specification.md keyboard subsystem table, and the ADR README index all need light updates. Spec Hygiene Rule 3 sweep below.
Follow-on work this ADR creates
Section titled “Follow-on work this ADR creates”- F1 — Flux PCB schematic update. Replace Pro Micro footprint metadata with KB2040 (same physical footprint). Update net names per §3. Continue routing.
- F2 — keyboard-electrical-spec.txt finalization. Pin map already drafted; reconcile against this ADR §3 as canonical and remove the GP4/GP5 placeholder for I²C (now GP12/GP13).
- F3 — QMK keymap port. Existing 31-key keymap (ADR-0022) retargets from
mcu = atmega32u4tomcu = rp2040. Stock QMK; no custom HAL. - F4 — Bring-up validation. Hardware agent verifies KB2040 enumeration on the Pi via the internal USB hub, validates SWD pad accessibility for debugging, and confirms QMK build for the new MCU target.
Documentation Updates (REQUIRED — part of the decision, not aspirational)
Section titled “Documentation Updates (REQUIRED — part of the decision, not aspirational)”-
docs/adr/ADR-0024-keyboard-mcu-rp2040-selection.md— this ADR. -
docs/adr/README.md— append entry for ADR-0024. -
docs/adr/ADR-0018-custom-mechanical-keyboard-build.md— §2 Decision and §“Consequences”: add a footnote that the open controller family is narrowed to RP2040-class by ADR-0024, with the KB2040 named as the v0.1 part. Do not rewrite ADR-0018; ADR-0018’s family-open framing is correct as written, and ADR-0024 narrows it within that frame. -
CLAUDE.md— Canonical Hardware Specification “Keys” row: tighten the “QMK-compatible controller (Pro Micro, RP2040-class, or equivalent)” phrase to “QMK-compatible RP2040-class controller (Adafruit KB2040 v0.1; Sea-Picro fallback)” and cite this ADR alongside ADR-0018. -
docs/device/hardware/build-specification.md— Keyboard subsystem table and §“Why a USB HID keyboard over direct GPIO matrix”: tighten controller references from “Pro Micro family / RP2040-class” to “RP2040-class (KB2040)” with this ADR cited. -
docs/device/hardware/keyboard-electrical-spec.txt— already updated to KB2040 in v1.1; reconcile pin map against §3 above (GP12/GP13 for I²C, not GP4/GP5). - Project-wide grep sweep for stale “Pro Micro” / “ATmega32U4” claims as the v0.1 controller. Permitted to remain: historical references in ADR-0018’s options-considered section, this ADR’s options-considered section, and
docs/_archive/. Not permitted: any claim in CLAUDE.md, build-specification.md, or the Flux PCB design files that names ATmega32U4 as the v0.1 part.
A PR that lands this ADR without ticking the four open boxes (README.md index, ADR-0018 footnote, CLAUDE.md, build-specification.md) fails review.
Narrative (for the design history)
Section titled “Narrative (for the design history)”ADR-0018 deliberately left the controller open — “any QMK-compatible board” — because the hardware agent’s bring-up call would have better information than the spec author. That openness was correct at the time. ADR-0023 then closed the calculus from a different angle: the keyboard MCU now runs an LIS3DH sensor pipeline plus a vendor-defined HID report alongside the matrix scan. On ATmega32U4 the v1 numbers fit, but with no margin for the v2 cart-FFI surface that ADR-0023 §9 already commits to. Concurrent with that, the keyboard PCB entered Flux design — and the controller footprint window stayed open for exactly long enough to make the pin choice cheaply. KB2040 is RP2040 in the Pro Micro form factor: drop-in mechanical, native USB-C, mature stock QMK target, exposed SWD pads, ~256× the flash and ~100× the SRAM of the part it replaces, and at $8.95 it’s also $6 cheaper than the Pro Micro clone it supersedes. Sea-Picro is the named fallback because the SoC equivalence is exact and the only failure mode is Adafruit supply. Pro Micro ATmega32U4 is no longer in the running — not because it can’t run v1 (it can), but because the ADR-0023 trajectory and the v2 cart-FFI commitment make the headroom argument decisive. A future reader should care because this ADR sets the pattern for the rest of the device’s MCU-hosted peripherals: when the firmware budget tightens, swap up while the design window is open, not after fab.