wip(rpi4): park the Pi 4 sketch — superseded by the Pi 5 machinery in #87

Never-committed working tree state found in the dev worktree: a Pi 4 hardware
module, a setup walkthrough, an upboard-serial refactor, and flake wiring that
does not evaluate (aarch64 packages block nested inside packages.x86_64-linux;
pkgs-aarch64 built with `inherit aarch64` instead of `system`, so it is really
x86; references a nixosConfigurations.rpi4-installed that is never defined).

Parked verbatim for reference. The real Pi 4 target is rebuilt on top of #87's
mkPiInstalled/mkPiImage shape in feat/rpi4-target.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013A6683cCHnQxFUosx1krY4
This commit is contained in:
Padreug 2026-09-20 11:17:41 +02:00
commit 7e59951fcc
7 changed files with 533 additions and 4 deletions

View file

@ -14,7 +14,8 @@ deploy/nixos/
├── hardware/
│ ├── douro.nix # Dell OptiPlex 9030 AIO (stock Douro motherboard; SATA SSD, eGalax touch)
│ ├── batm3.nix # GeneralBytes BATM3 chassis with a Dell OptiPlex 9030 AIO grafted in (custom mod; WireGuard wired in)
│ └── upboard.nix # Aaeon UP Board (Sintra + tejo; eMMC root via sdhci-acpi + mmc_block)
│ ├── upboard.nix # Aaeon UP Board (Sintra + tejo; eMMC root via sdhci-acpi + mmc_block)
│ └── raspberry-pi4.nix # Raspberry Pi 4 (aarch64) hardware configuration
├── udev/
│ └── 99-bitspire-hardware.rules # additional udev rules (loaded via configuration.nix)
├── provision-atm.sh # Push LNbits credentials to a deployed ATM via SSH
@ -34,7 +35,7 @@ Each ATM model has two flake outputs:
| `packages.x86_64-linux.iso-<model>` | ISO | ISO image of the live variant |
| `packages.x86_64-linux.disk-image-<model>` | raw image | dd-able full disk image of the installed variant |
Models: `douro`, `tejo`, `sintra`, `batm3`.
Models: `douro`, `tejo`, `sintra`, `batm3`, `rpi4`.
```bash
# Build a Sintra disk image
@ -43,11 +44,111 @@ nix build .#disk-image-sintra
# Build a live ISO for tejo
nix build .#iso-tejo
# Build a Raspberry Pi 4 disk image (aarch64)
nix build .#disk-image-rpi4
# → result/nixos.img (Raspberry Pi firmware bootloader, GPT+ESP)
```
## Deploying to Sintra (full walkthrough)
The Sintra ships from the factory with whatever its previous OS was — Android, vendor Linux, or wiped. You need an Alpine live USB to act as your installer.
The Sintra ships from the factory with whatever its previous OS was — Android, vendor Linux, or wiped.
## Deploying to Raspberry Pi 4 (full walkthrough)
The Raspberry Pi 4 uses an SD card as primary storage and boots via the Raspberry Pi firmware bootloader (rpi-bootloader). Unlike Intel-based ATMs that use systemd-boot, the RPi4 config uses the native Raspberry Pi bootloader.
### Prerequisites
- Raspberry Pi 4 board (4GB or 8GB RAM recommended for Electron)
- 32GB+ SD card (or external SSD for better reliability)
- Network connection (ethernet recommended for stability)
- SSH access or a serial console (UART) for first-boot provisioning
### Build the disk image
```bash
nix build .#disk-image-rpi4
# → result/nixos.img (raw disk image, aarch64)
```
### Flash to SD card
```bash
# Replace /dev/sdX with your SD card device
sudo dd if=result/nixos.img of=/dev/sdX bs=4M status=progress conv=fsync && sync
```
**Important:** Verify the target is the SD card, not your main disk:
```bash
lsblk -f /dev/sdX
# Should show 'SD_CARD' or similar, not a hard drive name
```
### Boot and provision
1. Insert the SD card into the Raspberry Pi 4.
2. Connect Ethernet and power on.
3. Wait for boot (you should see Raspberry Pi firmware loading messages).
4. The kiosk screen should show "ATM unavailable — needs provisioning".
From your dev box, provision LNbits credentials:
```bash
LNBITS_SERVER_PUBKEY=$(docker logs <lnbits-container> 2>&1 | \
grep -oP 'Public key \(share this\):\s*\K[a-f0-9]{64}' | tail -1)
RELAY_URL=ws://<dev-lan-ip>:5001/nostrrelay/test \
LNBITS_SERVER_PUBKEY="$LNBITS_SERVER_PUBKEY" \
ATM_PRIVATE_KEY=$(openssl rand -hex 32) \
bash deploy/nixos/provision-atm.sh <rpi4-ip> 22
```
The script SSHes to `bitspire@<rpi4-ip>:22`, writes `/var/lib/bitspire/.env`, and restarts `bitspire.service`. After a few seconds the kiosk connects to LNbits over nostr-transport and shows the live UI.
### First-time deploy
**Save the generated `ATM_PRIVATE_KEY`.** LNbits identifies this ATM by its public key; if you regenerate the key on re-provision, LNbits will auto-create a fresh wallet and the old wallet's balance becomes inaccessible.
### Re-flash
To preserve the ATM's identity and transaction history, backup the `.env` and `state.db` from the running Raspberry Pi before reflashing:
```bash
# From the Raspberry Pi
mkdir -p ~/rpi4-backup-$(date +%Y%m%d)
cp /var/lib/bitspire/.env ~/rpi4-backup-$(date +%Y%m%d)/
cp /var/lib/bitspire/state.db ~/rpi4-backup-$(date +%Y%m%d)/
# Copy to dev box
scp bitspire@<rpi4-ip>:~/rpi4-backup-*/.env ~/rpi4-backup-*/
scp bitspire@<rpi4-ip>:~/rpi4-backup-*/state.db ~/rpi4-backup-*/
```
On re-flash, source the backup values:
```bash
set -a; source ~/rpi4-backup-<date>/.env; set +a
ATM_PRIVATE_KEY=$VITE_ATM_PRIVATE_KEY \
LNBITS_SERVER_PUBKEY=$VITE_LNBITS_SERVER_PUBKEY \
RELAY_URL=$VITE_RELAY_URL \
bash deploy/nixos/provision-atm.sh <rpi4-ip> 22
```
### Hardware-specific notes
- **Storage:** SD cards are slower and have limited write cycles compared to SSDs. Consider using a high-quality card and avoid frequent rewrites. For production, an external SSD via USB 3.0 is recommended for better reliability.
- **Power:** The Raspberry Pi 4 can draw up to 3A at 5V. Use a reliable power supply rated for at least 5V 3A.
- **Thermal:** The Pi 4 runs warm. Ensure adequate cooling (passive heatsinks or a fan) for 24/7 operation.
- **Console:** The serial console is available at `/dev/ttyAMA0` (PL011 UART) at 115200 baud, useful for debugging.
- **Swap:** A 2GB swapfile is enabled by default to prevent hard-freeze under memory pressure. Consider increasing this on 4GB models.
### Troubleshooting
- **Boot fails:** Check the SD card is properly flashed and inserted. Try rebuilding the image with a fresh `nix build .#disk-image-rpi4`.
- **No network:** Verify the Ethernet cable is connected and the Pi's lights show activity. Check `/etc/resolv.conf` after boot.
- **ATM doesn't connect to LNbits:** Verify `VITE_RELAY_URL` and `VITE_LNBITS_SERVER_PUBKEY` are set in `/var/lib/bitspire/.env` via SSH: `ssh bitspire@<rpi4-ip> 'cat /var/lib/bitspire/.env'`.
- **Electron fails to start:** Check memory usage (`free -h`). On 4GB models, close other processes to free up RAM. You need an Alpine live USB to act as your installer.
### 0. (Re-flash only) Preserve state from the existing Sintra

View file

@ -0,0 +1,124 @@
# Raspberry Pi 4 Hardware Configuration
# For bitSpire deployments on Raspberry Pi 4
# Requires: 4GB+ RAM (8GB recommended for Electron + kiosk)
# Storage: SD card or external SSD/eMMC (minimum 32GB)
#
# Note: Import shared serial peripheral configuration for consistency
# with other bitSpire hardware (useful for future extensions).
{ config, lib, pkgs, ... }:
{
# Import shared serial peripheral configuration (useful for future hardware extensions)
imports = [ ./upboard-serial.nix ];
# Raspberry Pi 4 uses aarch64 architecture
boot.kernelPackages = pkgs.linuxPackages_rpi4;
boot = {
loader = {
# Raspberry Pi 4 uses the Raspberry Pi firmware bootloader (rpi-bootloader)
grub.enable = lib.mkForce false;
# raspberryPi.enable has been removed in recent nixpkgs
# rpi-bootloader is now built into rpi4 kernel packages
};
initrd.availableKernelModules = [
"xhci_pci"
"usb_storage"
"sdhci_pci"
"sdhci"
"sd_mod"
"brcmmac"
"brcmfmac"
"mmc_block"
"cma"
];
initrd.kernelModules = [
"sdhci_pci"
"sdhci"
"mmc_block"
"brcmmac"
"brcmfmac"
"cma"
];
kernelModules = [
"kvm-arm"
"uas"
];
kernelParams = [
"console=tty1"
"console=ttyAMA0,115200n8"
"console=ttyS0,115200n8"
"root=/dev/mmcblk0p2"
"rootfstype=ext4"
"rootwait"
"rw"
# Disable PCIe to save power (optional - uncomment if needed)
# "pcie_aspm=off"
];
};
# Filesystem layout: SD card with GPT partition table
# Partition 1: EFI System Partition (FAT32)
# Partition 2: Root filesystem (ext4)
fileSystems."/" = {
device = "/dev/disk/by-label/nixos";
fsType = "ext4";
options = [ "noatime" ];
};
fileSystems."/boot" = {
device = "/dev/disk/by-label/ESP";
fsType = "vfat";
options = [ "defaults" "nofail" ];
};
# Swap configuration for better reliability on SD cards
swapDevices = [{
device = "/swapfile";
size = 2048; # 2GB swap
}];
# Power management for Raspberry Pi
powerManagement = {
enable = true;
cpuFreqGovernor = "ondemand";
};
# Disable suspend/hibernate for kiosk stability
systemd.targets = {
sleep.enable = false;
suspend.enable = false;
hibernate.enable = false;
hybrid-sleep.enable = false;
};
# Hardware-specific settings
hardware = {
# Raspberry Pi 4 has no discrete GPU (uses VideoCore)
graphics = {
enable = true;
};
# USB mass storage drivers (not applicable for ARM - handled by kernel modules)
# usb.enable = true;
# usb.enableUSBHub = true;
# RPi4-specific tweaks
enableRedistributableFirmware = true;
# CPU firmware (not applicable for ARM - no x86 microcode)
# cpu.amd64.updateMicrocode = false; # Not applicable for ARM
};
# USB configuration (not applicable for ARM)
# hardware.usb.enable = true;
# hardware.usb.enableUSBHub = true;
# Network configuration
# useDHCP is set in configuration.nix (base config)
networking.interfaces.eth0.useDHCP = true;
}

View file

@ -0,0 +1,32 @@
# Shared serial peripheral configuration for UP Board hardware
# (Sintra, tejo, and any other systems with similar UART layout)
#
# Serial ports used by bitSpire ATM:
# ttyJ4 = Printer (Nippon NP-2511D-2) - optional on some configs
# ttyJ5 = Validator (iVizion, ID003 protocol)
# ttyJ7 = Dispenser (Fujitsu F53/F56)
#
# These symlinks + udev rules are shared between live.nix (for testing)
# and hardware-specific modules (upboard.nix, douro.nix).
{ config, lib, pkgs, ... }:
{
# Serial peripheral symlinks
services.udev.extraRules = ''
# ── Validator (iVizion ID003) ─────────────────────────────────────
# ttyJ5 on UP Board (FTDI USB bridge) → /dev/ttyJ5
KERNEL=="ttyJ5", SYMLINK+="validator-device"
SUBSYSTEM=="tty", GROUP="uucp", MODE="0666"
# ── Dispenser (Fujitsu F56) ───────────────────────────────────────
# ttyJ7 on UP Board (SoC MMIO UART) → /dev/ttyJ7
KERNEL=="ttyJ7", SYMLINK+="dispenser-device"
SUBSYSTEM=="tty", GROUP="uucp", MODE="0666"
# ── Printer (Nippon NP-2511D-2) ───────────────────────────────────
# ttyJ4 on UP Board (optional - not all configs use it)
KERNEL=="ttyJ4", SYMLINK+="printer-device", OPTIONS+="static_node=ttyJ4"
SUBSYSTEM=="tty", GROUP="uucp", MODE="0666"
'';
}