feat: Raspberry Pi 4 (aarch64) target #110

Open
padreug wants to merge 12 commits from feat/rpi4-target into feat/rpi5-apex-hal
2 changed files with 60 additions and 3 deletions
Showing only changes of commit 64a19da924 - Show all commits

feat(machine): add rpi4/rpi5 device presets, wire 'apex' into the app types

The Pi 4 bring-up got as far as a rendering kiosk and then failed every
init cycle with

  [App] Initialization failed: TypeError: Cannot read properties of
        undefined (reading 'validator')

MACHINE_PRESETS had entries for sintra, tejo, douro, gaia and batm3 but
none for rpi4, so getDeviceConfig() dereferenced undefined. Pairing never
happened either: the throw lands before the signer runs, so a correctly
provisioned VITE_SPIRE_SEED sat in the process environment while
bunker_binding stayed at zero. rpi5 had the same hole and would have hit
it the moment anyone booted that target.

This is the third instance today of the same shape: the Pi targets reuse
the shared runtime, and the shared runtime carries per-model tables that
nobody added the Pi to. pcscd was the first (a busy-loop, no window), the
Electron cassette presets the second (silently seeded nothing).

Also widens the validator union from 'id003' | 'ebds' to include 'apex'
in both DeviceConfig and HalConfig. packages/hal has had
ValidatorType = 'id003' | 'ebds' | 'apex' since the Pyramid Apex driver
landed with the Pi 5 work, but these app-side unions were never widened,
so no machine could be configured to use the driver at all. The presets
below are the first thing that needed it, which is presumably why nobody
noticed.

The dispenser block in both presets is a placeholder, not a claim.
DispenserType has no 'none' variant and DeviceConfig requires the field,
so it points at a path that does not exist and carries no cassettes.
These boards are cash-in only until real hardware lands. A 'none'
dispenser variant would be the honest fix and is worth doing separately.

Validator device defaults to /dev/ttyValidator0, the FTDI udev symlink
raspberry-pi-4.nix creates. Swap to ttyValidator1 (CP210x) or
ttyValidator2 (CH340) to match the adapter fitted; `ls -l /dev/ttyValidator*`
after plugging it in says which appeared.
Padreug 2026-09-25 22:56:57 +02:00

View file

@ -15,7 +15,15 @@ import type { HalConfig, CassetteConfig } from '@/services/hal'
/**
* Supported machine models
*/
export type MachineModel = 'sintra' | 'tejo' | 'douro' | 'gaia' | 'batm3' | 'custom'
export type MachineModel =
| 'sintra'
| 'tejo'
| 'douro'
| 'gaia'
| 'batm3'
| 'rpi4'
| 'rpi5'
| 'custom'
/**
* Full device configuration
@ -28,7 +36,10 @@ export interface DeviceConfig {
/** Bill validator configuration */
validator: {
/** Validator protocol type */
type: 'id003' | 'ebds'
// 'apex' = Pyramid Apex RS-232. The driver landed with the Pi 5 work
// (packages/hal ValidatorType) but these app-side unions were never
// widened, so no machine could actually be configured to use it.
type: 'id003' | 'ebds' | 'apex'
/** Serial device path(s) */
device: string | string[]
}
@ -117,6 +128,52 @@ export const MACHINE_PRESETS: Record<MachineModel, Omit<DeviceConfig, 'fiatCode'
* - Dispenser: Fujitsu F56
* Note: Device paths may vary - verify on hardware
*/
/**
* Raspberry Pi 4 / CM4 reference build (aarch64).
* - Validator: Pyramid Apex 7600 over RS-232, via a USB-serial adapter
* - Dispenser: NONE WIRED YET — cash-in only
*
* The device path is the udev symlink raspberry-pi-4.nix creates for an
* FTDI bridge. Swap to ttyValidator1 (CP210x) or ttyValidator2 (CH340) to
* match the adapter actually fitted; `ls -l /dev/ttyValidator*` after
* plugging it in will say which one appeared.
*
* The dispenser block is a placeholder, not a claim. DispenserType has no
* 'none' variant and DeviceConfig requires the field, so it points at a
* path that does not exist and carries no cassettes. Cash-out is not
* available on this board until real hardware and a real path land here.
*/
rpi4: {
model: 'rpi4',
validator: {
type: 'apex',
device: '/dev/ttyValidator0',
},
dispenser: {
type: 'f56',
device: '/dev/ttyDispenser-not-fitted',
cassettes: [],
},
},
/**
* Raspberry Pi 5 reference build (aarch64). Same shape as rpi4 — same
* validator, same absent dispenser, same udev symlinks from
* raspberry-pi-5.nix.
*/
rpi5: {
model: 'rpi5',
validator: {
type: 'apex',
device: '/dev/ttyValidator0',
},
dispenser: {
type: 'f56',
device: '/dev/ttyDispenser-not-fitted',
cassettes: [],
},
},
gaia: {
model: 'gaia',
validator: {

View file

@ -23,7 +23,7 @@ export interface CassetteConfig {
export interface HalConfig {
validator: {
type: 'id003' | 'ebds'
type: 'id003' | 'ebds' | 'apex'
device: string | string[]
fiatCode: string
}