feat: Raspberry Pi 5 (aarch64) target + Pyramid Apex RS-232 bill validator #87
3 changed files with 217 additions and 2 deletions
feat(deploy): add aarch64 Raspberry Pi 5 target (sd-image)
Proper aarch64 NixOS target for the DIY Pi 5 build, wired additively so the x86 fleet path is untouched (both the new Pi sd-image and the existing sintra-installed still evaluate cleanly): - nixos-hardware input (raspberry-pi-5 module) for Pi kernel/firmware/GPU. - aarch64 pkgs + pkgs-unstable + mkAtmApp instances (parallel to x86). - mkPiConfig: aarch64 nixosSystem reusing the shared configuration.nix + bitspire-atm service, replicating the installed-config runtime (bitspire service, first-boot env seed, electron service override, swap). Drops the x86 fleet machinery for a first bring-up: no determinate/autoUpgrade (not yet fleet-managed) and no atm-tui (needs an aarch64 package). - raspberry-pi-5.nix hardware module: extlinux boot, vc4/v3d KMS for X, primary UART free for a GPIO-wired validator, no-suspend, and stable /dev/ttyValidator* udev symlinks for USB-serial validator adapters (Apex 7600 RS-232 via adapter, NV10 USB+). - nixosConfigurations.rpi5-installed + packages.aarch64-linux.sd-image-rpi5. BUILD NOTE: the app closure (aarch64 electron/native addons) needs an aarch64 builder — a native Pi/arm box or `boot.binfmt` qemu emulation on an x86 host. Config evaluates on x86; it just can't build there. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018ivBosaWmv8vwFE7ejrdHW
commit
a77bae50ee
63
deploy/nixos/hardware/raspberry-pi-5.nix
Normal file
63
deploy/nixos/hardware/raspberry-pi-5.nix
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# Raspberry Pi 5 hardware module (aarch64).
|
||||
#
|
||||
# The bitSpire equivalent of upboard.nix, but for a Pi 5 instead of the x86
|
||||
# UP Board. Kernel, firmware, GPU and bootloader come from the nixos-hardware
|
||||
# `raspberry-pi-5` module (added alongside this one in flake.nix); here we set
|
||||
# only the bitSpire-specific hardware glue: serial for the bill validators,
|
||||
# the kiosk display driver, and no-suspend.
|
||||
#
|
||||
# Wiring the parts (see docs) to a Pi 5:
|
||||
# - Bill validators (Apex 7600 RS-232, NV10 USB+): easiest via one USB-serial
|
||||
# adapter each → stable /dev/ttyValidator* symlinks below. A GPIO-UART wire
|
||||
# is also supported (primary UART enabled, console kept off it).
|
||||
# - QR scanner: USB HID, no config.
|
||||
# - 7" touchscreen: DSI or HDMI; the vc4/v3d KMS driver (from nixos-hardware)
|
||||
# backs X.
|
||||
# - Boot/root: USB-SATA SSD or the SD card.
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
# aarch64 target. (The flake instantiates this config with aarch64 pkgs; this
|
||||
# line documents/asserts it.)
|
||||
nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";
|
||||
|
||||
# Bootloader: the aarch64 sd-image uses the extlinux-compatible generator;
|
||||
# nixos-hardware's rpi5 module wires the firmware/u-boot. No systemd-boot
|
||||
# (that's x86/UEFI, as on the UP Board).
|
||||
boot.loader.grub.enable = lib.mkDefault false;
|
||||
boot.loader.generic-extlinux-compatible.enable = lib.mkDefault true;
|
||||
|
||||
# Primary UART (GPIO 14/15) available for a GPIO-wired validator. Keep the
|
||||
# serial console OFF it so the validator owns the line — mirrors upboard.nix
|
||||
# keeping ttyS4 free for the dispenser. USB-serial adapters are unaffected.
|
||||
boot.kernelParams = lib.mkDefault [ "console=tty0" ];
|
||||
|
||||
# X uses the Pi GPU's kernel modesetting driver (vc4/v3d KMS from
|
||||
# nixos-hardware). Electron renders through it as on the UP Board.
|
||||
services.xserver.videoDrivers = lib.mkDefault [ "modesetting" ];
|
||||
|
||||
hardware.enableRedistributableFirmware = true;
|
||||
|
||||
# Kiosk: never sleep.
|
||||
systemd.targets = {
|
||||
sleep.enable = false;
|
||||
suspend.enable = false;
|
||||
hibernate.enable = false;
|
||||
hybrid-sleep.enable = false;
|
||||
};
|
||||
|
||||
# Stable device symlinks for USB-serial bill-validator adapters, so the ATM
|
||||
# config can point at /dev/ttyValidator0 regardless of enumeration order.
|
||||
# Covers the common bridges (FTDI, Silicon Labs CP210x, WCH CH340). If two
|
||||
# adapters of the SAME chip are used, disambiguate by KERNELS/serial instead —
|
||||
# tune during bring-up. The NV10 USB+ presents its own USB CDC serial; add its
|
||||
# idVendor/idProduct here once known.
|
||||
services.udev.extraRules = lib.mkAfter ''
|
||||
# FTDI (e.g. FT232R) → ttyValidator0
|
||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SYMLINK+="ttyValidator0"
|
||||
# Silicon Labs CP210x → ttyValidator1
|
||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", SYMLINK+="ttyValidator1"
|
||||
# WCH CH340 → ttyValidator2
|
||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", SYMLINK+="ttyValidator2"
|
||||
'';
|
||||
}
|
||||
34
flake.lock
generated
34
flake.lock
generated
|
|
@ -405,6 +405,24 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixos-hardware": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1786867632,
|
||||
"narHash": "sha256-ez+ubZlA1RtdjCB18a6zJ9M4u8qoPDy08EcnsW5M3Xw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixos-hardware",
|
||||
"rev": "ff17823245ab9ff7bcae6acf950bd89cba82c38c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixos-hardware",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1773222311,
|
||||
|
|
@ -482,6 +500,19 @@
|
|||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1767892417,
|
||||
"narHash": "sha256-8bW3q88CEg2u4hSP66Vf4lpbLonHz7hqDNBMcCY7E9U=",
|
||||
"rev": "3497aa5c9457a9d88d71fa93a4a8368816fbeeba",
|
||||
"type": "tarball",
|
||||
"url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre924538.3497aa5c9457/nixexprs.tar.xz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"
|
||||
}
|
||||
},
|
||||
"nixpkgs_4": {
|
||||
"locked": {
|
||||
"lastModified": 1779467186,
|
||||
"narHash": "sha256-nOesoDCiXcUftqbRBMz9tt4blI5PvljMWbm3kuCA+0s=",
|
||||
|
|
@ -503,7 +534,8 @@
|
|||
"determinate": "determinate",
|
||||
"devenv": "devenv",
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs_3",
|
||||
"nixos-hardware": "nixos-hardware",
|
||||
"nixpkgs": "nixpkgs_4",
|
||||
"nixpkgs-unstable": "nixpkgs-unstable",
|
||||
"rust-overlay": "rust-overlay"
|
||||
}
|
||||
|
|
|
|||
122
flake.nix
122
flake.nix
|
|
@ -36,9 +36,12 @@
|
|||
url = "git+ssh://forgejo@git.atitlan.io/aiolabs/atm-tui.git";
|
||||
inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
};
|
||||
|
||||
# Raspberry Pi 5 (aarch64) hardware support for the DIY Pi build.
|
||||
nixos-hardware.url = "github:NixOS/nixos-hardware";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, nixpkgs-unstable, flake-utils, rust-overlay, devenv, determinate, atm-tui }:
|
||||
outputs = { self, nixpkgs, nixpkgs-unstable, flake-utils, rust-overlay, devenv, determinate, atm-tui, nixos-hardware }:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
|
||||
|
|
@ -59,6 +62,24 @@
|
|||
src = self;
|
||||
};
|
||||
|
||||
# aarch64 (Raspberry Pi 5) toolchain — a parallel set of pkgs + app
|
||||
# builder for the DIY Pi build. Kept fully separate from the x86 fleet
|
||||
# path so nothing above changes.
|
||||
pkgsAarch64 = import nixpkgs {
|
||||
system = "aarch64-linux";
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
pkgsUnstableAarch64 = import nixpkgs-unstable {
|
||||
system = "aarch64-linux";
|
||||
config.allowUnfree = true;
|
||||
overlays = [ (import rust-overlay) ];
|
||||
};
|
||||
mkAtmAppAarch64 = import ./nix/mkAtmApp.nix {
|
||||
pkgs = pkgsAarch64;
|
||||
pkgs-unstable = pkgsUnstableAarch64;
|
||||
src = self;
|
||||
};
|
||||
|
||||
# Fiat code per machine model
|
||||
fiatCodeForModel = {
|
||||
douro = "GTQ";
|
||||
|
|
@ -261,6 +282,92 @@
|
|||
})
|
||||
];
|
||||
};
|
||||
|
||||
# Raspberry Pi 5 (aarch64) installed config — the DIY Pi build. Mirrors
|
||||
# mkInstalledConfig's runtime (bitspire service, env activation, electron
|
||||
# service override, swap) on aarch64 + Pi hardware, but deliberately drops
|
||||
# the x86 fleet machinery for a first bring-up: no determinate/autoUpgrade
|
||||
# (not yet a managed fleet member) and no atm-tui (add once it publishes an
|
||||
# aarch64 package). Builds an SD image; needs an aarch64 builder (native
|
||||
# Pi / arm box / binfmt emulation) — the app closure won't build on x86.
|
||||
mkPiConfig = machineModel:
|
||||
let
|
||||
atm-app = mkAtmAppAarch64 {
|
||||
model = machineModel;
|
||||
fiatCode = fiatCodeForModel.${machineModel} or "USD";
|
||||
};
|
||||
fiatCode = fiatCodeForModel.${machineModel} or "USD";
|
||||
in
|
||||
nixpkgs.lib.nixosSystem {
|
||||
system = "aarch64-linux";
|
||||
specialArgs = {
|
||||
pkgs-unstable = pkgsUnstableAarch64;
|
||||
inherit atm-app;
|
||||
};
|
||||
modules = [
|
||||
nixos-hardware.nixosModules.raspberry-pi-5
|
||||
(nixpkgs + "/nixos/modules/installer/sd-card/sd-image-aarch64.nix")
|
||||
./deploy/nixos/configuration.nix
|
||||
./deploy/nixos/bitspire-atm.nix
|
||||
./deploy/nixos/hardware/raspberry-pi-5.nix
|
||||
({ config, lib, pkgs, pkgs-unstable, ... }: {
|
||||
services.bitspire = {
|
||||
enable = true;
|
||||
appDir = "${atm-app}";
|
||||
};
|
||||
|
||||
environment.systemPackages = [
|
||||
(pkgs.writeShellScriptBin "fund-atm" ''
|
||||
exec ${pkgs-unstable.nodejs}/bin/node ${atm-app}/dist-electron/fund-atm.bundle.cjs "$@"
|
||||
'')
|
||||
];
|
||||
environment.variables.ATM_DB_PATH = "/var/lib/bitspire/state.db";
|
||||
|
||||
boot.kernel.sysctl."kernel.unprivileged_userns_clone" = 1;
|
||||
security.sudo.wheelNeedsPassword = false;
|
||||
|
||||
# Same first-boot env seed as the x86 installed configs.
|
||||
system.activationScripts.bitspire-env = ''
|
||||
mkdir -p /var/lib/bitspire
|
||||
if [ ! -f /var/lib/bitspire/.env ]; then
|
||||
cp ${pkgs.writeText "bitspire-env-default" (''
|
||||
VITE_LAMASSU_MACHINE_MODEL=${machineModel}
|
||||
VITE_LAMASSU_FIAT_CODE=${fiatCode}
|
||||
VITE_SPIRE_SEED=
|
||||
ELECTRON_FORCE_PROD=1
|
||||
DISPLAY=:0
|
||||
'' + pkgs.lib.optionalString (config.services.bitspire.relayUrl != "") ''
|
||||
VITE_RELAY_URL=${config.services.bitspire.relayUrl}
|
||||
'' + pkgs.lib.optionalString (config.services.bitspire.lnbitsServerPubkey != "") ''
|
||||
VITE_LNBITS_SERVER_PUBKEY=${config.services.bitspire.lnbitsServerPubkey}
|
||||
'')} /var/lib/bitspire/.env
|
||||
chmod 600 /var/lib/bitspire/.env
|
||||
chown bitspire:bitspire /var/lib/bitspire/.env
|
||||
fi
|
||||
'';
|
||||
|
||||
# Electron runtime override (same flags as the x86 fleet, aarch64
|
||||
# electron). No eDP display-reset here — that's UP-Board-specific;
|
||||
# the Pi drives HDMI/DSI directly.
|
||||
systemd.services.bitspire.serviceConfig = {
|
||||
EnvironmentFile = lib.mkForce "/var/lib/bitspire/.env";
|
||||
Environment = "LD_LIBRARY_PATH=${pkgs.stdenv.cc.cc.lib}/lib";
|
||||
ExecStart = lib.mkForce "${pkgs-unstable.electron}/bin/electron --no-sandbox --disable-gpu-sandbox --disable-software-rasterizer --enable-logging ${atm-app}";
|
||||
MemoryMax = lib.mkForce "2G";
|
||||
NoNewPrivileges = lib.mkForce false;
|
||||
ProtectSystem = lib.mkForce false;
|
||||
ProtectHome = lib.mkForce false;
|
||||
PrivateTmp = lib.mkForce false;
|
||||
DevicePolicy = lib.mkForce "auto";
|
||||
DeviceAllow = lib.mkForce [ "char-* rw" ];
|
||||
};
|
||||
|
||||
swapDevices = [{ device = "/var/swapfile"; size = 2048; }];
|
||||
boot.tmp.cleanOnBoot = true;
|
||||
services.openssh.settings.PasswordAuthentication = lib.mkForce true;
|
||||
})
|
||||
];
|
||||
};
|
||||
in
|
||||
{
|
||||
# ── NixOS Configurations (top-level, not per-system) ──────────
|
||||
|
|
@ -293,6 +400,10 @@
|
|||
sintra-installed = mkInstalledConfig "sintra" ./deploy/nixos/hardware/upboard.nix;
|
||||
batm3-installed = mkInstalledConfig "batm3" ./deploy/nixos/hardware/batm3.nix;
|
||||
|
||||
# Raspberry Pi 5 (aarch64) DIY build — Apex 7600 / NV10 over USB-serial.
|
||||
# Build the SD image via packages.aarch64-linux.sd-image-rpi5.
|
||||
rpi5-installed = mkPiConfig "rpi5";
|
||||
|
||||
# USB-bootable variant of batm3-installed. This is the config the
|
||||
# flashed USB stick actually runs — distinct fs labels so stage-1 can't
|
||||
# latch the internal drive, nofail /boot, no growPartition, autoUpgrade
|
||||
|
|
@ -506,6 +617,15 @@
|
|||
# Backwards compat
|
||||
iso = self.nixosConfigurations.douro.config.system.build.isoImage;
|
||||
};
|
||||
|
||||
# ── Packages (aarch64-linux — Raspberry Pi 5 build) ───────────
|
||||
# Flashable SD image for the Pi 5. Build on an aarch64 builder (native Pi
|
||||
# / arm box / `boot.binfmt` emulation on this x86 host):
|
||||
# nix build .#packages.aarch64-linux.sd-image-rpi5
|
||||
packages.aarch64-linux = {
|
||||
sd-image-rpi5 = self.nixosConfigurations.rpi5-installed.config.system.build.sdImage;
|
||||
atm-app-rpi5 = mkAtmAppAarch64 { model = "rpi5"; fiatCode = "USD"; };
|
||||
};
|
||||
}
|
||||
//
|
||||
# ── Dev shells (per-system via flake-utils) ───────────────────
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue