# Raspberry Pi Zero 2 W (aarch64) "Psion sidecar": an RS232 companion for a # Psion 5MX. Two roles, split into submodules: ./serial-ppp.nix (PPP over the # serial line, NAT out to wifi, telnet login) and ./email-proxy.nix (cleartext # POP3/SMTP front end for the Psion's mail client). The raspberry-pi-3 # nixos-hardware profile (the Zero 2 W is the same BCM2837 SoC as the Pi 3) and # key-only sshd (../../modules/ssh.nix) are layered on in the flake host table. # Install notes: see ../../docs/hosts/pizero2w.md. { lib, ... }: { imports = [ ./hardware-configuration.nix ./serial-ppp.nix ./email-proxy.nix ]; # Match the flake's nixosConfigurations attribute name so `nh os switch` # (which selects by the local hostname) resolves without an explicit -H flag. networking.hostName = "lyrathorpe-zero2w"; # Headless server: modules/sway.nix is not imported and # features.swayDesktop.enable defaults to false, so this host keeps plain # TTY/SSH login. # Claude Code is a Node application. It runs on aarch64, but not usefully in # 512 MB of RAM, and its closure is unwelcome on an SD card. features.claudeCode.enable = false; # 512 MB total and no swap partition -- SD cards wear out under swap writes. # Compressed RAM swap instead; zstd is the best ratio-per-cycle the SoC can # sustain. zramSwap = { enable = true; algorithm = "zstd"; }; # The NixOS manual and man page index cost build time and a chunk of the card # for a box that is administered over SSH from elsewhere. documentation.nixos.enable = false; # Own the firmware partition declaratively: every switch rewrites config.txt, # the vendor device trees and the overlays below. Without this the card keeps # whatever config.txt the flashed image wrote and the UART overlays never # load. uboot.enable keeps the GPU firmware chainloading U-Boot -> extlinux, # which is how the NixOS aarch64 SD image boots; leaving it off would rewrite # config.txt without a `kernel=` line and the board would stop booting. hardware.raspberry-pi.firmware = { enable = true; uboot.enable = true; }; hardware.raspberry-pi.configtxt = { settings.all = { # Headless: hand the VideoCore the minimum and leave the rest to Linux. # start_x/camera_auto_detect otherwise reserve VRAM for a camera stack # this board does not have. gpu_mem = 16; start_x = 0; camera_auto_detect = false; # Left on, the firmware auto-loads the KMS display overlay, which wants # more VRAM than this board can spare for a monitor it will never have. display_auto_detect = false; }; # Replaces the profile's default (vc4-kms-v3d), which is display hardware # this host never uses. deviceTreeOverlays.all = [ # Move the PL011 UART off Bluetooth and onto GPIO 14/15, so /dev/ttyAMA0 # is the RS232 header. The mini UART (ttyS0) derives its baud rate from # the core clock and drifts at 115200. { disable-bt = { }; } # RTS/CTS on GPIO 16/17: the Psion's modem profile uses hardware flow # control, and so does pppd in ./serial-ppp.nix. { uart0.ctsrts = true; } ]; }; # Wifi is the Pi's uplink and the route the Psion reaches the internet over # (./serial-ppp.nix masquerades onto it). networking.interfaces.wlan0.useDHCP = true; networking.wireless = { enable = true; interfaces = [ "wlan0" ]; # PSKs stay out of the Nix store: wpa_supplicant reads them at runtime from # this file, which is created on the device (root-owned, 0600) and contains # psk_home= # See ../../docs/hosts/pizero2w.md. secretsFile = "/var/lib/wpa_supplicant/secrets.conf"; networks."CHANGE-ME-SSID".pskRaw = "ext:psk_home"; }; # The board takes a DHCP lease over wifi, so its address moves. mDNS makes it # findable as lyrathorpe-zero2w.local instead of hunting through the router's # lease table -- which matters most on first boot, when it is the only way in. services.avahi = { enable = true; openFirewall = true; publish = { enable = true; addresses = true; workstation = true; }; }; # Default-deny inbound. sshd opens 22 (../../modules/ssh.nix); everything the # Psion talks to is reached over the PPP link, which ./serial-ppp.nix marks # trusted. networking.firewall.enable = true; # See `man configuration.nix` / the stateVersion docs before changing. system.stateVersion = "26.05"; }