Canonical Hardware Specification — SINGLE SOURCE OF TRUTH
CRITICAL: Never restate these values in new documents. Reference this document. Never invent alternative specs. If a document contradicts these values, the document is wrong — fix it, don’t fork.
| Property | Value | Notes |
|---|---|---|
| Display (primary) | Elecrow 7” IPS, 1024×600 pixels, RGB565 framebuffer | Single primary display target. The 1024×600 panel is the canonical surface; cell density is per-region via integer-scaled 8×8 KN-86 Code Page glyphs, not a fixed text grid. See ADR-0036 (native framebuffer renderer, 2026-06-13). |
| Display (auxiliary) | 3.12” OLED, SSD1322, 256×64 yellow | CIPHER-LINE — secondary strip mounted above the keyboard. Mono yellow pixel element (close to amber #E6A020). 4-row layout at Press Start 2P 8×8 native (~32 chars/row): Row 1 = status strip (battery / timer / mode / TERM hint), Rows 2–3 = CIPHER scrollback (current fragment + previous echo), Row 4 = contextual (seed capture / gameplay timer / mission meta). 4-wire SPI on SPI0. ~10–30 mA typical (sparse text), ~40 mA peak. CIPHER is OLED-exclusive — no CIPHER glyphs on the main 128×75 grid. See ADR-0015. |
| Text grid (primary) | Parameterized per-region by integer scale | 128×75 = the 1× cell ceiling (1024/8 × 600/8); 80×25 = the 12×24 view (8×8 glyph at 1× horizontal / 2× vertical, centered in a 12×24 cell); both are equal-citizen views of the same 1024×600 surface. Per-element scale, not global — body text at 1× and headlines at 4×–5× compose on one screen. Row 0 = firmware status bar (top). Rows 1–73 = cartridge content. Row 74 = firmware action bar (bottom) — at 1× density. See ADR-0036 (native framebuffer renderer, 2026-06-13, supersedes ADR-0027’s display layer). |
| Usable content area | 128 columns × 73 rows (Rows 1–73) | Cartridges NEVER draw on Row 0 or Row 74. Runtime exposes these as cell-API getters: (cell-cols) → 128, (cell-rows-usable) → 73 (ADR-0027). |
| Font cell | 8×8 pixel bitmap, integer-scaled (1×, 2×, 3×, …) | Press Start 2P + CP437 + KN-86 custom glyphs at native 8×8, rendered by the KN-86 native framebuffer renderer at any integer scale: scale-1 body text (8×8 px), scale-2 chrome + subtitle (16×16), scale-4 timer/number (32×32), scale-5 headline (40×40). Per-element, freely composed. Maximum 1× density = 128×75 cells (1024/8 × 600/8). No letterbox, no fractional scaling, no Linux console PSF dependency. The retired model (8×8 source scaled into a 12×24 physical cell on a 960×600 logical framebuffer composited with a 32 px letterbox, plus the --font-12x24 / KN86_FONT_12X24 opt-in) is superseded by ADR-0014 → ADR-0027 → ADR-0036, which is the authoritative decision. |
| Font | Press Start 2P + CP437 box drawing, 8×8 bitmap | Layer 1: 256-glyph KN-86 Code Page. Layer 2 (planned): ~2,000-glyph Unicode subset. See kn-86/docs/software/api-reference/grammars/character-set.md. |
| Color | AMBER #E6A020 on black #000000 (default); WHITE #F0F0F0 and GREEN #33F033 selectable | Monochrome single-foreground. Three operator-selectable phosphor schemes mapping to CRT phosphor history: green=P1, amber=P3-ish, white=P4. AMBER is the canonical default (reverted to amber on operator review 2026-06-17, walking back the AMBER #E6A020 trial that ADR-0036 had selected on-glass 2026-06-13; amber is the established KEC house phosphor). WHITE and GREEN are starting values pending further on-glass tuning. Persisted per-operator in nosh-config.toml [aesthetic].mode ("amber" / "white" / "green"); cycled on the SYS-tab picker. See ADR-0036 (color decision, superseded by the 2026-06-17 amber reversal) + ADR-0034 (aesthetic-mode mechanism, amended). |
| Display modes | TEXT (1×: 128×75 cells max), HALF-BLOCK (128×150 pseudo-pixels), mixed-scale composition | TEXT mode draws the 8×8 Code Page at any integer scale; 128×75 is the 1× ceiling, not the only grid. Body text + headlines at different scales compose on one screen (the on-glass mixed-size pattern). HALF-BLOCK uses the U+2580 / U+2584 half-block glyphs for 2 vertical sub-pixels per cell (128 × 75 × 2 = 128×150); cart-usable 128×146 (2 sub-rows × 73 usable rows). Both modes are thin layers over the native renderer’s RGB565 framebuffer surface (ADR-0036). The retired BITMAP (960×600) pixel canvas and SPLIT mode remain superseded (ADR-0014 → ADR-0027 → ADR-0036). |
| Processor | Raspberry Pi Zero 2 W | Single primary processor. |
| Coprocessor | Raspberry Pi Pico 2 (RP2350) | Realtime I/O coprocessor mounted on the internal sub-board. Owns YM2149 PSG synthesis with I2S out to the WM8960 audio codec (I²C-configured) and the SSD1322 CIPHER-LINE OLED driver. UART command link @ 1 Mbps to Pi (UART0, GPIO14/15). ~25–55 mA active typical; <5 mA dormant. See ADR-0017 and kn-86/docs/software/api-reference/grammars/coprocessor-protocol.md. The DMG cartridge bus responsibility originally assigned to the Pico 2 in ADR-0017 was removed by ADR-0019 (cartridge interface moves to a USB-mass-storage SD card via the internal hub bridge IC; the Pico 2 no longer touches the cartridge bus). |
| Keys | 34 physical keys on a split layout (Ferris Sweep, 3×5 main + 2 thumb per side) per ADR-0031 | TERM is context-sensitive — default terminal function, CIPHER-LINE seed capture when seed display is active, additional contextual bindings per surface (see multi-res design plan; ADR-0015; ADR-0016 for nEmacs/REPL context-polymorphic dispatch and Nokia multi-tap). TERM lives on the right outer thumb (RT1) per ADR-0031 §3.1. The LEFT half carries the 14 Lisp primitives function block per ADR-0022 §1 legends (NIL / INFO / LAMBDA-printed-FN / CONS / LINK on top; APPLY / QUOTE / BACK / CAR / CDR on home; SYS / EQ / ATOM on bottom plus 2 spares; EVAL on inner-thumb LT0; LAYER on outer-thumb LT1, renamed from LSHIFT by ADR-0062 §2 because MO(L1) selects a layer and never shifted case). The function-block legends are confirmed KEC-accurate by ADR-0044 §5 — FN is the literal Fe lambda keyword — with one authoring-binding clarification: the EQ keycap art is unchanged, but its authoring-insert binds to KEC’s identity word is (KEC has no eq; numeric equality is the now-printable = / == / /=). The RIGHT half carries the digit 3×3 (1-2-3 top, 4-5-6 home, 7-8-9 bottom on inner three columns) preserving Nokia digit-to-letter binding from ADR-0016 §6 (2=ABC … 9=WXYZ), plus , . / ; - punctuation primaries and ENT on the outer two columns; 0 on inner-thumb RT0; TERM on outer-thumb RT1. SHIFT layer (LSHIFT held, QMK MO(L1)) delivers the secondary manifest ` ? @ " ) < = > : ( * ' ! + \ — KEC Lisp requires 21 punctuation characters (ADR-0044 as amended 2026-07-10, aligned to kec-lisp docs/language.md; the original 19-count missed ! and %), and 20 of them (these 15 SHIFT secondaries + the BASE primaries , / . ; -) are printed and reachable in ≤1 hold, with the 4-5-6 home-row triplet shift-secondaries reading < = > and ! (the bang-mutation suffix — vector-set!, hash-set!, set-seed!, …) on the formerly spare 9-key shift per the amendment. This reorganizes ADR-0031 §3.1’s pre-KEC secondaries — dropping the KEC-irrelevant $ ^ & # (and %, deliberately, to L2), promoting < > = ? ', repositioning ! to the 9-key shift — per ADR-0044 §2 + Amendment Log. The rare ASCII long tail (# & $ % ^ ~ ` |
| Audio | YM2149 PSG (3 tone + noise + envelope, 14 registers) | Software emulation. 44.1 kHz PCM, stereo-capable — mono sum to the built-in speaker, stereo to the headphone jack. See ADR-0054. |
| Accelerometer | STMicroelectronics LIS3DH (3-axis MEMS, ±4g range, 25 Hz ODR) | Hosted on the master (LEFT) half’s keyboard MCU (the RP2040-class KB2040 — one of two on the Sweep per ADR-0031 §5) on I²C1 (KB2040 silkscreen D6/D7, RP2040 GP6/GP7) per ADR-0032 §3 — originally I²C0 (D4/D5) per ADR-0024 §3 + ADR-0031 §3 but moved to resolve trackpoint pin conflict. INT1 line released — v1 polling-only at 25 Hz ODR. Motion events are extracted on-MCU (peak-magnitude threshold + 4-bucket classifier) and reported to the Pi over a vendor-defined HID report on the existing keyboard interface — no second USB device, no separate evdev node. Drives the firmware-internal nOSh ambient CRT-glitch system (Off / Low / Medium / High setting, persisted at /home/shared/nosh-config.toml). v1 is firmware-only; cart-FFI exposure is deferred. Active draw ~0.1 mA; negligible against the power envelope (see Battery row / ADR-0038). Axis convention: +X = operator-right, +Y = up (away from keyplate), +Z = out of the screen toward the operator. See ADR-0023. |
| Real-time clock (RTC) | DS3231-class I²C RTC + backup coin cell (e.g. CR2032) | Battery-backed real-time clock on the Pi’s I²C bus, added because the deck is offline-first (no regular NTP) and the ADR-0045 background-system power-off catch-up needs true elapsed-time across power cycles — the Pi Zero 2 W has no built-in RTC and CLOCK_MONOTONIC resets every boot. Kernel restores the system clock at boot via dtoverlay=i2c-rtc,ds3231 + hwclock; no RTC driver in libnosh (the runtime just reads the RTC-synced system clock). Supersedes the prior no-RTC fake-hwclock-only assumption (fake-hwclock demoted to a dead-battery fallback). Exact part / I²C address confirmed at bring-up against existing I²C peripherals (LIS3DH, power INA219). See ADR-0045. |
| Audio codec/amp | WM8960 stereo codec (I²C-configured) | Stereo DAC + ~1 W class-D speaker amp + stereo headphone amp on one chip. Fed I2S by the Pico 2; needs I²C register setup (master/address finalized at bring-up). Replaces the MAX98357A mono I2S DAC/amp — chosen to drive both the mono speaker and the stereo headphone jack from one part (accepted trade: ~1 W speaker drive vs the MAX98357A’s ~3.2 W). See ADR-0054. |
| Speaker | Gikfun 2″ (≈50 mm) full-range, 4 Ω 5 W, single (mono) | One driver, mono built-in; driven well under its 5 W rating by the WM8960 speaker amp. Replaces the 28 mm 8 Ω 2 W driver. Stereo lives on the headphone jack, not the built-in speaker. See ADR-0054. |
| Headphone jack | 3.5 mm stereo TRS, switched | Off the WM8960 headphone-amp output; stereo. Speaker auto-cuts on plug insert (switched jack / codec jack-detect — finalized at bring-up). Now canonical per ADR-0054 (2026-07-02), resolving the prior bench-only / non-canonical status noted in kn-86/docs/device/hardware/audio.md. |
| Battery | Waveshare UPS Module 3S (5 V/5 A) + 3× 18650 Li-ion (series, 9.0–12.6 V pack) | Integrated power subsystem — battery management + 5 V/5 A boost + simultaneous charge/output + onboard fuel gauge on one board. Supersedes the TP4056 + PowerBoost 1000C + bare 3000 mAh LiPo + separate display supply (ADR-0038, 2026-06-13). The 5 V/5 A rail powers the entire device including the 7” primary display (the display is no longer on a separate v0.1 USB-C supply). Charging: 12.6 V 2 A via DC5521 barrel jack (not USB-C). Battery readout: onboard INA219 over I²C (voltage/current/power) → nOSh Row-0 battery glyph — replaces the resistor-divider + MCP3008 ADC plan. Protection: overcharge / over-discharge / overcurrent / short / reverse (S-8254AA + HY2213 balance) — reverse-polarity-safe 18650 holders. Runtime (est.): ~4–5 h with the display active, ~10–12 h electronics-only; validated at bring-up. Cells (3× genuine 18650, matched) are not included. See ADR-0038. |
| Case | Pelican 1170 Protector Case (black) | Off-the-shelf polypropylene hardcase with ABS latches and integrated hinge. Exterior 296 × 212 × 96 mm (11.64 × 8.34 × 3.78 in); interior 268 × 153 × 80 mm (10.54 × 6.04 × 3.16 in); 0.95 kg with foam; watertight seal; −40 °F to 190 °F operating range. Custom 3D-printed inset panels (PLA or PETG — material TBD during bring-up) carry the primary display bezel, the CIPHER-LINE OLED bezel (mounted above the key plate), the key plate, the cartridge-slot retainer, and port cutouts; Pelican foam is cut to seat internal components. The Pelican shell itself is not machined or modified. |
| Cartridge interface | Full-size SD card via USB mass storage | SD socket (push-push, surface-mount, with card-detect) wired through a USB-to-SD card reader bridge IC on the internal USB hub from ADR-0018. The Pi mounts the cart as a standard block device. See ADR-0019. |
| Cartridge shell | ~58 × 65 × 8 mm two-piece clamshell sled carrying a standard full-size SD card | Operator-facing artifact (ritual, collectibility, label surface, tactile mass); the SD card is the underlying storage. Reloadable — the user can open the shell to replace or re-flash the SD card. See ADR-0019. |
| Per-cartridge save | File on SD card filesystem | Save state lives as a file (e.g., /save/<cart_id>.sav) on the cartridge’s own SD card. Universal Deck State retains cross-cartridge fields (handle, credits, reputation) on the device’s microSD per ADR-0011. Closes ADR-0006 open question #2. See ADR-0019. |
| Hardware ship target | Q4 2027 |
Spec Hygiene Rules
Section titled “Spec Hygiene Rules”- Never duplicate specs. When writing or editing any doc, reference this document (
docs/device/hardware/canonical-spec.md) instead of restating values. - Never create new resolution profiles, grid sizes, or font dimensions without Josh’s explicit approval and an update to this table.
- When an ADR changes a spec value, update THIS table and do a project-wide grep to fix all stale references in the same PR. The ADR’s “Documentation Updates” section is a hard requirement, not aspirational.
- The emulator grid is runtime-queried, not a fixed
types.hconstant (ADR-0027). The termbox2 cell layer exposes the grid via cell-API getters —(cell-cols)= 128,(cell-rows-usable)= 73 — and the legacyKN86_TEXT_COLS/KN86_TEXT_ROWS/KN86_CELL_*/KN86_FRAMEBUFFER_*constants are removed when the nOSh re-flow lands. Until that re-flow lands (tracked in the nOSh re-flow task),types.hstill carries the legacy 80×25 constants as a known, tracked deviation — see thetypes.hheader comment. Do not treat the legacy constants as canonical. - Row layout is non-negotiable: Row 0 = status bar (firmware), Row 74 = action bar (firmware), Rows 1–73 = content (cartridge). Every gameplay spec, screen design, and UI wireframe must use this layout.
- CIPHER is OLED-exclusive, with one sanctioned exception. CIPHER voice renders on the CIPHER-LINE auxiliary display. The main 128×75 grid carries cartridge content + firmware rows 0/74 only — no CIPHER glyphs on the primary display, except the Null cartridge, which is granted a main-grid CIPHER-escape capability as a designed gameplay mechanic. No other cartridge may render CIPHER on the main grid. See ADR-0015.