NixOS on ARM/Raspberry Pi 4: Difference between revisions

imported>N0nameuser
update hardware audio block
Mmxgn (talk | contribs)
m remove extra whitespace
 
(35 intermediate revisions by 15 users not shown)
Line 27: Line 27:
|}
|}
</div>
</div>
The Raspberry Pi family of devices is a series of single-board computers made by the Raspberry Pi Foundation. They are all based on Broadcom System-on-a-chip (SoCs).  
The Raspberry Pi family of devices is a series of single-board computers made by the Raspberry Pi Foundation. They are all based on Broadcom System-on-a-chip (SoCs).


== Status ==
== Status ==
Line 47: Line 47:
Using <code>nixos-generate-config</code> will generate the required minimal configuration.
Using <code>nixos-generate-config</code> will generate the required minimal configuration.


For better GPU Support and some deviceTree quirks add the nixos-hardware channel:
Raspberry Pi 4 is well-supported on modern kernels. However, if you encounter issues with GPU support or other deviceTree quirks, you may wish to add the nixos-hardware channel:
<code>nix-channel --add https://github.com/NixOS/nixos-hardware/archive/master.tar.gz nixos-hardware</code>
 
<code>
nix-channel --add https://github.com/NixOS/nixos-hardware/archive/master.tar.gz nixos-hardware
 
nix-channel --update
</code>


{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
Line 55: Line 60:
   imports =
   imports =
     [
     [
       <nixos-hardware/raspberry-pi/4>
       </nowiki><<nowiki>nixos-hardware/raspberry-pi/4</nowiki>><nowiki>
       ./hardware-configuration.nix
       ./hardware-configuration.nix
     ];
     ];
Line 70: Line 75:
     raspberrypi-eeprom
     raspberrypi-eeprom
   ];
   ];
   system.stateVersion = "23.11";
   system.stateVersion = "24.11";
}
}
</nowiki>}}
</nowiki>}}
=== <code>config.txt</code> ===
{{warning|Since 24.11, the option <code>boot.loader.raspberrypi</code> which included <code>firmwareConfig</code> is removed from <code>nixpkgs</code>, therefore changes have to be written to <code>config.txt</code> directly<ref>https://github.com/NixOS/nixpkgs/pull/241534</ref>}}
To edit options only available through <code>config.txt</code>, as of May 12, 2025, you can only do so non-declaratively:
{{commands|<nowiki>
$ sudo mount /dev/disk/by-label/FIRMWARE /mnt
$ sudo vim /mnt/config.txt # <-- make changes here
</nowiki>}}
For example, [https://www.raspberrypi-spy.co.uk/2020/11/overclocking-the-raspberry-pi-400/ overclocking] the Raspberry Pi 400 can be done by adding the following:
{{file|config.txt|text|<nowiki>
arm_freq=2000
over_voltage=6
</nowiki>}}
=== USB boot ===
=== USB boot ===


Line 82: Line 104:
</nowiki>}}
</nowiki>}}


Now reboot the device so it can update the firmware from boot partition.
Now reboot the device so it can update the firmware from the boot partition.


=== GPU support ===
=== GPU support ===
Line 94: Line 116:
     enable = true;
     enable = true;
     displayManager.lightdm.enable = true;
     displayManager.lightdm.enable = true;
     desktopManager.gnome3.enable = true;
     desktopManager.gnome.enable = true;
     videoDrivers = [ "fbdev" ];
     videoDrivers = [ "fbdev" ];
   };
   };
Line 101: Line 123:
==== With GPU ====
==== With GPU ====


In [https://github.com/NixOS/nixos-hardware/pull/261 nixos-hardware#261] a new option has been added to use the <code>fkms-3d</code> overlay. This will only work with the vendor kernel.
In [https://github.com/NixOS/nixos-hardware/pull/261 nixos-hardware#261] an option has been added to use the <code>fkms-3d</code> ([https://wiki.archlinux.org/title/Kernel_mode_setting modesetting]) overlay which uses the [https://www.raspberrypi.com/news/vc4-and-v3d-opengl-drivers-for-raspberry-pi-an-update/ V3D renderer]. This will only work with the vendor kernel, which is the default in NixOS.


{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
Line 116: Line 138:
     enable = true;
     enable = true;
     displayManager.lightdm.enable = true;
     displayManager.lightdm.enable = true;
     desktopManager.gnome3.enable = true;
     desktopManager.gnome.enable = true;
   };
   };
}
}
</nowiki>}}
</nowiki>}}
==== Tools ====
 
=== Tools ===


The raspberry tools are available in the <code>libraspberrypi</code> package and include commands like <code>vcgencmd</code> to measure temperature and CPU frequency.
The raspberry tools are available in the <code>libraspberrypi</code> package and include commands like <code>vcgencmd</code> to measure temperature and CPU frequency.


==== Audio ====
=== Audio ===


In addition to the usual config, you will need to enable hardware audio support:
In addition to the usual config, you will need to enable hardware audio support:


{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
   sound.enable = true;
{
  hardware.pulseaudio.enable = true;
  # Enable audio devices
  hardware.raspberry-pi."4".audio = true;
   boot.kernelParams = [ "snd_bcm2835.enable_hdmi=1" "snd_bcm2835.enable_headphones=1" ];
}
</nowiki>}}
{{file|config.txt|txt|<nowiki>
dtparam=audio=on
</nowiki>}}
</nowiki>}}


=== Using GPIO pins as non root ===
If you're running headless, you can also disable HDMI audio and force use of the headphones jack by adding <code>hdmi_ignore_edid_audio=1</code> on a line below <code>dtparam=audio=on</code>.


By default, the GPIO pins are enable, but can only be accessed by the root user.
=== Networking ===
This can be address by adding a [https://wiki.archlinux.org/title/Udev udev] rule to your configuration that changes the owner ship of <code>/dev/gpiomem</code> and the other required devices.


The following code adds a group <code>gpio</code> and adds the user <code>mygpiouser</code> to that group.
Ethernet and wifi interfaces should work out of the box. In addition to normal network configuration, consider disabling wifi powersaving if you experience slowness or issues with the host becoming unreachable on the network shortly after boot. For NetworkManager, the following configuration is sufficient:
You probably want to put your own user name here.


The <code>extraRules</code> change the owner of <code>gpiomem</code> and all other files needed for GPIO to work to <code>root:gpio</code> and changes the permissions to <code>0660</code>.
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{
  # Basic networking
  networking.networkmanager.enable = true;
  # Prevent host becoming unreachable on wifi after some time.
  networking.networkmanager.wifi.powersave = false;
}
</nowiki>}}
 
=== Using GPIO pins as non-root ===
 
By default, the GPIO pins are enabled, but can only be accessed by the root user.
This can be addressed by adding a [https://wiki.archlinux.org/title/Udev udev] rule to your configuration that changes the ownership of <code>/dev/gpiomem</code> and the other required devices.
 
The following code adds a group <code>gpio</code> and adds the user <code>mygpiouser</code> to that group. You probably want to put your own user name here.
 
The <code>extraRules</code> changes the owner of <code>gpiomem</code> and all other files needed for GPIO to work to <code>root:gpio</code> and changes the permissions to <code>0660</code>.
Therefore, the root user and anyone in the gpio group can now access the GPIO pins.
Therefore, the root user and anyone in the gpio group can now access the GPIO pins.


Line 152: Line 193:
   services.udev.extraRules = ''
   services.udev.extraRules = ''
     SUBSYSTEM=="bcm2835-gpiomem", KERNEL=="gpiomem", GROUP="gpio",MODE="0660"
     SUBSYSTEM=="bcm2835-gpiomem", KERNEL=="gpiomem", GROUP="gpio",MODE="0660"
     SUBSYSTEM=="gpio", KERNEL=="gpiochip*", ACTION=="add", RUN+="${pkgs.bash}/bin/bash -c 'chown root:gpio /sys/class/gpio/export /sys/class/gpio/unexport ; chmod 220 /sys/class/gpio/export /sys/class/gpio/unexport'"
     SUBSYSTEM=="gpio", KERNEL=="gpiochip*", ACTION=="add", RUN+="${pkgs.bash}/bin/bash -c 'chown root:gpio /sys/class/gpio/export /sys/class/gpio/unexport ; chmod 220 /sys/class/gpio/export /sys/class/gpio/unexport'"
     SUBSYSTEM=="gpio", KERNEL=="gpio*", ACTION=="add",RUN+="${pkgs.bash}/bin/bash -c 'chown root:gpio /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value ; chmod 660 /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value'"
     SUBSYSTEM=="gpio", KERNEL=="gpio*", ACTION=="add",RUN+="${pkgs.bash}/bin/bash -c 'chown root:gpio /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value ; chmod 660 /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value'"
   '';
   '';
Line 168: Line 209:


To enable the SPI, you would normally add <code>dtparam=spi=on</code> to <code>/boot/config.txt</code>.
To enable the SPI, you would normally add <code>dtparam=spi=on</code> to <code>/boot/config.txt</code>.
This is not possbible on NixOS, and instead you have to apply a device tree overlay.
This is not possible on NixOS, and instead you have to apply a device tree overlay.
For this we use the <code>hardware.deviceTree.overlays</code> option.
For this we use the <code>hardware.deviceTree.overlays</code> option.
After applying the overlay, we add an <code>spi</code> group and change the owner of the <code>spidev</code> device to it, similarly to [[#Using GPIO pins as non root |GPIO]].
After applying the overlay, we add an <code>spi</code> group and change the owner of the <code>spidev</code> device to it, similarly to [[#Using GPIO pins as non root |GPIO]].


<syntaxHighlight lang="nix">
<syntaxhighlight lang="nix">
hardware.raspberry-pi."4".apply-overlays-dtmerge.enable = true;
hardware.raspberry-pi."4".apply-overlays-dtmerge.enable = true;
hardware.deviceTree = {
hardware.deviceTree = {
Line 180: Line 221:
     {
     {
       name = "spi";
       name = "spi";
       dtsoFile = ./spi0-0cd.dtso;
       dtboFile = ./spi0-0cs.dtbo;
     }
     }
   ];
   ];
Line 190: Line 231:
   SUBSYSTEM=="spidev", KERNEL=="spidev0.0", GROUP="spi", MODE="0660"
   SUBSYSTEM=="spidev", KERNEL=="spidev0.0", GROUP="spi", MODE="0660"
'';
'';
</syntaxHighlight>
</syntaxhighlight>


The the <code>spi0-0cd.dtso</code> file can be downlaoded [https://github.com/raspberrypi/firmware/blob/master/boot/overlays/spi0-0cs.dtbo here].
The the <code>spi0-0cd.dtso</code> file can be downloaded [https://github.com/raspberrypi/firmware/blob/master/boot/overlays/spi0-0cs.dtbo here].
You might have to change the <code>compatible</code> field to "raspberrypi" in the dtbo file.
You might have to change the <code>compatible</code> field to "raspberrypi" in the dtbo file.


Line 220: Line 261:
   services.udev.extraRules = ''
   services.udev.extraRules = ''
     # allow access to raspi cec device for video group (and optionally register it as a systemd device, used below)
     # allow access to raspi cec device for video group (and optionally register it as a systemd device, used below)
     SUBSYSTEM=="vchiq", GROUP="video", MODE="0660", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/dev/vchiq"
     KERNEL=="vchiq", GROUP="video", MODE="0660", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/dev/vchiq"
   '';
   '';


   # optional: attach a persisted cec-client to `/run/cec.fifo`, to avoid the CEC ~1s startup delay per command
   # optional: attach a persisted cec-client to `/run/cec.fifo`, to avoid the CEC ~1s startup delay per command
   # scan for devices: `echo 'scan' > /run/cec.fifo ; journalctl -u cec-client.service`
   # scan for devices: `echo 'scan' </nowiki>><nowiki> /run/cec.fifo ; journalctl -u cec-client.service`
   # set pi as active source: `echo 'as' > /run/cec.fifo`
   # set pi as active source: `echo 'as' </nowiki>><nowiki> /run/cec.fifo`
   systemd.sockets."cec-client" = {
   systemd.sockets."cec-client" = {
     after = [ "dev-vchiq.device" ];
     after = [ "dev-vchiq.device" ];
Line 242: Line 283:
     serviceConfig = {
     serviceConfig = {
       ExecStart = ''${pkgs.libcec}/bin/cec-client -d 1'';
       ExecStart = ''${pkgs.libcec}/bin/cec-client -d 1'';
       ExecStop = ''/bin/sh -c "echo q > /run/cec.fifo"'';
       ExecStop = ''/bin/sh -c "echo q </nowiki>><nowiki> /run/cec.fifo"'';
       StandardInput = "socket";
       StandardInput = "socket";
       StandardOutput = "journal";
       StandardOutput = "journal";
Line 249: Line 290:
}
}
</nowiki>}}
</nowiki>}}
=== Enabling Bluetooth ===
One might get bluetooth to work with this in the configuration file:
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
  systemd.services.btattach = {
    before = [ "bluetooth.service" ];
    after = [ "dev-ttyAMA0.device" ];
    wantedBy = [ "multi-user.target" ];
    serviceConfig = {
      ExecStart = "${pkgs.bluez}/bin/btattach -B /dev/ttyAMA0 -P bcm -S 3000000";
    };
  };
</nowiki>}}
== Customizing & Generating SD image without installation step ==
There's a nix-community project to support fine-grained kernel & config.txt, and generate the image directly:
[https://github.com/nix-community/raspberry-pi-nix/ nix-community/raspberry-pi-nix]


== Notes about the boot process ==
== Notes about the boot process ==
Line 266: Line 326:
=== Audio not playing and Bluetooth: no controller available ===
=== Audio not playing and Bluetooth: no controller available ===


On the Raspberry Pi kernel, the jack may never play audio, and no Bluetooth devices may ever be found. To get this to work, it is recommended to switch to the mainline kernel. See [https://github.com/NixOS/nixpkgs/issues/123725 nixpkgs#123725] for more info.
On the Raspberry Pi kernel, the jack may never play audio, and no Bluetooth devices may ever be found. To get this to work, it is recommended to switch to the mainline kernel. See [https://github.com/NixOS/nixpkgs/issues/123725 nixpkgs#123725] for more info.


<hr />
=== Touch screen not working ===
You have to declare this in your <code>configuration.nix</code><ref>https://discourse.nixos.org/t/cant-get-nixos-x-to-work-on-a-raspberry-pi-with-dsi-display/44532/3</ref>:<syntaxhighlight lang="nix">
hardware.raspberry-pi."4" = {
  touch-ft5406.enable = true;
};
</syntaxhighlight>