alpha
Login
or
Join now
seanaye.bsky.social
/
nixos-config
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
me like nix
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Overview
Issues
Pulls
Pipelines
add pi config for libcamera
author
Sean Aye
date
3 months ago
(Mar 22, 2026, 8:03 PM -0400)
commit
41ca9e0d
41ca9e0d92730b8f574709401a3169969f5ad640
parent
7dfe3acc
7dfe3acc92437c00a822089712f047fe61fc62e7
change-id
qwztuyrz
qwztuyrzrspkkorymntlquxornuwymxo
+378
-10
5 changed files
Expand all
Collapse all
Unified
Split
flake.nix
hosts
pi
configuration.nix
overlays
apply-overlays-dtmerge.nix
packages
libcamera-rpi.nix
rpicam-apps.nix
+10
flake.nix
Reviewed
···
93
93
"${nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64.nix"
94
94
nixos-hardware.nixosModules.raspberry-pi-4
95
95
./hosts/pi/configuration.nix
96
96
+
97
97
+
# Allow missing kernel modules (Pi kernel doesn't have all x86 modules)
98
98
+
# See: https://github.com/NixOS/nixpkgs/issues/154163
99
99
+
({
100
100
+
nixpkgs.overlays = [
101
101
+
(final: super: {
102
102
+
makeModulesClosure = x: super.makeModulesClosure (x // { allowMissing = true; });
103
103
+
})
104
104
+
];
105
105
+
})
96
106
];
97
107
specialArgs = { inherit inputs; };
98
108
};
+248
-10
hosts/pi/configuration.nix
Reviewed
···
1
1
-
{ pkgs, lib, ... }:
1
1
+
{ pkgs, lib, config, ... }:
2
2
+
3
3
+
let
4
4
+
# Workaround from https://github.com/NixOS/nixos-hardware/blob/master/raspberry-pi/4/apply-overlays-dtmerge.nix
5
5
+
deviceTree_overlay = _final: prev: {
6
6
+
deviceTree = {
7
7
+
applyOverlays = prev.callPackage ./overlays/apply-overlays-dtmerge.nix { };
8
8
+
compileDTS = prev.deviceTree.compileDTS;
9
9
+
};
10
10
+
};
11
11
+
12
12
+
# Custom libcamera with Raspberry Pi IPA/pipeline support
13
13
+
libcamera-rpi = pkgs.libcamera.overrideAttrs (old: {
14
14
+
mesonFlags = (old.mesonFlags or [ ]) ++ [
15
15
+
"-Dipas=rpi/vc4,rpi/pisp"
16
16
+
"-Dpipelines=rpi/vc4,rpi/pisp"
17
17
+
];
18
18
+
});
19
19
+
20
20
+
# rpicam-apps using custom libcamera
21
21
+
rpicam-apps = pkgs.stdenv.mkDerivation rec {
22
22
+
pname = "rpicam-apps";
23
23
+
version = "1.11.1";
24
24
+
25
25
+
src = pkgs.fetchFromGitHub {
26
26
+
owner = "raspberrypi";
27
27
+
repo = "rpicam-apps";
28
28
+
rev = "v${version}";
29
29
+
hash = "sha256-hVoKbvWFeramPkHuibJwUgFOPS9v588+K8828a1fNnA=";
30
30
+
};
31
31
+
32
32
+
nativeBuildInputs = with pkgs; [
33
33
+
meson
34
34
+
ninja
35
35
+
pkg-config
36
36
+
];
2
37
38
38
+
buildInputs = [
39
39
+
libcamera-rpi
40
40
+
pkgs.libdrm
41
41
+
pkgs.libexif
42
42
+
pkgs.libjpeg
43
43
+
pkgs.libpng
44
44
+
pkgs.libtiff
45
45
+
pkgs.boost
46
46
+
pkgs.ffmpeg
47
47
+
];
48
48
+
49
49
+
mesonFlags = [
50
50
+
"-Denable_libav=enabled"
51
51
+
"-Denable_drm=enabled"
52
52
+
"-Denable_egl=disabled"
53
53
+
"-Denable_qt=disabled"
54
54
+
"-Denable_opencv=disabled"
55
55
+
"-Denable_tflite=disabled"
56
56
+
"-Denable_hailo=disabled"
57
57
+
];
58
58
+
59
59
+
meta = with lib; {
60
60
+
description = "Raspberry Pi camera applications";
61
61
+
homepage = "https://github.com/raspberrypi/rpicam-apps";
62
62
+
license = licenses.bsd2;
63
63
+
platforms = [ "aarch64-linux" ];
64
64
+
};
65
65
+
};
66
66
+
in
3
67
{
4
68
networking.hostName = "pi";
69
69
+
70
70
+
# Add device tree overlay for dtmerge support
71
71
+
nixpkgs.overlays = [ deviceTree_overlay ];
5
72
6
73
# Disable ZFS which isn't supported on Pi
7
74
boot.supportedFilesystems = lib.mkForce [ "vfat" "ext4" ];
75
75
+
76
76
+
# Pi kernel lacks device-mapper, so use legacy initrd (not systemd)
77
77
+
boot.initrd.systemd.enable = false;
8
78
9
79
# Enable SSH for headless setup
10
80
services.openssh = {
···
27
97
# Allow sudo without password for wheel group
28
98
security.sudo.wheelNeedsPassword = false;
29
99
30
30
-
# Camera support
31
31
-
hardware.raspberry-pi."4" = {
32
32
-
fkms-3d.enable = true;
33
33
-
};
34
34
-
35
100
# go2rtc for camera streaming to Home Assistant
36
101
services.go2rtc = {
37
102
enable = true;
38
103
settings = {
104
104
+
ffmpeg.bin = "${pkgs.ffmpeg}/bin/ffmpeg";
39
105
streams = {
40
40
-
picam = "exec:rpicam-vid -t 0 --inline --width 1920 --height 1080 --framerate 30 -o -";
106
106
+
# Use rpicam-vid for h264 streaming
107
107
+
picam = "exec:${rpicam-apps}/bin/rpicam-vid -t 0 --width 1920 --height 1080 --framerate 30 --codec h264 --inline -o -";
41
108
};
42
109
};
43
110
};
44
111
112
112
+
# udev rule to give video group access to DMA heap devices (required for libcamera)
113
113
+
services.udev.extraRules = ''
114
114
+
SUBSYSTEM=="dma_heap", GROUP="video", MODE="0660"
115
115
+
'';
116
116
+
117
117
+
# Override go2rtc systemd service to run as root for camera access
118
118
+
systemd.services.go2rtc.serviceConfig = {
119
119
+
User = lib.mkForce "root";
120
120
+
};
121
121
+
122
122
+
# Camera and system tools
123
123
+
environment.systemPackages = [
124
124
+
pkgs.ffmpeg
125
125
+
pkgs.libraspberrypi # for vcgencmd
126
126
+
libcamera-rpi # custom libcamera with Pi support
127
127
+
rpicam-apps # rpicam-vid, rpicam-still, etc.
128
128
+
pkgs.v4l-utils # for v4l2-ctl debugging
129
129
+
];
130
130
+
131
131
+
# Device tree configuration for Pi Camera v3 (IMX708)
132
132
+
# Based on https://github.com/raspberrypi/linux/blob/rpi-6.6.y/arch/arm/boot/dts/overlays/imx708-overlay.dts
133
133
+
# and https://github.com/wagdav/homelab/blob/master/modules/camera-rpi-v1/default.nix
134
134
+
hardware.deviceTree.filter = "bcm2711-rpi-4*.dtb";
135
135
+
hardware.deviceTree.overlays = [
136
136
+
{
137
137
+
name = "imx708-overlay";
138
138
+
dtsText = ''
139
139
+
// SPDX-License-Identifier: GPL-2.0-only
140
140
+
// Definitions for IMX708 camera module on VC I2C bus
141
141
+
/dts-v1/;
142
142
+
/plugin/;
143
143
+
144
144
+
/{
145
145
+
compatible = "brcm,bcm2711";
146
146
+
147
147
+
fragment@0 {
148
148
+
target = <&i2c0if>;
149
149
+
__overlay__ {
150
150
+
status = "okay";
151
151
+
};
152
152
+
};
153
153
+
154
154
+
clk_frag: fragment@1 {
155
155
+
target = <&cam1_clk>;
156
156
+
__overlay__ {
157
157
+
status = "okay";
158
158
+
clock-frequency = <24000000>;
159
159
+
};
160
160
+
};
161
161
+
162
162
+
fragment@2 {
163
163
+
target = <&i2c0mux>;
164
164
+
__overlay__ {
165
165
+
status = "okay";
166
166
+
};
167
167
+
};
168
168
+
169
169
+
reg_frag: fragment@3 {
170
170
+
target = <&cam1_reg>;
171
171
+
cam_reg: __overlay__ {
172
172
+
startup-delay-us = <70000>;
173
173
+
off-on-delay-us = <30000>;
174
174
+
regulator-min-microvolt = <2700000>;
175
175
+
regulator-max-microvolt = <2700000>;
176
176
+
};
177
177
+
};
178
178
+
179
179
+
i2c_frag: fragment@100 {
180
180
+
target = <&i2c_csi_dsi>;
181
181
+
__overlay__ {
182
182
+
#address-cells = <1>;
183
183
+
#size-cells = <0>;
184
184
+
status = "okay";
185
185
+
186
186
+
// IMX708 sensor configuration (from imx708.dtsi)
187
187
+
cam_node: imx708@1a {
188
188
+
compatible = "sony,imx708";
189
189
+
reg = <0x1a>;
190
190
+
status = "okay";
191
191
+
192
192
+
clocks = <&cam1_clk>;
193
193
+
clock-names = "inclk";
194
194
+
195
195
+
vana1-supply = <&cam1_reg>;
196
196
+
vana2-supply = <&cam_dummy_reg>;
197
197
+
vdig-supply = <&cam_dummy_reg>;
198
198
+
vddl-supply = <&cam_dummy_reg>;
199
199
+
200
200
+
rotation = <180>;
201
201
+
orientation = <2>;
202
202
+
203
203
+
port {
204
204
+
cam_endpoint: endpoint {
205
205
+
clock-lanes = <0>;
206
206
+
data-lanes = <1 2>;
207
207
+
clock-noncontinuous;
208
208
+
link-frequencies =
209
209
+
/bits/ 64 <450000000>;
210
210
+
};
211
211
+
};
212
212
+
};
213
213
+
214
214
+
// VCM (autofocus motor) configuration
215
215
+
vcm_node: dw9817@c {
216
216
+
compatible = "dongwoon,dw9817-vcm";
217
217
+
reg = <0x0c>;
218
218
+
status = "okay";
219
219
+
VDD-supply = <&cam1_reg>;
220
220
+
};
221
221
+
};
222
222
+
};
223
223
+
224
224
+
csi_frag: fragment@101 {
225
225
+
target = <&csi1>;
226
226
+
csi: __overlay__ {
227
227
+
status = "okay";
228
228
+
brcm,media-controller;
229
229
+
230
230
+
port {
231
231
+
csi_ep: endpoint {
232
232
+
remote-endpoint = <&cam_endpoint>;
233
233
+
clock-lanes = <0>;
234
234
+
data-lanes = <1 2>;
235
235
+
clock-noncontinuous;
236
236
+
};
237
237
+
};
238
238
+
};
239
239
+
};
240
240
+
241
241
+
__overrides__ {
242
242
+
rotation = <&cam_node>,"rotation:0";
243
243
+
orientation = <&cam_node>,"orientation:0";
244
244
+
media-controller = <&csi>,"brcm,media-controller?";
245
245
+
cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
246
246
+
<&csi_frag>, "target:0=",<&csi0>,
247
247
+
<&clk_frag>, "target:0=",<&cam0_clk>,
248
248
+
<®_frag>, "target:0=",<&cam0_reg>,
249
249
+
<&cam_node>, "clocks:0=",<&cam0_clk>,
250
250
+
<&cam_node>, "vana1-supply:0=",<&cam0_reg>,
251
251
+
<&vcm_node>, "VDD-supply:0=",<&cam0_reg>;
252
252
+
vcm = <&vcm_node>, "status",
253
253
+
<0>, "=4";
254
254
+
link-frequency = <&cam_endpoint>,"link-frequencies#0";
255
255
+
};
256
256
+
};
257
257
+
258
258
+
&cam_endpoint {
259
259
+
remote-endpoint = <&csi_ep>;
260
260
+
};
261
261
+
'';
262
262
+
}
263
263
+
];
264
264
+
265
265
+
# Raspberry Pi firmware for camera
266
266
+
hardware.enableRedistributableFirmware = true;
267
267
+
268
268
+
# Add camera config and overlays to firmware partition
269
269
+
sdImage.populateFirmwareCommands = lib.mkAfter ''
270
270
+
chmod u+w ./firmware/config.txt
271
271
+
cat >> ./firmware/config.txt << EOF
272
272
+
273
273
+
# Camera support - Pi Camera v3 (IMX708)
274
274
+
gpu_mem=256
275
275
+
EOF
276
276
+
277
277
+
# Copy device tree overlays for camera auto-detect
278
278
+
if [ -d ${pkgs.raspberrypifw}/share/raspberrypi/boot/overlays ]; then
279
279
+
cp -r ${pkgs.raspberrypifw}/share/raspberrypi/boot/overlays ./firmware/
280
280
+
fi
281
281
+
'';
282
282
+
45
283
# Networking
46
284
networking.networkmanager.enable = true;
47
285
48
286
# Firewall
49
287
networking.firewall.allowedTCPPorts = [
50
50
-
22 # SSH
51
51
-
1984 # go2rtc API
52
52
-
8554 # RTSP
288
288
+
22 # SSH
289
289
+
1984 # go2rtc API
290
290
+
8554 # RTSP
53
291
];
54
292
55
293
system.stateVersion = "24.11";
+50
hosts/pi/overlays/apply-overlays-dtmerge.nix
Reviewed
···
1
1
+
# modification of nixpkgs deviceTree.applyOverlays to resolve https://github.com/NixOS/nixpkgs/issues/125354
2
2
+
# derived from https://github.com/NixOS/nixpkgs/blob/916ca8f2b0c208def051f8ea9760c534a40309db/pkgs/os-specific/linux/device-tree/default.nix
3
3
+
{ lib, stdenvNoCC, dtc, libraspberrypi }:
4
4
+
5
5
+
with lib; (base: overlays': stdenvNoCC.mkDerivation {
6
6
+
name = "device-tree-overlays";
7
7
+
nativeBuildInputs = [ dtc libraspberrypi ];
8
8
+
buildCommand =
9
9
+
let
10
10
+
overlays = toList overlays';
11
11
+
in
12
12
+
''
13
13
+
mkdir -p $out
14
14
+
cd "${base}"
15
15
+
find . -type f -name '*.dtb' -print0 \
16
16
+
| xargs -0 cp -v --no-preserve=mode --target-directory "$out" --parents
17
17
+
18
18
+
for dtb in $(find "$out" -type f -name '*.dtb'); do
19
19
+
dtbCompat=$(fdtget -t s "$dtb" / compatible 2>/dev/null || true)
20
20
+
# skip files without `compatible` string
21
21
+
test -z "$dtbCompat" && continue
22
22
+
23
23
+
${flip (concatMapStringsSep "\n") overlays (o: ''
24
24
+
overlayCompat="$(fdtget -t s "${o.dtboFile}" / compatible)"
25
25
+
26
26
+
# skip incompatible and non-matching overlays
27
27
+
if [[ ! "$dtbCompat" =~ "$overlayCompat" ]]; then
28
28
+
echo "Skipping overlay ${o.name}: incompatible with $(basename "$dtb")"
29
29
+
elif ${if ((o.filter or null) == null) then "false" else ''
30
30
+
[[ "''${dtb//${o.filter}/}" == "$dtb" ]]
31
31
+
''}
32
32
+
then
33
33
+
echo "Skipping overlay ${o.name}: filter does not match $(basename "$dtb")"
34
34
+
else
35
35
+
echo -n "Applying overlay ${o.name} to $(basename "$dtb")... "
36
36
+
mv "$dtb"{,.in}
37
37
+
38
38
+
# dtmerge requires a .dtbo ext for dtbo files, otherwise it adds it to the given file implicitly
39
39
+
dtboWithExt="$TMPDIR/$(basename "${o.dtboFile}").dtbo"
40
40
+
cp -r ${o.dtboFile} "$dtboWithExt"
41
41
+
42
42
+
dtmerge "$dtb.in" "$dtb" "$dtboWithExt"
43
43
+
44
44
+
echo "ok"
45
45
+
rm "$dtb.in" "$dtboWithExt"
46
46
+
fi
47
47
+
'')}
48
48
+
49
49
+
done'';
50
50
+
})
+9
packages/libcamera-rpi.nix
Reviewed
···
1
1
+
# Override libcamera to include Raspberry Pi IPA and pipeline support
2
2
+
{ pkgs }:
3
3
+
4
4
+
pkgs.libcamera.overrideAttrs (old: {
5
5
+
mesonFlags = old.mesonFlags ++ [
6
6
+
"-Dipas=rpi/vc4,rpi/pisp"
7
7
+
"-Dpipelines=rpi/vc4,rpi/pisp"
8
8
+
];
9
9
+
})
+61
packages/rpicam-apps.nix
Reviewed
···
1
1
+
{
2
2
+
lib,
3
3
+
stdenv,
4
4
+
fetchFromGitHub,
5
5
+
meson,
6
6
+
ninja,
7
7
+
pkg-config,
8
8
+
libcamera-rpi, # Custom libcamera with Pi support
9
9
+
libdrm,
10
10
+
libexif,
11
11
+
libjpeg,
12
12
+
libpng,
13
13
+
libtiff,
14
14
+
boost,
15
15
+
ffmpeg,
16
16
+
}:
17
17
+
18
18
+
stdenv.mkDerivation rec {
19
19
+
pname = "rpicam-apps";
20
20
+
version = "1.5.3";
21
21
+
22
22
+
src = fetchFromGitHub {
23
23
+
owner = "raspberrypi";
24
24
+
repo = "rpicam-apps";
25
25
+
rev = "v${version}";
26
26
+
hash = "sha256-0000000000000000000000000000000000000000000="; # Placeholder - will be updated on first build
27
27
+
};
28
28
+
29
29
+
nativeBuildInputs = [
30
30
+
meson
31
31
+
ninja
32
32
+
pkg-config
33
33
+
];
34
34
+
35
35
+
buildInputs = [
36
36
+
libcamera-rpi
37
37
+
libdrm
38
38
+
libexif
39
39
+
libjpeg
40
40
+
libpng
41
41
+
libtiff
42
42
+
boost
43
43
+
ffmpeg
44
44
+
];
45
45
+
46
46
+
mesonFlags = [
47
47
+
"-Denable_libav=enabled"
48
48
+
"-Denable_drm=enabled"
49
49
+
"-Denable_egl=disabled"
50
50
+
"-Denable_qt=disabled"
51
51
+
"-Denable_opencv=disabled"
52
52
+
"-Denable_tflite=disabled"
53
53
+
];
54
54
+
55
55
+
meta = with lib; {
56
56
+
description = "Raspberry Pi camera applications";
57
57
+
homepage = "https://github.com/raspberrypi/rpicam-apps";
58
58
+
license = licenses.bsd2;
59
59
+
platforms = [ "aarch64-linux" ];
60
60
+
};
61
61
+
}