{ config, lib, pkgs, ... }: with lib; let cfg = config.hardware.uconsole-cm5-aio-v2; # GPIO pin map matching the AIO v2 board hardware # SDR (RTL-SDR): GPIO 7 # LoRa (SX1262) : GPIO 16 # USB Hub Interne: GPIO 23 # GPS (GNSS) : GPIO 27 gpioMap = { GPS = 27; LORA = 16; SDR = 7; USB = 23; }; # Generate a script that applies boot rail states via pinctrl applyRailsScript = pkgs.writeShellScript "apply-aio-v2-rails" ( '' set -e PINCTRL=${pkgs.libraspberrypi}/bin/pinctrl '' + concatStringsSep "" (mapAttrsToList (name: pin: '' if [ "${if cfg.bootRails.${name} then "1" else "0"}" = "1" ]; then echo "AIO v2: ${name} (GPIO${toString pin}) → ON" $PINCTRL set ${toString pin} op dh else echo "AIO v2: ${name} (GPIO${toString pin}) → OFF" $PINCTRL set ${toString pin} op dl fi '') gpioMap) ); # aiov2_ctl CLI tool — fetched from GitHub, available as `aiov2_ctl` aiov2CtlPkg = pkgs.stdenv.mkDerivation rec { pname = "aiov2_ctl"; version = "0-unstable-2026-06-16"; src = pkgs.fetchFromGitHub { owner = "hackergadgets"; repo = "aiov2_ctl"; rev = "main"; hash = "sha256-ddffc50568fb082340283ab4acb58d22ddbf429985a71f1804a6ca1deb455289"; }; dontUnpack = true; installPhase = '' mkdir -p $out/bin $out/share/aiov2_ctl/img cp $src/aiov2_ctl.py $out/bin/aiov2_ctl chmod +x $out/bin/aiov2_ctl patchShebangs $out/bin/aiov2_ctl substituteInPlace $out/bin/aiov2_ctl \ --replace-fail '"/usr/local/share/aiov2_ctl/img/' '"'$out'/share/aiov2_ctl/img/' cp -r $src/img/* $out/share/aiov2_ctl/img/ ''; meta = { description = "HackerGadgets uConsole AIO v2 GPIO control and telemetry tool"; homepage = "https://github.com/hackergadgets/aiov2_ctl"; license = lib.licenses.mit; maintainers = with lib.maintainers; [ ]; platforms = [ "aarch64-linux" ]; }; }; in { options.hardware.uconsole-cm5-aio-v2 = { enable = mkEnableOption "HackerGadgets uConsole AIO v2 board support"; bootRails = { GPS = mkOption { type = types.bool; default = false; description = "Enable GPS module at boot (GPIO 27)"; }; LORA = mkOption { type = types.bool; default = false; description = "Enable LoRa module at boot (GPIO 16)"; }; SDR = mkOption { type = types.bool; default = false; description = "Enable SDR module at boot (GPIO 7)"; }; USB = mkOption { type = types.bool; default = false; description = "Enable internal USB hub at boot (GPIO 23)"; }; }; package = mkOption { type = types.package; default = aiov2CtlPkg; defaultText = literalExpression "aiov2CtlPkg"; description = "aiov2_ctl package to use"; }; enableGPS = mkOption { type = types.bool; default = false; description = '' Enable GPS UART (/dev/ttyAMA0 at 9600 baud). Requires enabling UART on the CM5 via boot.kernelParams. ''; }; enableGUI = mkOption { type = types.bool; default = false; description = '' Enable the system tray GUI for aiov2_ctl. Requires a desktop environment with system tray support. ''; }; }; config = mkIf cfg.enable { # Package the aiov2_ctl tool + pinctrl environment.systemPackages = with pkgs; [ cfg.package libraspberrypi # provides pinctrl ]; # Boot rail systemd oneshot service systemd.services.aiov2-rails-boot = { description = "Apply AIO v2 GPIO rail boot states"; after = [ "local-fs.target" ]; wants = [ "local-fs.target" ]; before = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "oneshot"; ExecStart = "${applyRailsScript}"; RemainAfterExit = true; }; }; # GPS configuration boot.kernelParams = mkIf cfg.enableGPS [ "uart0=on" ]; users.users = mkIf cfg.enableGPS { gortium = { extraGroups = [ "dialout" ]; }; }; # GUI autostart (XDG) systemd.user.services.aiov2-ctl-gui = mkIf cfg.enableGUI { description = "AIO v2 System Tray Controller"; after = [ "graphical-session.target" ]; wants = [ "graphical-session.target" ]; wantedBy = [ "graphical-session.target" ]; serviceConfig = { Type = "simple"; ExecStart = "${cfg.package}/bin/aiov2_ctl --gui"; Restart = "on-failure"; RestartSec = 5; }; environment = { AIOV2_CTL_DEBUG = "0"; }; }; }; }