diff --git a/flake.nix b/flake.nix index 778474b..f0971ef 100644 --- a/flake.nix +++ b/flake.nix @@ -120,6 +120,7 @@ }; in { nix.package = lix-cross.lix; }) agenix.nixosModules.default + ./modules/nixos/hardware/uconsole-cm5-aio-v2.nix ./hosts/uconsole-cm5/configuration.nix ./hosts/uconsole-cm5/hardware-configuration.nix ]; @@ -147,6 +148,7 @@ nixos-raspberrypi.nixosModules.sd-image nixos-uconsole.nixosModules.uconsole-cm5 agenix.nixosModules.default + ./modules/nixos/hardware/uconsole-cm5-aio-v2.nix ./hosts/uconsole-cm5/configuration.nix ]; }).config.system.build.sdImage; diff --git a/hosts/uconsole-cm5/configuration.nix b/hosts/uconsole-cm5/configuration.nix index 438578a..19e7166 100644 --- a/hosts/uconsole-cm5/configuration.nix +++ b/hosts/uconsole-cm5/configuration.nix @@ -26,5 +26,20 @@ # Firmware hardware.enableRedistributableFirmware = true; - # DSI display fix: le kernel patch est dans flake.nix (patches/0008-panel-cwu50-no-burst.patch) + # HackerGadgets AIO v2 board + hardware.uconsole-cm5-aio-v2 = { + enable = true; + + # Rails actifs au boot + bootRails = { + GPS = false; # activé à la demande via aiov2_ctl GPS on + LORA = false; # activé à la demande via aiov2_ctl LORA on + SDR = false; # activé à la demande via aiov2_ctl SDR on + USB = false; # activé à la demande via aiov2_ctl USB on + }; + + enableGPS = false; # activer quand antenne GPS branchée + }; + + # DSI display fix: le kernel patch est dans flake.nix (patches/0008-panel-cwu50-fix-init-seq1.patch) } diff --git a/modules/nixos/hardware/uconsole-cm5-aio-v2.nix b/modules/nixos/hardware/uconsole-cm5-aio-v2.nix new file mode 100644 index 0000000..a216da7 --- /dev/null +++ b/modules/nixos/hardware/uconsole-cm5-aio-v2.nix @@ -0,0 +1,169 @@ +{ 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.raspberrypi-tools}/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 = lib.fakeSha256; # will fail with real hash on first build + }; + + 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 + raspberrypi-tools # 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"; + }; + }; + }; +}