diff --git a/deploy/nixos/hardware/raspberry-pi-5.nix b/deploy/nixos/hardware/raspberry-pi-5.nix new file mode 100644 index 0000000..6e56ff1 --- /dev/null +++ b/deploy/nixos/hardware/raspberry-pi-5.nix @@ -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" + ''; +} diff --git a/flake.lock b/flake.lock index 7ea214a..27a1879 100644 --- a/flake.lock +++ b/flake.lock @@ -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" } diff --git a/flake.nix b/flake.nix index 29f44b1..158dfac 100644 --- a/flake.nix +++ b/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) ───────────────────