me like nix
1{ pkgs, lib, config, ... }:
2
3let
4 # Workaround from https://github.com/NixOS/nixos-hardware/blob/master/raspberry-pi/4/apply-overlays-dtmerge.nix
5 deviceTree_overlay = _final: prev: {
6 deviceTree = {
7 applyOverlays = prev.callPackage ./overlays/apply-overlays-dtmerge.nix { };
8 compileDTS = prev.deviceTree.compileDTS;
9 };
10 };
11
12 # Custom libcamera with Raspberry Pi IPA/pipeline support
13 libcamera-rpi = pkgs.libcamera.overrideAttrs (old: {
14 mesonFlags = (old.mesonFlags or [ ]) ++ [
15 "-Dipas=rpi/vc4,rpi/pisp"
16 "-Dpipelines=rpi/vc4,rpi/pisp"
17 ];
18 });
19
20 # rpicam-apps using custom libcamera
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 };
66in
67{
68 networking.hostName = "pi";
69
70 # Add device tree overlay for dtmerge support
71 nixpkgs.overlays = [ deviceTree_overlay ];
72
73 # Disable ZFS which isn't supported on Pi
74 boot.supportedFilesystems = lib.mkForce [ "vfat" "ext4" ];
75
76 # Pi kernel lacks device-mapper, so use legacy initrd (not systemd)
77 boot.initrd.systemd.enable = false;
78
79 # Enable SSH for headless setup
80 services.openssh = {
81 enable = true;
82 settings = {
83 PasswordAuthentication = false;
84 PermitRootLogin = "no";
85 };
86 };
87
88 # User config
89 users.users.sean = {
90 isNormalUser = true;
91 extraGroups = [ "wheel" "video" ];
92 openssh.authorizedKeys.keys = [
93 "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDCIqgZ7kedxo+mOW7YG73Vp3zel3h180y3GKvHtRsXfGlpIIvRDy7pgCBQ4AGXYD4y78URQmFohYSAPqCPOPaWcU2un3XG9KvCzEsHmsbskPonitUmCiKvrKkb6oW4jCBtd7AEtBn+AiajAQFtPZ7NN2Df3AmTypvR6Irg7R+nxnfc9NTIHmGvxSDyWcbb4pguL20sctUSqGL6xGh8q/bqhdOThSimM+z9bEUNxK/5rPhwkNniMrp4pJcUrUiAh5/4DiRFG6KT+oeg+/myoz/Z1sPvAs7u/8JDQI4RshRD8Hu0oTkRBN6Hxj478q2SXbeBUZlD6IdjP3RhGpmSecoDdtWqKbpuV3eVRtQtba3KL86GBeV/bugaOdJ1Aud+1SOFJreAAuvxzMMKT+cdQZk6oOPP148DA/No+mDm/2S43lcdCXh79wA6YRAmKQ8jmZxTCtPutrvuZK1rguvvUlEoG/vhdNHh7eDa4Td07V6bjCRPUl8qk/e4M0E3pwsTlZc="
94 ];
95 };
96
97 # Allow sudo without password for wheel group
98 security.sudo.wheelNeedsPassword = false;
99
100 # go2rtc for camera streaming to Home Assistant
101 services.go2rtc = {
102 enable = true;
103 settings = {
104 ffmpeg.bin = "${pkgs.ffmpeg}/bin/ffmpeg";
105 streams = {
106 # Use rpicam-vid for h264 streaming
107 picam = "exec:${rpicam-apps}/bin/rpicam-vid -t 0 --width 1920 --height 1080 --framerate 30 --codec h264 --inline -o -";
108 };
109 };
110 };
111
112 # udev rule to give video group access to DMA heap devices (required for libcamera)
113 services.udev.extraRules = ''
114 SUBSYSTEM=="dma_heap", GROUP="video", MODE="0660"
115 '';
116
117 # Override go2rtc systemd service to run as root for camera access
118 systemd.services.go2rtc.serviceConfig = {
119 User = lib.mkForce "root";
120 };
121
122 # Camera and system tools
123 environment.systemPackages = [
124 pkgs.ffmpeg
125 pkgs.libraspberrypi # for vcgencmd
126 libcamera-rpi # custom libcamera with Pi support
127 rpicam-apps # rpicam-vid, rpicam-still, etc.
128 pkgs.v4l-utils # for v4l2-ctl debugging
129 ];
130
131 # Device tree configuration for Pi Camera v3 (IMX708)
132 # Based on https://github.com/raspberrypi/linux/blob/rpi-6.6.y/arch/arm/boot/dts/overlays/imx708-overlay.dts
133 # and https://github.com/wagdav/homelab/blob/master/modules/camera-rpi-v1/default.nix
134 hardware.deviceTree.filter = "bcm2711-rpi-4*.dtb";
135 hardware.deviceTree.overlays = [
136 {
137 name = "imx708-overlay";
138 dtsText = ''
139 // SPDX-License-Identifier: GPL-2.0-only
140 // Definitions for IMX708 camera module on VC I2C bus
141 /dts-v1/;
142 /plugin/;
143
144 /{
145 compatible = "brcm,bcm2711";
146
147 fragment@0 {
148 target = <&i2c0if>;
149 __overlay__ {
150 status = "okay";
151 };
152 };
153
154 clk_frag: fragment@1 {
155 target = <&cam1_clk>;
156 __overlay__ {
157 status = "okay";
158 clock-frequency = <24000000>;
159 };
160 };
161
162 fragment@2 {
163 target = <&i2c0mux>;
164 __overlay__ {
165 status = "okay";
166 };
167 };
168
169 reg_frag: fragment@3 {
170 target = <&cam1_reg>;
171 cam_reg: __overlay__ {
172 startup-delay-us = <70000>;
173 off-on-delay-us = <30000>;
174 regulator-min-microvolt = <2700000>;
175 regulator-max-microvolt = <2700000>;
176 };
177 };
178
179 i2c_frag: fragment@100 {
180 target = <&i2c_csi_dsi>;
181 __overlay__ {
182 #address-cells = <1>;
183 #size-cells = <0>;
184 status = "okay";
185
186 // IMX708 sensor configuration (from imx708.dtsi)
187 cam_node: imx708@1a {
188 compatible = "sony,imx708";
189 reg = <0x1a>;
190 status = "okay";
191
192 clocks = <&cam1_clk>;
193 clock-names = "inclk";
194
195 vana1-supply = <&cam1_reg>;
196 vana2-supply = <&cam_dummy_reg>;
197 vdig-supply = <&cam_dummy_reg>;
198 vddl-supply = <&cam_dummy_reg>;
199
200 rotation = <180>;
201 orientation = <2>;
202
203 port {
204 cam_endpoint: endpoint {
205 clock-lanes = <0>;
206 data-lanes = <1 2>;
207 clock-noncontinuous;
208 link-frequencies =
209 /bits/ 64 <450000000>;
210 };
211 };
212 };
213
214 // VCM (autofocus motor) configuration
215 vcm_node: dw9817@c {
216 compatible = "dongwoon,dw9817-vcm";
217 reg = <0x0c>;
218 status = "okay";
219 VDD-supply = <&cam1_reg>;
220 };
221 };
222 };
223
224 csi_frag: fragment@101 {
225 target = <&csi1>;
226 csi: __overlay__ {
227 status = "okay";
228 brcm,media-controller;
229
230 port {
231 csi_ep: endpoint {
232 remote-endpoint = <&cam_endpoint>;
233 clock-lanes = <0>;
234 data-lanes = <1 2>;
235 clock-noncontinuous;
236 };
237 };
238 };
239 };
240
241 __overrides__ {
242 rotation = <&cam_node>,"rotation:0";
243 orientation = <&cam_node>,"orientation:0";
244 media-controller = <&csi>,"brcm,media-controller?";
245 cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
246 <&csi_frag>, "target:0=",<&csi0>,
247 <&clk_frag>, "target:0=",<&cam0_clk>,
248 <®_frag>, "target:0=",<&cam0_reg>,
249 <&cam_node>, "clocks:0=",<&cam0_clk>,
250 <&cam_node>, "vana1-supply:0=",<&cam0_reg>,
251 <&vcm_node>, "VDD-supply:0=",<&cam0_reg>;
252 vcm = <&vcm_node>, "status",
253 <0>, "=4";
254 link-frequency = <&cam_endpoint>,"link-frequencies#0";
255 };
256 };
257
258 &cam_endpoint {
259 remote-endpoint = <&csi_ep>;
260 };
261 '';
262 }
263 ];
264
265 # Raspberry Pi firmware for camera
266 hardware.enableRedistributableFirmware = true;
267
268 # Add camera config and overlays to firmware partition
269 sdImage.populateFirmwareCommands = lib.mkAfter ''
270 chmod u+w ./firmware/config.txt
271 cat >> ./firmware/config.txt << EOF
272
273# Camera support - Pi Camera v3 (IMX708)
274gpu_mem=256
275EOF
276
277 # Copy device tree overlays for camera auto-detect
278 if [ -d ${pkgs.raspberrypifw}/share/raspberrypi/boot/overlays ]; then
279 cp -r ${pkgs.raspberrypifw}/share/raspberrypi/boot/overlays ./firmware/
280 fi
281 '';
282
283 # Networking
284 networking.networkmanager.enable = true;
285
286 # Firewall
287 networking.firewall.allowedTCPPorts = [
288 22 # SSH
289 1984 # go2rtc API
290 8554 # RTSP
291 ];
292
293 system.stateVersion = "24.11";
294}