Jump to content

NixOS on ARM/Raspberry Pi 4: Difference between revisions

Added instructions for getting bluetooth atleast usable
imported>Fstuess
No edit summary
(Added instructions for getting bluetooth atleast usable)
 
(33 intermediate revisions by 21 users not shown)
Line 4: Line 4:
!colspan="2" class="title"|Raspberry Pi 4 Family
!colspan="2" class="title"|Raspberry Pi 4 Family
|-
|-
|colspan="2"|(Image not available)
|colspan="2"|[[File:Raspberry_Pi_4,_2_GB_RAM_version_4.jpg|frameless|256px|A Raspberry Pi 4.]]
|-
|-
!Manufacturer
!Manufacturer
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 37: Line 37:
First follow the [[NixOS_on_ARM#Installation|generic installation steps]] to get the installer image and install using the [[NixOS_on_ARM#NixOS_installation_.26_configuration|installation and configuration steps]].
First follow the [[NixOS_on_ARM#Installation|generic installation steps]] to get the installer image and install using the [[NixOS_on_ARM#NixOS_installation_.26_configuration|installation and configuration steps]].


The Raspberry Pi 4B should work with either the [https://hydra.nixos.org/job/nixos/trunk-combined/nixos.sd_image.aarch64-linux generic SD image] or [https://hydra.nixos.org/job/nixos/trunk-combined/nixos.sd_image_new_kernel.aarch64-linux the new kernel variant], starting with 21.05 (or unstable). As long as a kernel 5.10 or newer is in use.
The Raspberry Pi 4B works with the [https://hydra.nixos.org/job/nixos/trunk-combined/nixos.sd_image.aarch64-linux generic SD image].


Please note: to uncompress the .zstd, one may use the <code>unzstd</code> command (equivalent to <code>zstd -d</code>) on supported machines. The zstd commands can be accessed from the <code>zstd</code> package.
Sample instructions for [https://nix.dev/tutorials/installing-nixos-on-a-raspberry-pi installing NixOS on a Raspberry Pi] are available at nix.dev.


Sample instructions for [https://nix.dev/tutorials/installing-nixos-on-a-raspberry-pi installing NixOS on a Raspberry Pi] are available at nix.dev.
{{warning| Note that the Raspberry Pi 4 has two HDMI outputs, and apparently sometimes the user prompt for the console/TTY is displayed on HDMI 1 while the boot process is displayed on HDMI 0 (this may even [https://raspberrypi.stackexchange.com/a/112071/149250 be the case] with the official (non NixOs) non-graphical lite image). So if after the message "Welcome on NixOs" at the end of phase 2 your screen goes black/disconnects, try to use the other HDMI port. See the related bug [https://github.com/NixOS/nixpkgs/issues/179701 here].}}


=== Configuration ===
=== Configuration ===


{{outdated|These instructions were written when the generic image did not work. Using the vendor kernel may be desirable under some conditions<sup>[which?]</sup>}}
Using <code>nixos-generate-config</code> will generate the required minimal configuration.


Using <code>nixos-generate-config</code> will not generate the required minimal configuration.
For better GPU support and some deviceTree quirks add the nixos-hardware channel:


Remember to add the nixos-unstable channel.
<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>
{ config, pkgs, lib, ... }:
{ config, pkgs, lib, ... }:
{
{
# This configuration worked on 09-03-2021 nixos-unstable @ commit 102eb68ceec
  imports =
# The image used https://hydra.nixos.org/build/134720986
    [
 
      </nowiki><<nowiki>nixos-hardware/raspberry-pi/4</nowiki>><nowiki>
  boot = {
      ./hardware-configuration.nix
    kernelPackages = pkgs.linuxPackages_rpi4;
    tmpOnTmpfs = true;
    initrd.availableKernelModules = [ "usbhid" "usb_storage" ];
    # ttyAMA0 is the serial console broken out to the GPIO
    kernelParams = [
        "8250.nr_uarts=1"
        "console=ttyAMA0,115200"
        "console=tty1"
        # Some gui programs need this
        "cma=128M"
     ];
     ];
   };
   hardware = {
 
     raspberry-pi."4".apply-overlays-dtmerge.enable = true;
  boot.loader.raspberryPi = {
     deviceTree = {
     enable = true;
    version = 4;
  };
  boot.loader.grub.enable = false;
  boot.loader.generic-extlinux-compatible.enable = true;
 
  # Required for the Wireless firmware
  hardware.enableRedistributableFirmware = true;
 
  networking = {
    hostName = "nixos-raspi-4"; # Define your hostname.
     networkmanager = {
       enable = true;
       enable = true;
      filter = "*rpi-4-*.dtb";
     };
     };
   };
   };
 
  console.enable = false;
   environment.systemPackages = with pkgs; [
   environment.systemPackages = with pkgs; [
     neovim
     libraspberrypi
    raspberrypi-eeprom
   ];
   ];
 
   system.stateVersion = "23.11";
  users = {
    defaultUserShell = pkgs.zsh;
    mutableUsers = false;
    users.root = {
      password = "apassword";
    };
    users.anormaluser = {
      isNormalUser = true;
      password = "apassword";
      extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user.
    };
  };
 
  environment.variables = {
    EDITOR = "nvim";
  };
 
  programs.zsh = {
    enable = true;
    syntaxHighlighting.enable = true;
    interactiveShellInit = ''
      source ${pkgs.grml-zsh-config}/etc/zsh/zshrc
    '';
    promptInit = ""; # otherwise it'll override the grml prompt
  };
 
  nix = {
    autoOptimiseStore = true;
    gc = {
      automatic = true;
      dates = "weekly";
      options = "--delete-older-than 30d";
    };
    # Free up to 1GiB whenever there is less than 100MiB left.
    extraOptions = ''
      min-free = ${toString (100 * 1024 * 1024)}
      max-free = ${toString (1024 * 1024 * 1024)}
    '';
  };
 
  # Assuming this is installed on top of the disk image.
  fileSystems = {
    "/" = {
      device = "/dev/disk/by-label/NIXOS_SD";
      fsType = "ext4";
      options = [ "noatime" ];
    };
  };
 
  nixpkgs.config = {
    allowUnfree = true;
  };
  powerManagement.cpuFreqGovernor = "ondemand";
   system.stateVersion = "20.09";
  #swapDevices = [ { device = "/swapfile"; size = 3072; } ];
}
}
</nowiki>}}
</nowiki>}}
Line 159: Line 87:
</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.


When running from USB device without SD card present, kernel spams log about missing SD card, workaround for this is to set:
{{outdated|This will only work when not using U-Boot. Configuring through an overlay will be required.}}
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{
  boot.loader.raspberryPi.firmwareConfig = "dtparam=sd_poll_once=on";
}
</nowiki>}}
=== GPU support ===
=== GPU support ===


Line 180: Line 99:
     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 187: Line 106:
==== 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 202: Line 121:
     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:


{{outdated|An equivalent change that works with U-Boot through <code>hardware.deviceTree</code> is needed.}}
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
  sound.enable = true;
  hardware.pulseaudio.enable = true;
  hardware.raspberry-pi."4".audio.enable = true;
</nowiki>}}


In addition to the usual config, you will need to enable audio support explicitly in the firmwareConfig.
For audio through hdmi:


{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
   sound.enable = true;
   sound.enable = true;
   hardware.pulseaudio.enable = true;
   hardware.pulseaudio.enable = true;
  boot.kernelParams = [
"snd_bcm2835.enable_hdmi=1"];
</nowiki>}}
=== Using GPIO pins as non-root ===


  boot.loader.raspberryPi.firmwareConfig = ''
By default, the GPIO pins are enabled, but can only be accessed by the root user.
    dtparam=audio=on
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.
  '';
</nowiki>}}


==== Gpio ====
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.
Add gpio group and change permission for all users in new gpio group.
Now all users in gpio group have access to /dev/gpiomem and to gpio pins via sysfs.


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.


{{file|/etc/nixos/configuration.nix|nix|<nowiki>
<syntaxHighlight lang="nix">
   # add gpio group
   # Create gpio group
   users.groups.gpio = {};
   users.groups.gpio = {};


   # udev rule for gpio
   # Change permissions gpio devices
   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'"
   '';
   '';


   # add user with gpio group
   # Add user to group
   users = {
   users = {
    mutableUsers = false;
     users.mygpiouser = {
     users.mygpiouser = {
      isNormalUser = true;
       extraGroups = [ "gpio" ... ];
      password = "mygpiouserpasswd";
      ....
       extraGroups = [ "wheel" "gpio" ];
     };
     };
   };
   };
</syntaxHighlight>
=== Enabling the SPI ===
To enable the SPI, you would normally add <code>dtparam=spi=on</code> to <code>/boot/config.txt</code>.
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.
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">
hardware.raspberry-pi."4".apply-overlays-dtmerge.enable = true;
hardware.deviceTree = {
  enable = true;
  filter = "*-rpi-*.dtb";
  overlays = [
    {
      name = "spi";
      dtsoFile = ./spi0-0cd.dtso;
    }
  ];
};
users.groups.spi = {};
services.udev.extraRules = ''
  SUBSYSTEM=="spidev", KERNEL=="spidev0.0", GROUP="spi", MODE="0660"
'';
</syntaxHighlight>
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.
=== HDMI-CEC ===


A few bits and pieces for using HDMI-CEC on the Pi4:
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{ pkgs, ... }:
{
  # an overlay to enable raspberrypi support in libcec, and thus cec-client
  nixpkgs.overlays = [
    # nixos-22.05
    # (self: super: { libcec = super.libcec.override { inherit (self) libraspberrypi; }; })
    # nixos-22.11
    (self: super: { libcec = super.libcec.override { withLibraspberrypi = true; }; })
  ];
  # install libcec, which includes cec-client (requires root or "video" group, see udev rule below)
  # scan for devices: `echo 'scan' | cec-client -s -d 1`
  # set pi as active source: `echo 'as' | cec-client -s -d 1`
  environment.systemPackages = with pkgs; [
    libcec
  ];
  services.udev.extraRules = ''
    # allow access to raspi cec device for video group (and optionally register it as a systemd device, used below)
    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
  # scan for devices: `echo 'scan' </nowiki>><nowiki> /run/cec.fifo ; journalctl -u cec-client.service`
  # set pi as active source: `echo 'as' </nowiki>><nowiki> /run/cec.fifo`
  systemd.sockets."cec-client" = {
    after = [ "dev-vchiq.device" ];
    bindsTo = [ "dev-vchiq.device" ];
    wantedBy = [ "sockets.target" ];
    socketConfig = {
      ListenFIFO = "/run/cec.fifo";
      SocketGroup = "video";
      SocketMode = "0660";
    };
  };
  systemd.services."cec-client" = {
    after = [ "dev-vchiq.device" ];
    bindsTo = [ "dev-vchiq.device" ];
    wantedBy = [ "multi-user.target" ];
    serviceConfig = {
      ExecStart = ''${pkgs.libcec}/bin/cec-client -d 1'';
      ExecStop = ''/bin/sh -c "echo q </nowiki>><nowiki> /run/cec.fifo"'';
      StandardInput = "socket";
      StandardOutput = "journal";
      Restart="no";
  };
}
</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>}}
== Notes about the boot process ==


== Troubleshooting ==
Unless using an extremely early WIP image, the Raspberry Pi 4B boots using the U-Boot platform firmware.


=== Power issues ===
=== Updating U-Boot/Firmware ===


The Raspberry Pi 4B is as power-hungry, if not more, as its predecessors. It is important to have a [https://www.raspberrypi.org/documentation/hardware/raspberrypi/power/README.md sufficient enough power supply] or ''weirdness'' may happen. Weirdness may include:
{{commands| <nowiki>
$ nix-shell -p raspberrypi-eeprom
$ sudo mount /dev/disk/by-label/FIRMWARE /mnt
$ sudo BOOTFS=/mnt FIRMWARE_RELEASE_STATUS=stable rpi-eeprom-update -d -a
</nowiki>}} [https://nix.dev/tutorials/installing-nixos-on-a-raspberry-pi#updating-firmware source]


* Lightning bolt icon on HDMI output "breaking" the display.
== Troubleshooting ==
* Screen switching back to u-boot text
** Fixable temporarily when power is sufficient by switching VT (alt+F2 / alt+F1)
* Random hangs


{{note|A ''properly rated'' USB power supply, AND a good cable are necessary. The cable has to be short enough to not incur power losses through the length. Do note that thin and cheap cables usually have thinner copper wires, which in turn accentuates power losses.}}
=== Audio not playing and Bluetooth: no controller available ===


Note that the Type-C USB receptacle for the Raspberry Pi 4B '''does not implement Power Delivery (USB PD)'''. This means that it is limited to whatever the power supply will provide when not negotiating power, which is most likely 5V at some undetermined power level.
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>
2

edits