me like nix
0

Configure Feed

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

update rpi cam setup

author
Sean Aye
date (Apr 20, 2026, 1:00 PM -0400) commit bda163ae parent 7b440484 change-id vnokkrzm
+38 -361
-50
hosts/pi-common/overlays/apply-overlays-dtmerge.nix
··· 1 - # modification of nixpkgs deviceTree.applyOverlays to resolve https://github.com/NixOS/nixpkgs/issues/125354 2 - # derived from https://github.com/NixOS/nixpkgs/blob/916ca8f2b0c208def051f8ea9760c534a40309db/pkgs/os-specific/linux/device-tree/default.nix 3 - { lib, stdenvNoCC, dtc, libraspberrypi }: 4 - 5 - with lib; (base: overlays': stdenvNoCC.mkDerivation { 6 - name = "device-tree-overlays"; 7 - nativeBuildInputs = [ dtc libraspberrypi ]; 8 - buildCommand = 9 - let 10 - overlays = toList overlays'; 11 - in 12 - '' 13 - mkdir -p $out 14 - cd "${base}" 15 - find . -type f -name '*.dtb' -print0 \ 16 - | xargs -0 cp -v --no-preserve=mode --target-directory "$out" --parents 17 - 18 - for dtb in $(find "$out" -type f -name '*.dtb'); do 19 - dtbCompat=$(fdtget -t s "$dtb" / compatible 2>/dev/null || true) 20 - # skip files without `compatible` string 21 - test -z "$dtbCompat" && continue 22 - 23 - ${flip (concatMapStringsSep "\n") overlays (o: '' 24 - overlayCompat="$(fdtget -t s "${o.dtboFile}" / compatible)" 25 - 26 - # skip incompatible and non-matching overlays 27 - if [[ ! "$dtbCompat" =~ "$overlayCompat" ]]; then 28 - echo "Skipping overlay ${o.name}: incompatible with $(basename "$dtb")" 29 - elif ${if ((o.filter or null) == null) then "false" else '' 30 - [[ "''${dtb//${o.filter}/}" == "$dtb" ]] 31 - ''} 32 - then 33 - echo "Skipping overlay ${o.name}: filter does not match $(basename "$dtb")" 34 - else 35 - echo -n "Applying overlay ${o.name} to $(basename "$dtb")... " 36 - mv "$dtb"{,.in} 37 - 38 - # dtmerge requires a .dtbo ext for dtbo files, otherwise it adds it to the given file implicitly 39 - dtboWithExt="$TMPDIR/$(basename "${o.dtboFile}").dtbo" 40 - cp -r ${o.dtboFile} "$dtboWithExt" 41 - 42 - dtmerge "$dtb.in" "$dtb" "$dtboWithExt" 43 - 44 - echo "ok" 45 - rm "$dtb.in" "$dtboWithExt" 46 - fi 47 - '')} 48 - 49 - done''; 50 - })
+6 -15
modules/hosts/pi.nix
··· 4 4 nm = config.flake.modules.nixos; 5 5 in 6 6 { 7 - flake.nixosConfigurations.pi = inputs.nixpkgs.lib.nixosSystem { 8 - system = "aarch64-linux"; 7 + flake.nixosConfigurations.pi = inputs.nixos-raspberrypi.lib.nixosSystem { 9 8 modules = [ 10 - "${inputs.nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64.nix" 11 - inputs.nixos-hardware.nixosModules.raspberry-pi-4 9 + { imports = [ 10 + inputs.nixos-raspberrypi.nixosModules.raspberry-pi-4.base 11 + inputs.nixos-raspberrypi.nixosModules.sd-image 12 + ]; 13 + } 12 14 inputs.agenix.nixosModules.default 13 - 14 - # Allow missing kernel modules 15 - ({ 16 - nixpkgs.overlays = [ 17 - (final: super: { 18 - makeModulesClosure = x: super.makeModulesClosure (x // { allowMissing = true; }); 19 - }) 20 - ]; 21 - }) 22 15 23 16 # Aspect modules 24 17 nm.pi-camera ··· 36 29 height = 1080; 37 30 }; 38 31 framerate = 30; 39 - deviceTreeFilter = "bcm2711-rpi-4*.dtb"; 40 - deviceTreeCompatible = "brcm,bcm2711"; 41 32 gpuMem = 256; 42 33 }; 43 34
+19 -41
modules/hosts/pizero.nix
··· 4 4 nm = config.flake.modules.nixos; 5 5 in 6 6 { 7 - flake.nixosConfigurations.pizero = inputs.nixpkgs.lib.nixosSystem { 8 - system = "aarch64-linux"; 7 + flake.nixosConfigurations.pizero = inputs.nixos-raspberrypi.lib.nixosSystem { 9 8 modules = [ 10 - "${inputs.nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64.nix" 11 - inputs.agenix.nixosModules.default 12 - 13 - # Allow missing kernel modules 14 - ({ 15 - nixpkgs.overlays = [ 16 - (final: super: { 17 - makeModulesClosure = x: super.makeModulesClosure (x // { allowMissing = true; }); 18 - }) 9 + { imports = [ 10 + inputs.nixos-raspberrypi.nixosModules.raspberry-pi-02.base 11 + inputs.nixos-raspberrypi.nixosModules.sd-image 19 12 ]; 20 - }) 13 + } 14 + inputs.agenix.nixosModules.default 21 15 22 16 # Aspect modules 23 17 nm.pi-camera ··· 25 19 nm.pi-stability 26 20 27 21 # Pi Zero 2W specific settings 28 - ( 29 - { pkgs, lib, ... }: 30 - { 31 - networking.hostName = "pizero"; 22 + { 23 + networking.hostName = "pizero"; 32 24 33 - pi = { 34 - streamName = "pizerocam"; 35 - resolution = { 36 - width = 1280; 37 - height = 720; 38 - }; 39 - framerate = 15; 40 - deviceTreeFilter = "bcm2837-rpi-zero-2-w.dtb"; 41 - deviceTreeCompatible = "brcm,bcm2837"; 42 - gpuMem = 128; 43 - flipCamera = true; 25 + pi = { 26 + streamName = "pizerocam"; 27 + resolution = { 28 + width = 1280; 29 + height = 720; 44 30 }; 45 - 46 - # Use RPi kernel which includes IMX708 camera driver 47 - boot.kernelPackages = pkgs.linuxPackages_rpi3; 48 - 49 - # Disable NixOS DTB so U-Boot uses the firmware's DTB 50 - hardware.deviceTree.enable = lib.mkForce false; 51 - sdImage.populateFirmwareCommands = lib.mkAfter '' 52 - chmod u+w ./firmware/config.txt 53 - echo "dtoverlay=imx708" >> ./firmware/config.txt 54 - echo "camera_auto_detect=1" >> ./firmware/config.txt 55 - ''; 31 + framerate = 15; 32 + gpuMem = 128; 33 + flipCamera = true; 34 + }; 56 35 57 - system.stateVersion = "24.11"; 58 - } 59 - ) 36 + system.stateVersion = "24.11"; 37 + } 60 38 ]; 61 39 }; 62 40 }
+13 -255
modules/pi-camera.nix
··· 3 3 { pkgs, lib, config, ... }: 4 4 let 5 5 cfg = config.pi; 6 - 7 - deviceTree_overlay = _final: prev: { 8 - deviceTree = { 9 - applyOverlays = prev.callPackage ../hosts/pi-common/overlays/apply-overlays-dtmerge.nix { }; 10 - compileDTS = prev.deviceTree.compileDTS; 11 - }; 12 - }; 13 - 14 - libcamera-rpi = pkgs.libcamera.overrideAttrs (old: { 15 - mesonFlags = (old.mesonFlags or [ ]) ++ [ 16 - "-Dipas=rpi/vc4,rpi/pisp" 17 - "-Dpipelines=rpi/vc4,rpi/pisp" 18 - ]; 19 - }); 20 - 21 - rpicam-apps = pkgs.stdenv.mkDerivation rec { 22 - pname = "rpicam-apps"; 23 - version = "1.11.1"; 24 - 25 - src = pkgs.fetchFromGitHub { 26 - owner = "raspberrypi"; 27 - repo = "rpicam-apps"; 28 - rev = "v${version}"; 29 - hash = "sha256-hVoKbvWFeramPkHuibJwUgFOPS9v588+K8828a1fNnA="; 30 - }; 31 - 32 - nativeBuildInputs = with pkgs; [ 33 - meson 34 - ninja 35 - pkg-config 36 - ]; 37 - 38 - buildInputs = [ 39 - libcamera-rpi 40 - pkgs.libdrm 41 - pkgs.libexif 42 - pkgs.libjpeg 43 - pkgs.libpng 44 - pkgs.libtiff 45 - pkgs.boost 46 - pkgs.ffmpeg 47 - ]; 48 - 49 - mesonFlags = [ 50 - "-Denable_libav=enabled" 51 - "-Denable_drm=enabled" 52 - "-Denable_egl=disabled" 53 - "-Denable_qt=disabled" 54 - "-Denable_opencv=disabled" 55 - "-Denable_tflite=disabled" 56 - "-Denable_hailo=disabled" 57 - ]; 58 - 59 - meta = with lib; { 60 - description = "Raspberry Pi camera applications"; 61 - homepage = "https://github.com/raspberrypi/rpicam-apps"; 62 - license = licenses.bsd2; 63 - platforms = [ "aarch64-linux" ]; 64 - }; 65 - }; 66 6 in 67 7 { 68 8 options.pi = { ··· 87 27 default = 30; 88 28 description = "Camera framerate"; 89 29 }; 90 - deviceTreeFilter = lib.mkOption { 91 - type = lib.types.str; 92 - description = "Device tree filter pattern"; 93 - }; 94 - deviceTreeCompatible = lib.mkOption { 95 - type = lib.types.str; 96 - description = "Device tree compatible string"; 97 - }; 98 30 gpuMem = lib.mkOption { 99 31 type = lib.types.int; 100 32 default = 256; 101 33 description = "GPU memory allocation in MB"; 102 34 }; 103 - cmaMem = lib.mkOption { 104 - type = lib.types.int; 105 - default = 256; 106 - description = "CMA (Contiguous Memory Allocator) size in MB for DMA buffers"; 107 - }; 108 35 flipCamera = lib.mkOption { 109 36 type = lib.types.bool; 110 37 default = false; ··· 113 40 }; 114 41 115 42 config = { 116 - # CMA is configured via device tree overlay (cma-256m) 117 - # Do NOT use boot.kernelParams cma= as it bypasses the DT node 118 - # and prevents /dev/dma_heap/linux,cma from being created 119 - 120 43 nix.settings.trusted-users = [ "sean" ]; 121 44 122 45 networking.useDHCP = true; 123 - nixpkgs.overlays = [ deviceTree_overlay ]; 124 - 125 - boot.supportedFilesystems = lib.mkForce [ 126 - "vfat" 127 - "ext4" 128 - ]; 129 - boot.initrd.systemd.enable = false; 130 46 131 47 services.openssh = { 132 48 enable = true; ··· 150 66 151 67 security.sudo.wheelNeedsPassword = false; 152 68 69 + # Firmware config.txt: CMA and GPU memory 70 + hardware.raspberry-pi.config.all = { 71 + options.gpu_mem = { 72 + enable = true; 73 + value = cfg.gpuMem; 74 + }; 75 + dt-overlays.vc4-kms-v3d.params.cma-256.enable = true; 76 + }; 77 + 153 78 services.go2rtc = { 154 79 enable = true; 155 80 settings = { 156 81 ffmpeg.bin = "${pkgs.ffmpeg}/bin/ffmpeg"; 157 82 streams = { 158 - "${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 --autofocus-mode continuous${lib.optionalString cfg.flipCamera " --vflip --hflip"} -o -"; 83 + "${cfg.streamName}" = "exec:${pkgs.rpi.rpicam-apps}/bin/rpicam-vid -t 0 --width ${toString cfg.resolution.width} --height ${toString cfg.resolution.height} --framerate ${toString cfg.framerate} --codec h264 --inline --autofocus-mode continuous${lib.optionalString cfg.flipCamera " --vflip --hflip"} -o -"; 159 84 }; 160 85 }; 161 86 }; ··· 169 94 SupplementaryGroups = [ "video" ]; 170 95 }; 171 96 systemd.services.go2rtc.environment = { 172 - LIBCAMERA_IPA_MODULE_PATH = "${libcamera-rpi}/lib/libcamera/ipa"; 97 + LIBCAMERA_IPA_MODULE_PATH = "${pkgs.rpi.libcamera}/lib/libcamera/ipa"; 173 98 }; 174 99 175 100 environment.systemPackages = [ 176 101 pkgs.ffmpeg 177 - pkgs.libraspberrypi 178 - libcamera-rpi 179 - rpicam-apps 102 + pkgs.rpi.libcamera 103 + pkgs.rpi.rpicam-apps 180 104 pkgs.v4l-utils 181 105 ]; 182 - 183 - hardware.deviceTree.filter = cfg.deviceTreeFilter; 184 - hardware.deviceTree.overlays = [ 185 - { 186 - # Increase CMA from 64MB to 256MB via device tree 187 - # (cmdline cma= bypasses the DT node and /dev/dma_heap/linux,cma isn't created) 188 - name = "cma-256m"; 189 - dtsText = '' 190 - /dts-v1/; 191 - /plugin/; 192 - 193 - / { 194 - compatible = "${cfg.deviceTreeCompatible}"; 195 - 196 - fragment@99 { 197 - target-path = "/reserved-memory/linux,cma"; 198 - __overlay__ { 199 - size = <0x10000000>; /* 256MB */ 200 - }; 201 - }; 202 - }; 203 - ''; 204 - } 205 - { 206 - name = "imx708-overlay"; 207 - dtsText = '' 208 - // SPDX-License-Identifier: GPL-2.0-only 209 - // Definitions for IMX708 camera module on VC I2C bus 210 - /dts-v1/; 211 - /plugin/; 212 - 213 - /{ 214 - compatible = "${cfg.deviceTreeCompatible}"; 215 - 216 - fragment@0 { 217 - target = <&i2c0if>; 218 - __overlay__ { 219 - status = "okay"; 220 - }; 221 - }; 222 - 223 - clk_frag: fragment@1 { 224 - target = <&cam1_clk>; 225 - __overlay__ { 226 - status = "okay"; 227 - clock-frequency = <24000000>; 228 - }; 229 - }; 230 - 231 - fragment@2 { 232 - target = <&i2c0mux>; 233 - __overlay__ { 234 - status = "okay"; 235 - }; 236 - }; 237 - 238 - reg_frag: fragment@3 { 239 - target = <&cam1_reg>; 240 - cam_reg: __overlay__ { 241 - startup-delay-us = <70000>; 242 - off-on-delay-us = <30000>; 243 - regulator-min-microvolt = <2700000>; 244 - regulator-max-microvolt = <2700000>; 245 - }; 246 - }; 247 - 248 - i2c_frag: fragment@100 { 249 - target = <&i2c_csi_dsi>; 250 - __overlay__ { 251 - #address-cells = <1>; 252 - #size-cells = <0>; 253 - status = "okay"; 254 - 255 - cam_node: imx708@1a { 256 - compatible = "sony,imx708"; 257 - reg = <0x1a>; 258 - status = "okay"; 259 - 260 - clocks = <&cam1_clk>; 261 - clock-names = "inclk"; 262 - 263 - vana1-supply = <&cam1_reg>; 264 - vana2-supply = <&cam_dummy_reg>; 265 - vdig-supply = <&cam_dummy_reg>; 266 - vddl-supply = <&cam_dummy_reg>; 267 - 268 - rotation = <180>; 269 - orientation = <2>; 270 - 271 - port { 272 - cam_endpoint: endpoint { 273 - clock-lanes = <0>; 274 - data-lanes = <1 2>; 275 - clock-noncontinuous; 276 - link-frequencies = 277 - /bits/ 64 <450000000>; 278 - }; 279 - }; 280 - }; 281 - 282 - vcm_node: dw9817@c { 283 - compatible = "dongwoon,dw9817-vcm"; 284 - reg = <0x0c>; 285 - status = "okay"; 286 - VDD-supply = <&cam1_reg>; 287 - }; 288 - }; 289 - }; 290 - 291 - csi_frag: fragment@101 { 292 - target = <&csi1>; 293 - csi: __overlay__ { 294 - status = "okay"; 295 - brcm,media-controller; 296 - 297 - port { 298 - csi_ep: endpoint { 299 - remote-endpoint = <&cam_endpoint>; 300 - clock-lanes = <0>; 301 - data-lanes = <1 2>; 302 - clock-noncontinuous; 303 - }; 304 - }; 305 - }; 306 - }; 307 - 308 - __overrides__ { 309 - rotation = <&cam_node>,"rotation:0"; 310 - orientation = <&cam_node>,"orientation:0"; 311 - media-controller = <&csi>,"brcm,media-controller?"; 312 - cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>, 313 - <&csi_frag>, "target:0=",<&csi0>, 314 - <&clk_frag>, "target:0=",<&cam0_clk>, 315 - <&reg_frag>, "target:0=",<&cam0_reg>, 316 - <&cam_node>, "clocks:0=",<&cam0_clk>, 317 - <&cam_node>, "vana1-supply:0=",<&cam0_reg>, 318 - <&vcm_node>, "VDD-supply:0=",<&cam0_reg>; 319 - vcm = <&vcm_node>, "status", 320 - <0>, "=4"; 321 - link-frequency = <&cam_endpoint>,"link-frequencies#0"; 322 - }; 323 - }; 324 - 325 - &cam_endpoint { 326 - remote-endpoint = <&csi_ep>; 327 - }; 328 - ''; 329 - } 330 - ]; 331 - 332 - hardware.enableRedistributableFirmware = true; 333 - 334 - sdImage.populateFirmwareCommands = lib.mkAfter '' 335 - chmod u+w ./firmware/config.txt 336 - cat >> ./firmware/config.txt << EOF 337 - 338 - # Camera support - Pi Camera v3 (IMX708) 339 - dtoverlay=imx708 340 - gpu_mem=${toString cfg.gpuMem} 341 - dtoverlay=cma,cma-${toString cfg.cmaMem} 342 - EOF 343 - 344 - if [ -d ${pkgs.raspberrypifw}/share/raspberrypi/boot/overlays ]; then 345 - cp -r ${pkgs.raspberrypifw}/share/raspberrypi/boot/overlays ./firmware/ 346 - fi 347 - ''; 348 106 349 107 networking.firewall.allowedTCPPorts = [ 350 108 22