me like nix
0

Configure Feed

Select the types of activity you want to include in your feed.

1{ pkgs, lib, config, ... }: 2 3let 4 cfg = config.pi; 5 6 # Workaround from https://github.com/NixOS/nixos-hardware/blob/master/raspberry-pi/4/apply-overlays-dtmerge.nix 7 deviceTree_overlay = _final: prev: { 8 deviceTree = { 9 applyOverlays = prev.callPackage ./overlays/apply-overlays-dtmerge.nix { }; 10 compileDTS = prev.deviceTree.compileDTS; 11 }; 12 }; 13 14 # Custom libcamera with Raspberry Pi IPA/pipeline support 15 libcamera-rpi = pkgs.libcamera.overrideAttrs (old: { 16 mesonFlags = (old.mesonFlags or [ ]) ++ [ 17 "-Dipas=rpi/vc4,rpi/pisp" 18 "-Dpipelines=rpi/vc4,rpi/pisp" 19 ]; 20 }); 21 22 # rpicam-apps using custom libcamera 23 rpicam-apps = pkgs.stdenv.mkDerivation rec { 24 pname = "rpicam-apps"; 25 version = "1.11.1"; 26 27 src = pkgs.fetchFromGitHub { 28 owner = "raspberrypi"; 29 repo = "rpicam-apps"; 30 rev = "v${version}"; 31 hash = "sha256-hVoKbvWFeramPkHuibJwUgFOPS9v588+K8828a1fNnA="; 32 }; 33 34 nativeBuildInputs = with pkgs; [ 35 meson 36 ninja 37 pkg-config 38 ]; 39 40 buildInputs = [ 41 libcamera-rpi 42 pkgs.libdrm 43 pkgs.libexif 44 pkgs.libjpeg 45 pkgs.libpng 46 pkgs.libtiff 47 pkgs.boost 48 pkgs.ffmpeg 49 ]; 50 51 mesonFlags = [ 52 "-Denable_libav=enabled" 53 "-Denable_drm=enabled" 54 "-Denable_egl=disabled" 55 "-Denable_qt=disabled" 56 "-Denable_opencv=disabled" 57 "-Denable_tflite=disabled" 58 "-Denable_hailo=disabled" 59 ]; 60 61 meta = with lib; { 62 description = "Raspberry Pi camera applications"; 63 homepage = "https://github.com/raspberrypi/rpicam-apps"; 64 license = licenses.bsd2; 65 platforms = [ "aarch64-linux" ]; 66 }; 67 }; 68in 69{ 70 options.pi = { 71 streamName = lib.mkOption { 72 type = lib.types.str; 73 description = "Name of the camera stream"; 74 }; 75 76 resolution = { 77 width = lib.mkOption { 78 type = lib.types.int; 79 default = 1920; 80 description = "Camera resolution width"; 81 }; 82 height = lib.mkOption { 83 type = lib.types.int; 84 default = 1080; 85 description = "Camera resolution height"; 86 }; 87 }; 88 89 framerate = lib.mkOption { 90 type = lib.types.int; 91 default = 30; 92 description = "Camera framerate"; 93 }; 94 95 deviceTreeFilter = lib.mkOption { 96 type = lib.types.str; 97 description = "Device tree filter pattern"; 98 }; 99 100 deviceTreeCompatible = lib.mkOption { 101 type = lib.types.str; 102 description = "Device tree compatible string (e.g., brcm,bcm2711)"; 103 }; 104 105 gpuMem = lib.mkOption { 106 type = lib.types.int; 107 default = 256; 108 description = "GPU memory allocation in MB"; 109 }; 110 111 flipCamera = lib.mkOption { 112 type = lib.types.bool; 113 default = false; 114 description = "Flip camera image vertically and horizontally (180 degree rotation)"; 115 }; 116 117 }; 118 119 config = { 120 nix.settings.trusted-users = [ "sean" ]; 121 122 # Pre-generated SSH host key for agenix decryption (shared across all Pis) 123 services.openssh.hostKeys = [ 124 { 125 path = "/etc/ssh/ssh_host_ed25519_key"; 126 type = "ed25519"; 127 } 128 ]; 129 130 environment.etc."ssh/ssh_host_ed25519_key" = { 131 source = /home/sean/nixos-config/secrets/pi_host_key; 132 mode = "0600"; 133 }; 134 135 # Agenix configuration - use Nix store path directly so the key is available 136 # before the etc activation script runs (agenix activates before etc) 137 age.identityPaths = [ "${/home/sean/nixos-config/secrets/pi_host_key}" ]; 138 age.secrets.wifi = { 139 file = ../../secrets/wifi.age; 140 mode = "0444"; 141 }; 142 143 # WiFi configuration using wpa_supplicant with agenix credentials 144 networking.wireless = { 145 enable = true; 146 secretsFile = config.age.secrets.wifi.path; 147 networks."GL-MT6000-6a6".pskRaw = "ext:WIFI_PSK"; 148 }; 149 150 # Enable DHCP for ethernet 151 networking.useDHCP = true; 152 # Add device tree overlay for dtmerge support 153 nixpkgs.overlays = [ deviceTree_overlay ]; 154 155 # Disable ZFS which isn't supported on Pi 156 boot.supportedFilesystems = lib.mkForce [ "vfat" "ext4" ]; 157 158 # Pi kernel lacks device-mapper, so use legacy initrd (not systemd) 159 boot.initrd.systemd.enable = false; 160 161 # Enable SSH for headless setup 162 services.openssh = { 163 enable = true; 164 settings = { 165 PasswordAuthentication = false; 166 PermitRootLogin = "no"; 167 }; 168 }; 169 170 # User config 171 users.users.sean = { 172 isNormalUser = true; 173 extraGroups = [ "wheel" "video" ]; 174 openssh.authorizedKeys.keys = [ 175 "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDCIqgZ7kedxo+mOW7YG73Vp3zel3h180y3GKvHtRsXfGlpIIvRDy7pgCBQ4AGXYD4y78URQmFohYSAPqCPOPaWcU2un3XG9KvCzEsHmsbskPonitUmCiKvrKkb6oW4jCBtd7AEtBn+AiajAQFtPZ7NN2Df3AmTypvR6Irg7R+nxnfc9NTIHmGvxSDyWcbb4pguL20sctUSqGL6xGh8q/bqhdOThSimM+z9bEUNxK/5rPhwkNniMrp4pJcUrUiAh5/4DiRFG6KT+oeg+/myoz/Z1sPvAs7u/8JDQI4RshRD8Hu0oTkRBN6Hxj478q2SXbeBUZlD6IdjP3RhGpmSecoDdtWqKbpuV3eVRtQtba3KL86GBeV/bugaOdJ1Aud+1SOFJreAAuvxzMMKT+cdQZk6oOPP148DA/No+mDm/2S43lcdCXh79wA6YRAmKQ8jmZxTCtPutrvuZK1rguvvUlEoG/vhdNHh7eDa4Td07V6bjCRPUl8qk/e4M0E3pwsTlZc=" 176 ]; 177 }; 178 179 # Allow sudo without password for wheel group 180 security.sudo.wheelNeedsPassword = false; 181 182 # go2rtc for camera streaming to Home Assistant 183 services.go2rtc = { 184 enable = true; 185 settings = { 186 ffmpeg.bin = "${pkgs.ffmpeg}/bin/ffmpeg"; 187 streams = { 188 "${cfg.streamName}" = "exec:${rpicam-apps}/bin/rpicam-vid -t 0 --width ${toString cfg.resolution.width} --height ${toString cfg.resolution.height} --framerate ${toString cfg.framerate} --codec h264 --inline${lib.optionalString cfg.flipCamera " --vflip --hflip"} -o -"; 189 }; 190 }; 191 }; 192 193 # udev rule to give video group access to DMA heap devices (required for libcamera) 194 services.udev.extraRules = '' 195 SUBSYSTEM=="dma_heap", GROUP="video", MODE="0660" 196 ''; 197 198 # Override go2rtc systemd service to run as root for camera access 199 systemd.services.go2rtc.serviceConfig = { 200 User = lib.mkForce "root"; 201 }; 202 203 # Camera and system tools 204 environment.systemPackages = [ 205 pkgs.ffmpeg 206 pkgs.libraspberrypi 207 libcamera-rpi 208 rpicam-apps 209 pkgs.v4l-utils 210 ]; 211 212 # Device tree configuration for Pi Camera v3 (IMX708) 213 hardware.deviceTree.filter = cfg.deviceTreeFilter; 214 hardware.deviceTree.overlays = [ 215 { 216 name = "imx708-overlay"; 217 dtsText = '' 218 // SPDX-License-Identifier: GPL-2.0-only 219 // Definitions for IMX708 camera module on VC I2C bus 220 /dts-v1/; 221 /plugin/; 222 223 /{ 224 compatible = "${cfg.deviceTreeCompatible}"; 225 226 fragment@0 { 227 target = <&i2c0if>; 228 __overlay__ { 229 status = "okay"; 230 }; 231 }; 232 233 clk_frag: fragment@1 { 234 target = <&cam1_clk>; 235 __overlay__ { 236 status = "okay"; 237 clock-frequency = <24000000>; 238 }; 239 }; 240 241 fragment@2 { 242 target = <&i2c0mux>; 243 __overlay__ { 244 status = "okay"; 245 }; 246 }; 247 248 reg_frag: fragment@3 { 249 target = <&cam1_reg>; 250 cam_reg: __overlay__ { 251 startup-delay-us = <70000>; 252 off-on-delay-us = <30000>; 253 regulator-min-microvolt = <2700000>; 254 regulator-max-microvolt = <2700000>; 255 }; 256 }; 257 258 i2c_frag: fragment@100 { 259 target = <&i2c_csi_dsi>; 260 __overlay__ { 261 #address-cells = <1>; 262 #size-cells = <0>; 263 status = "okay"; 264 265 // IMX708 sensor configuration (from imx708.dtsi) 266 cam_node: imx708@1a { 267 compatible = "sony,imx708"; 268 reg = <0x1a>; 269 status = "okay"; 270 271 clocks = <&cam1_clk>; 272 clock-names = "inclk"; 273 274 vana1-supply = <&cam1_reg>; 275 vana2-supply = <&cam_dummy_reg>; 276 vdig-supply = <&cam_dummy_reg>; 277 vddl-supply = <&cam_dummy_reg>; 278 279 rotation = <180>; 280 orientation = <2>; 281 282 port { 283 cam_endpoint: endpoint { 284 clock-lanes = <0>; 285 data-lanes = <1 2>; 286 clock-noncontinuous; 287 link-frequencies = 288 /bits/ 64 <450000000>; 289 }; 290 }; 291 }; 292 293 // VCM (autofocus motor) configuration 294 vcm_node: dw9817@c { 295 compatible = "dongwoon,dw9817-vcm"; 296 reg = <0x0c>; 297 status = "okay"; 298 VDD-supply = <&cam1_reg>; 299 }; 300 }; 301 }; 302 303 csi_frag: fragment@101 { 304 target = <&csi1>; 305 csi: __overlay__ { 306 status = "okay"; 307 brcm,media-controller; 308 309 port { 310 csi_ep: endpoint { 311 remote-endpoint = <&cam_endpoint>; 312 clock-lanes = <0>; 313 data-lanes = <1 2>; 314 clock-noncontinuous; 315 }; 316 }; 317 }; 318 }; 319 320 __overrides__ { 321 rotation = <&cam_node>,"rotation:0"; 322 orientation = <&cam_node>,"orientation:0"; 323 media-controller = <&csi>,"brcm,media-controller?"; 324 cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>, 325 <&csi_frag>, "target:0=",<&csi0>, 326 <&clk_frag>, "target:0=",<&cam0_clk>, 327 <&reg_frag>, "target:0=",<&cam0_reg>, 328 <&cam_node>, "clocks:0=",<&cam0_clk>, 329 <&cam_node>, "vana1-supply:0=",<&cam0_reg>, 330 <&vcm_node>, "VDD-supply:0=",<&cam0_reg>; 331 vcm = <&vcm_node>, "status", 332 <0>, "=4"; 333 link-frequency = <&cam_endpoint>,"link-frequencies#0"; 334 }; 335 }; 336 337 &cam_endpoint { 338 remote-endpoint = <&csi_ep>; 339 }; 340 ''; 341 } 342 ]; 343 344 # Raspberry Pi firmware for camera 345 hardware.enableRedistributableFirmware = true; 346 347 # Add camera config and overlays to firmware partition 348 sdImage.populateFirmwareCommands = lib.mkAfter '' 349 chmod u+w ./firmware/config.txt 350 cat >> ./firmware/config.txt << EOF 351 352# Camera support - Pi Camera v3 (IMX708) 353gpu_mem=${toString cfg.gpuMem} 354EOF 355 356 # Copy device tree overlays for camera auto-detect 357 if [ -d ${pkgs.raspberrypifw}/share/raspberrypi/boot/overlays ]; then 358 cp -r ${pkgs.raspberrypifw}/share/raspberrypi/boot/overlays ./firmware/ 359 fi 360 ''; 361 362 # Firewall 363 networking.firewall.allowedTCPPorts = [ 364 22 # SSH 365 1984 # go2rtc API 366 8554 # RTSP 367 ]; 368 }; 369}