Skip to content

ADR-0018: Custom Mechanical Keyboard Build for 30-Key Input

The KN-86’s 30-key input has been planned as “bespoke matrix wired to a Pro Micro” since the Pi-Zero pivot. The Input System Architecture names a specific Arduino Pro Micro (ATmega32U4), a custom 8×4 key matrix hand-wired to its GPIO, and 1N4148 diodes for rollover isolation — construction details implied by the spec but never committed to a concrete fabrication plan.

Stepping back: this is a custom mechanical keyboard. The mechanical keyboard hobbyist ecosystem has already solved every problem in our spec as commodity parts and open-source firmware — PCB design, hot-swap switch sockets, QMK/Vial firmware, debounce, USB HID enumeration, layer support, macro recording, and keymap authoring. Rolling our own matrix PCB and bespoke controller firmware would reproduce work that is freely available.

The architectural surface the Pi Zero 2 W sees is unchanged by this decision (USB HID over Linux evdev, per the existing spec). What changes is the construction and sourcing model: we commit to the mech-keeb ecosystem as the pipeline, we open up the controller family (any QMK-compatible board), we open up the PCB path (custom-fab unified 30-key OR modified split layout), and we promote the internal USB hub IC from a v0.2 nice-to-have to a v0.1 requirement.

  • We’re moving from pure spec work into hardware bring-up. The Pi-Zero Build Spec §4 Stage 3 requires a concrete sourcing and fab plan for the keyboard assembly, and that plan was still “hand-wire a matrix to a Pro Micro” — insufficient for a case-mounted build.
  • The Amazon Shopping List already flags an internal USB hub IC (TUSB2036 or FE1.1s, ~$2) as a v0.2 line item because v0.1 needs two USB devices on the Pi’s single OTG port (keyboard HID + cartridge bridge CDC). Pulling that forward to v0.1 removes the dangling-dongle topology of the current BOM (powered 4-port Sabrent hub, external OTG adapter) and cleans up the internal cable routing.
  • ADR-0011’s early-boot key-scan gate depends on the keyboard being USB HID (not GPIO). The existing spec already names this dependency; this ADR formalizes the commitment rather than changing it.
  • The logical 30-key layout (14 function + 16 numpad, phone-layout, per CLAUDE.md Canonical Hardware Specification) is fixed. Any PCB choice must realize the exact 30-key physical arrangement under a single keyplate.
  • TERM key context-sensitivity lives in nOSh, not firmware — the keyboard must deliver the raw TERM scancode. Firmware-layer QMK features (tap-dance, layers, combos) are not used for TERM.
  • All keyboard hardware lives inside the Pelican 1170 case. No externally routed USB cables between the keyboard and the Pi.
  • Total switch count is 30 populated + 5 spares = 35 switches (CLAUDE.md).
  • The device is not a split ergo from the operator’s perspective — the keyplate is unified.

The 30-key input subsystem is built as a custom mechanical keyboard using the mechanical keyboard hobbyist ecosystem as the fabrication and sourcing pipeline, connected internally to the Pi Zero 2 W through a dedicated USB hub.

  1. Construction pattern: custom mech-keeb build. Hot-swap switch sockets (Mill-Max 0305 or Kailh-brand), Kailh Choc v1 switches, MBK-compatible keycaps, 1N4148 diodes for rollover isolation — all commodity parts from the mech-keeb community supply chain.
  2. Controller: QMK-compatible keyboard controller (family). Any controller in the QMK/Vial-supported family is acceptable: Pro Micro (ATmega32U4), RP2040-class boards (Sea-Picro, KB2040, Adafruit KB2040), nice!nano, or equivalent. Exact part chosen by the hardware agent during bring-up based on availability, USB-C preference, and program space. Firmware is stock QMK (or Vial built on QMK).
  3. PCB path: custom-fab unified 30-key board (preferred) OR modified split layout (fallback). The preferred path is a custom-fabricated unified 30-key PCB designed around the canonical key arrangement, with hot-swap footprints and the controller socketed or onboard. An acceptable fallback is a pair of split-layout PCBs (Corne, Ferris Sweep, or similar) electrically reconnected under a single keyplate with a single controller. Either path must deliver the same logical 30-key scancode set to the controller.
  4. USB HID interface to the Pi (unchanged). The keyboard enumerates over USB as a standard HID device; nOSh reads via Linux evdev (/dev/input/event*). No changes to the architectural surface Pi-side.
  5. Internal USB hub as v0.1 requirement. An internal USB hub IC (TUSB2036, FE1.1s, or equivalent) on the interior plate fans the Pi Zero 2 W’s OTG port out to the keyboard controller and the cartridge bridge (ADR-0011). This replaces the external Sabrent powered hub that was in the Amazon BOM. No externally routed USB cables.
  6. Keyboard firmware stays dumb. LAMBDA hold, QUOTE bookmark slots, SYS hold abort, TERM context dispatch, hold detection, and macro replay remain nOSh-owned. The keyboard firmware delivers raw key-down / key-up events for the 30-key map. QMK layer / tap-dance / combo features are not used in v0.1; if a future revision wants them, a new ADR authorizes the move.
  7. No bespoke matrix firmware. We do not write custom ATmega32U4 / RP2040 firmware for matrix scanning. QMK handles matrix scanning, debounce, and HID enumeration. Our firmware contribution is the 30-key keymap file only.

Option A: Hand-wired matrix on a Pro Micro (prior-spec default) (REJECTED)

Section titled “Option A: Hand-wired matrix on a Pro Micro (prior-spec default) (REJECTED)”

Hand-wire a 30-key Choc v1 matrix directly to a Pro Micro’s GPIO pins, flash stock QMK, route USB to the Pi. This is what the existing Input System Architecture and Build Spec imply.

Rejected because: hand-wiring 30 switches with diodes is error-prone and produces a fragile assembly unsuited to a case-mounted production device. Without a PCB there is no hot-swap path — switch selection is fixed at solder time. Any layout change requires rewiring. The Pro Micro specifically names an aging MCU and closes off better controllers. The existing spec described this as a pragmatic starting point, not an endpoint.

Option B: Custom-fab unified 30-key PCB with QMK-compatible controller (ACCEPTED — preferred)

Section titled “Option B: Custom-fab unified 30-key PCB with QMK-compatible controller (ACCEPTED — preferred)”

Design a 30-key PCB around the canonical phone-layout arrangement, populate with hot-swap sockets and 1N4148 SMD diodes, socket or onboard a QMK-compatible controller, fab at JLCPCB / OshPark / PCBWay. Flash stock QMK with the 30-key keymap.

Chosen because: matches the device’s cohesive identity (single plate, single controller, clean internal routing), preserves hot-swap for switch experimentation through bring-up and after, reuses the mech-keeb firmware stack unchanged, supports keymap revision via firmware flash only. Fabrication cost is $30–$60 for 5 boards at JLCPCB; lead time ~2 weeks including design.

Option C: Modified split PCB (Corne, Ferris Sweep) reconnected internally (ACCEPTED — fallback)

Section titled “Option C: Modified split PCB (Corne, Ferris Sweep) reconnected internally (ACCEPTED — fallback)”

Use a well-supported split-ergo PCB (e.g., two Corne halves at 42 keys each, two Ferris Sweep halves at 34 keys each) and wire the halves to a single controller inside the case, under a single unified keyplate. Populate only the keys we use; leave unused footprints empty under the plate.

Chosen as fallback because: the split-ergo ecosystem has production-proven PCBs with hot-swap footprints already on them, giving a zero-fab path. Acceptable when custom fab lead times constrain a build cycle. Aesthetically compromised (two PCBs under one plate, possible unused key positions) but functionally identical to Option B. Selected if Option B’s fab cycle is unacceptable or during a first-iteration prototype pass.

Option D: Direct GPIO matrix to the Pi Zero 2 W’s header (REJECTED)

Section titled “Option D: Direct GPIO matrix to the Pi Zero 2 W’s header (REJECTED)”

Wire the 30-key matrix directly to the Pi Zero 2 W’s GPIO header, scan from userspace.

Rejected in the existing Build Spec (§2 “Why Pro Micro keyboard over direct GPIO matrix”) for three reasons that remain valid: GPIO pin starvation against I2S and future peripherals, userspace debounce/scan code we’d have to write and maintain, and Linux scheduling latency coupled to the scan loop. No change from the prior rejection; this ADR generalizes the prior spec’s “Pro Micro” to “QMK-compatible controller” but preserves the rejection of direct-GPIO.

Option E: No internal hub, keep the external Sabrent powered hub (REJECTED)

Section titled “Option E: No internal hub, keep the external Sabrent powered hub (REJECTED)”

Keep the current Amazon BOM (line 7a: Sabrent 4-port USB 2.0 powered hub, $13) as a visible external module attached to the Pi’s OTG port.

Rejected because: a dangling $13 powered hub with its own barrel jack is incompatible with the case-mounted build. The v0.2 plan already specifies a ~$2 internal hub IC — there is no cost-or-complexity reason to defer it. Pulling it forward to v0.1 is strictly better than shipping the first bench rig with a loose Sabrent hub inside (or outside) the Pelican.


Against Option A (hand-wired + Pro Micro), Option B (custom-fab unified) wins on:

  • Manufacturability. A fabbed PCB is repeatable across units; a hand-wired matrix is not.
  • Hot-swap. B supports switch changes without soldering; A does not.
  • Controller flexibility. B lets us pick modern RP2040-class parts with USB-C native; A names a specific aged MCU.
  • Keymap iteration. B updates via firmware flash; A requires physical rework on layout changes.

B’s cost: ~2 weeks of PCB design + one fab cycle ($30–$60) before input bring-up. Mitigated by C as the fallback when time pressure rules B out.

Against Option C (split-reconnected), Option B wins on aesthetic cohesion and internal cable cleanliness. C wins on time-to-first-build (a split PCB can be ordered today; B takes a design cycle). C is the right fallback but not the preferred endpoint — a shipped device should have Option B’s unified PCB, not two split halves visibly bridged.

Against Option D (direct GPIO), every point from the existing Build Spec stands. This ADR does not revisit that rejection.

Against Option E (external powered hub), the $11 delta between Sabrent ($13) and internal hub IC (~$2) plus the case-integration win makes the choice trivial. The only reason E was on the BOM is that v0.1 preceded the construction model we’re now committing to.


  • Eliminates bespoke electronics work. No custom firmware, no custom matrix scan code, no hand-wired assembly — all commodity.
  • Hot-swap enables switch experimentation. Kailh Choc White, Jade, Brown, silent, or any MBK-compatible variant is drop-in swappable during bring-up and beyond.
  • Keymap iteration is firmware-only. If the 30-key layout changes (TERM placement, ergonomics tuning), we re-flash QMK — no rewiring.
  • Internal USB hub solves two problems at once. Keyboard + cartridge bridge (ADR-0011) coexist cleanly on the Pi OTG port with no external hub dongle and no external USB cables.
  • Controller choice stays open. Hardware agent picks the specific QMK-compatible part during bring-up; Pro Micro clones remain acceptable, modern RP2040-class boards become acceptable.
  • Lower BOM. Replaces the $13 Sabrent hub with a ~$2 hub IC. Hand-wiring hookup wire (BOM line 12, $12) becomes optional — the PCB routes the matrix instead.
  • Adds a PCB-design task to hardware scope. Unified custom-fab means one design cycle before input bring-up. Lead time and ~$30–$60 fab cost.
  • Promotes the USB hub IC to v0.1. Previously targeted for v0.2. Requires one additional part on the interior plate.
  • Dual-PCB aesthetic if Option C is chosen. Split halves under a unified keyplate are invisible to the operator but cheap-looking if the case is opened.
  • Commits to MBK / Choc v1 ecosystem at the PCB footprint level. Already true from the existing spec, but now hardwired into the fab.
  • KBD-01 (Hardware): Design custom 30-key PCB (Option B). KiCad schematic + layout, hot-swap footprints (Mill-Max 0305 or Kailh), 1N4148 SMD diodes, QMK-compatible controller socket or onboard controller, USB-C connector (if controller is RP2040-class) or micro-USB (if Pro Micro). Deliverable: fab-ready Gerbers + BOM + assembly drawing.
  • KBD-02 (Hardware): Evaluate Corne and Ferris Sweep as Option C fallback PCBs. Confirm whether either’s matrix can be re-keymapped to cover the 30-key arrangement cleanly. Deliverable: short evaluation memo with a recommendation.
  • KBD-03 (Hardware): Select and spec the internal USB hub IC (TUSB2036, FE1.1s, or equivalent) + its interior-plate integration. Update the Pi-Zero Build Spec §2 topology diagram and the Sourcing Guide BOM. Deliverable: part selection, schematic fragment, and BOM delta.
  • KBD-04 (Platform Engineering): Author the QMK keymap file for the 30-key layout per docs/ui-design/KN-86-Input-System-Architecture.md §3D numpad table. Deliverable: firmware/kn86-keyboard/keymap.c (approximate path) producing the scancodes nOSh expects.
  • KBD-05 (Platform Engineering): Update ADR-0011’s early-boot key-scan wording to generalize “Pro Micro” to “USB HID keyboard.” Included in this ADR’s documentation updates.

Documentation Updates (REQUIRED — part of the decision, not aspirational)

Section titled “Documentation Updates (REQUIRED — part of the decision, not aspirational)”
  • docs/architecture/adr/README.md — add ADR-0018 to the index with status Accepted.
  • CLAUDE.md — Canonical Hardware Specification “Keys” row: append the USB HID / QMK-compatible controller / internal USB hub clarification and the ADR-0018 citation. Platform Engineering agent scope: remove “GPIO keyboard matrix” from the device tree overlays list (pre-existing inaccuracy; keyboard is and has always been USB HID in the Pi Zero era).
  • docs/ui-design/KN-86-Input-System-Architecture.md — §2 Hardware: replace “Arduino Pro Micro” / “Pro Micro” with “QMK-compatible keyboard controller” throughout. Collapse the duplicated “Scanning (Device)” / “Scanning (Prototype)” subsections into one that describes the custom mech-keeb build. Add an “Option B vs. Option C PCB path” subsection referencing this ADR. §4A event flow diagram: relabel “Pro Micro Matrix” to “Keyboard Controller.”
  • docs/hardware/KN-86-Pi-Zero-Build-Specification.md — §2 Hardware Topology diagram: change “Pro Micro (ATmega32U4)” to “Kbd Controller (QMK)” and add the internal USB hub block. §2 Subsystem roles table: update the “Input” row. §2 “Why Pro Micro keyboard over direct GPIO matrix”: rewrite as “Why a USB HID keyboard over direct GPIO matrix” with the argument unchanged and the name generalized. §4 Stage 3 Input: update controller references and the assembly steps.
  • docs/hardware/KN-86-Pi-Zero-Sourcing-Guide.md — §1 overview, §2 BOM line 5, §3 Supplier notes, and §6 References: generalize the Pro Micro references to “QMK-compatible keyboard controller.” Add a BOM line for the internal USB hub IC. Flag that the Pro Micro is still an acceptable specific choice within the family.
  • KN-86-Amazon-Shopping-List.md — line 5 Pro Micro entry: generalize heading and note the RP2040-class alternatives. USB hub note: rewrite to reflect that the hub is now v0.1 internal hub IC, and the external Sabrent is only a bench-bring-up stand-in.
  • docs/architecture/KN-86-Prototype-Architecture.md — “Keyboard Architecture” section and the hardware-overview table: generalize Pro Micro references; note the custom-fab PCB or split-reconnected path.
  • docs/architecture/adr/ADR-0011-pi-zero-firmware-update-system.md — “Early-boot key-scan timing” wording: generalize “Pro Micro keyboard” to “USB HID keyboard.” The boot-path logic is unchanged.

A PR that lands this ADR without ticking these boxes fails review per CLAUDE.md Spec Hygiene Rule 3.


We’d been treating the keyboard as an embedded problem — a matrix to scan, a controller to program, a cable to route. Stepping back, the KN-86’s 30-key input is a custom mechanical keyboard, full stop. The mech-keeb hobbyist community has already solved PCB design, hot-swap switch mounting, firmware (QMK / Vial), debounce, USB HID enumeration, and keymap authoring as commodity parts with open-source tooling. Our job is to pick the physical layout, adopt the ecosystem, fab a unified 30-key board (or reuse a split one under the keyplate), and route the USB internally to the Pi. This ADR draws that line. From here on, the keyboard is any custom mech keeb: a fabbed PCB, hot-swap sockets, a QMK-compatible controller, flashed with our 30-key keymap, connected to the Pi through an internal USB hub. No bespoke firmware, no hand-wiring, no custom protocols. The architectural surface the Pi sees is unchanged from what the previous spec described — USB HID over evdev — we’ve just committed to the construction model that the community built around that surface.