NixOS on raspberry pi zero w

Nothing wrong really, you just need to run nix-channel --update to download the channel.

Alternatively, you could import ${modulesPath}/installer/cd-dvd/channel.nix, which will copy the channel into the image, so you don’t need to download it later.

oh ok. Trying the import thing like so doesn’t work:

# flake.nix
...
          system.stateVersion = "23.11";
          imports = [
            "${modulesPath}/installer/cd-dvd/channel.nix"
          ];

because I get this error on build:

/nix/store/9daif9c2pmwa7hwnzrazkxi6nykrl0v3-extlinux-conf-builder.sh: line 138: cd: /nix/var/nix/profiles: No such file or directory
Preparing store paths for image...
Creating an EXT4 image of 1744146432 bytes (numInodes=107152, numDataBlocks=211513)
mke2fs 1.47.0 (5-Feb-2023)
Discarding device blocks: done
Creating filesystem with 425817 4k blocks and 106496 inodes
Filesystem UUID: 44444444-4444-4444-8888-888888888888
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912

Allocating group tables: done
Writing inode tables: done
Creating journal (8192 blocks): done
Copying files into the device: ext2fs_symlink: Could not allocate inode in ext2 filesystem while creating symlink "coreutils"
__populate_fs: Could not allocate inode in ext2 filesystem while writing symlink"coreutils"
mkfs.ext4: Could not allocate inode in ext2 filesystem while populating file system

However running nix-channel --update works, but it takes a while. Then I’am able to run the nix-shell -p hello command, but it builds a lot of things from scratch because there are a lot of gcc processes shown when I run top in a second tty.

So I’m now asking me a few things:

  1. how can I fix the channel error from above by importing it directly into the image to speed up things?
  2. how can I include some packages like hello or python already when I build the image so that they don’t need to be compiled on the rpi itself?
  3. If I include things like users, service configurations, … in the sd-image build flake, is that included later in the sd-image, and where do I find the nix configuration file defining that in the sd image? (I couldn’t find anything like that in /etc/nix or /etc/nixos). And is that even necessary when I’m not planning on rebuilding the system on the device directly?

Sorry if those questions sound easy, I am currently trying to get my first nixos system running. And again, thank you really much for your great help, really appreciate it.

because I get this error on build:

I’m not sure: it may be that this module is slightly incompatible with the sd image builder. It’s normally used for the installer image.

but it builds a lot of things from scratch because there are a lot of gcc processes

That’s unfortunately expected: there is no binary cache at all for armv6, so any package not included in the image will have to be built.

  1. how can I fix the channel error from above by importing it directly into the image to speed up things?

No idea, sorry.

  1. how can I include some packages like hello or python already when I build the image so that they don’t need to be compiled on the rpi itself?

Just add them to environment.systemPackages, like you normally would for NixOS.

  1. If I include things like users, service configurations, … in the sd-image build flake, is that included later in the sd-image

Yes, you’re essentially building a normal system (in the /run/current-system directory sense), but the result and all its dependencies are collected into a compressed archive (the image).

and where do I find the nix configuration file defining that in the sd image? (I couldn’t find anything like that in /etc/nix or /etc/nixos). And is that even necessary when I’m not planning on rebuilding the system on the device directly?

No, it’s not necessary if you don’t plan on editing the configuration later (and running nixos-rebuild).
What I do is: I write a typical configuration.nix file, import the image builder module, build the image, flash unto the SD card and plug it in the raspberry pi. On the first boot the image will even auto-expand to fill the whole card, so it will look like a normal install.

Thank you very much for your help and being patient with me. Installing packages like python through environment.systemPackages already works now. I also think I understood a bit further how the sd image and the configuration.nix works.

I’m now trying to get i2c working, so first I added the i2c-tools package, build and bootet, but the command i2c-detect -y 1 now shows me this error (also tested with sudo):

Error: Could not open file `/dev/i2c-1' or `/dev/i2c/1': No such file or directory"

So I thought that maybe i2c is not enabled.
First I tried adding this device tree overlay to my flake, but I still get the same error:

        hardware.deviceTree = {
           enable = true;
           overlays = [
             {
               name = "i2c1-overlay";
               dtsText = ''
                 /dts-v1/;
                 /plugin/;
                 / {
                   compatible = "brcm,bcm2835";
                   fragment@0 {
                     target = <&i2c1>;
                     __overlay__ {
                       status = "okay";
                     };
                   };
                 };
               '';
             }
           ];
         };

Next I tried this, but I still get the same error.

         boot.loader.raspberryPi.firmwareConfig = ''
            dtparam=i2c=on
          '';
          boot.kernelModules = [ "i2c-dev" ];
          hardware.i2c.enable = true;

Do you got i2c working on your model B or have any idea what I could try next?

Thank you very much for your help and being patient with me. Installing packages like python through environment.systemPackages already works now. I also think I understood a bit further how the sd image and the configuration.nix works.

No problem, I’m glad to be of help.

Do you got i2c working on your model B or have any idea what I could try next?

I have never really tried i2c. I used SPI to flash chips but that was some time back, so I don’t remember whether I was using NixOS or alarm.

IIRC the dtparam options won’t work because they are a feature of the raspberry pi bootloader, but we are using uboot. So, the right approach is to use a device tree overlay, as in your first attempt.

I gave a quick look at the dtb (if you build linuxPackages_rpi1.kernel, you can inspect the tree with nix run -f '<nixpkgs>' dtc result/dtbs/bcm2835-rpi-zero-w.dtb) and I think something like this should work:

{ name = "enable-i2c1-device";
  dtsText = ''
    /dts-v1/;
    /plugin/;
    / { compatible = "brcm,bcm2835-i2c"; };
    &i2c1 { status = "okay"; };
  '';
}

There are two i2c devices listed, so try both to be sure.

Thanks for your help. Trying your provided device tree doesn’t work. Reading the pin assignments from the bcm2835 datasheet it seems like i2c1 should be right one, because the pins 2,3 (these are what I’m using, already tested with raspberry pi os) are named SDA1 and SCL1. GPIO0 and 1 have the SDA0 and SCL0 label. But either way, shouldn’t there be a /dev/i2c* file or directory which I never saw with nixos.

What syntax is that dts actually? It looks really weird to me. And where do you have that &i2c1 thing from? Why is that outside of the compatible scope? I can’t find something like that on the internet.

I now played a bit further with the dts by actually extracting the dtb files from the second partition and converting them to dts files using nix run -f '<nixpkgs>' dtc m/boot/nixos/<some-hash>-device-tree-overlays/bcm2835-rpi-zero-w.dtb > current.dts and found out that:

  1. if I use your provided device tree overlay there is no diff in the dts file to just using nothing.
  2. if I use the device tree overlay (I now found the source again, I just used the one from the rpi4 on the nixpgs and adjusted the chip) from my first post about it (post #16), I get the following diff compared to just adding nothing. But this still not works:
diff --git a/bcm2835-rpi-zero-w-nix.dts b/bcm2835-rpi-zero-w-nix-wdt-2.dts
index 6b839b7..e926da4 100644
--- a/bcm2835-rpi-zero-w-nix.dts
+++ b/bcm2835-rpi-zero-w-nix-wdt-2.dts
@@ -756,21 +756,21 @@
                        phandle = <0x05>;
                };

                i2c@7e804000 {
                        compatible = "brcm,bcm2835-i2c";
                        reg = <0x7e804000 0x1000>;
                        interrupts = <0x02 0x15>;
                        clocks = <0x08 0x14>;
                        #address-cells = <0x01>;
                        #size-cells = <0x00>;
-                       status = "disabled";
+                       status = "okay";
                        pinctrl-names = "default";
                        pinctrl-0 = <0x14>;
                        clock-frequency = <0x186a0>;
                        phandle = <0x24>;
                };

                usb@7e980000 {
                        compatible = "brcm,bcm2708-usb";
                        reg = <0x7e980000 0x10000 0x7e006000 0x1000>;
                        interrupts = <0x01 0x09 0x02 0x00>;

I then tried to manually modify the dtb file in the image by mounting the image, converting it to a dts file, applying the following patch and compiling it back with the following command. Then I moved it into the image, unmounted and tried to boot it. I then also verified that uboot showed the loading of the correct .dtb file on boot.

nix-shell -p dtc
dtc -O dtb -o bcm2835-rpi-zero-w-nix-wdt-3-modify.dtb bcm2835-rpi-zero-w-nix-wdt-3-modify.dts

This patch basically modified/added the status="okay"; everywhere where i2c is mentioned.

dts patch
diff --git a/bcm2835-rpi-zero-w-nix.dts b/bcm2835-rpi-zero-w-nix-wdt-3-modify.dts
index 6b839b7..def3c51 100644
--- a/bcm2835-rpi-zero-w-nix.dts
+++ b/bcm2835-rpi-zero-w-nix-wdt-3-modify.dts
@@ -624,15 +624,15 @@
                i2c@7e205000 {
                        compatible = "brcm,bcm2835-i2c";
                        reg = <0x7e205000 0x200>;
                        interrupts = <0x02 0x15>;
                        clocks = <0x08 0x14>;
                        #address-cells = <0x01>;
                        #size-cells = <0x00>;
-                       status = "disabled";
+                       status = "okay";
                        clock-frequency = <0x186a0>;
                        phandle = <0x1a>;
                };

                dpi@7e208000 {
                        compatible = "brcm,bcm2835-dpi";
                        reg = <0x7e208000 0x8c>;
@@ -759,15 +759,15 @@
                i2c@7e804000 {
                        compatible = "brcm,bcm2835-i2c";
                        reg = <0x7e804000 0x1000>;
                        interrupts = <0x02 0x15>;
                        clocks = <0x08 0x14>;
                        #address-cells = <0x01>;
                        #size-cells = <0x00>;
-                       status = "disabled";
+                       status = "okay";
                        pinctrl-names = "default";
                        pinctrl-0 = <0x14>;
                        clock-frequency = <0x186a0>;
                        phandle = <0x24>;
                };

                usb@7e980000 {
@@ -850,15 +850,15 @@
                i2c@7e805000 {
                        compatible = "brcm,bcm2835-i2c";
                        reg = <0x7e805000 0x1000>;
                        interrupts = <0x02 0x15>;
                        clocks = <0x08 0x14>;
                        #address-cells = <0x01>;
                        #size-cells = <0x00>;
-                       status = "disabled";
+                       status = "okay";
                        clock-frequency = <0x186a0>;
                        phandle = <0x18>;
                };

                vec@7e806000 {
                        compatible = "brcm,bcm2835-vec";
                        reg = <0x7e806000 0x1000>;
@@ -993,28 +993,30 @@
                };

                i2c0mux {
                        compatible = "i2c-mux-pinctrl";
                        #address-cells = <0x01>;
                        #size-cells = <0x00>;
                        i2c-parent = <0x1a>;
-                       status = "disabled";
+                       status = "okay";
                        pinctrl-names = "i2c0\0i2c_csi_dsi";
                        pinctrl-0 = <0x1b>;
                        pinctrl-1 = <0x1c>;
                        phandle = <0x23>;

                        i2c@0 {
+                               status = "okay";
                                reg = <0x00>;
                                #address-cells = <0x01>;
                                #size-cells = <0x00>;
                                phandle = <0x7e>;
                        };

                        i2c@1 {
+                               status = "okay";
                                reg = <0x01>;
                                #address-cells = <0x01>;
                                #size-cells = <0x00>;
                                phandle = <0x7f>;
                        };
                };

But also that way, there is no /dev/i2c* so the i2cdetect command fails. Do you have any idea how I can get that to work?

What syntax is that dts actually? It looks really weird to me.

It seems to be a syntactic sugar1 2 that has been available for a while, but few people have picked it up. Anyway, this is what the kernel docs tell you to write, now.

And where do you have that &i2c1 thing from?

I took the label from the base device tree: you can dump it as I said before.

Why is that outside of the compatible scope? I can’t find something like that on the internet.

I suppose it’s to avoid repeating it for every property being changes. I can
assure you it’s correct: i have several overlays written like this (enabling
audio, gpu, etc.)

I don’t remember where I first learned about overlays, probably from someone in
#nixos-on-arm:nixos.org. You should try asking there, because I think this is as far as I can go.

But also that way, there is no /dev/i2c* so the i2cdetect command fails. Do you have any idea how I can get that to work?

I don’t know, AFAIK hardware.i2c.enable = true is all you should need to get the i2c devices set up.

Hello. Thank you for this great topic.
So I decided to try build my own image for rpi zero w.
I basically used provided flake and try to compile on aws aarch64 machine.

However, without luck.
System is 23.11 and this issue (unable to unpack archive) seems to be weird.

What I missed? NixOS on aws is just some random selected comunity ami so maybe should I start to dig here?

error: builder for '/nix/store/vy12b7lmf3y548whsjfv3m81vlm8g2zz-libpng-apng-armv6l-unknown-linux-gnueabihf-1.6.40.drv' failed with exit code 1;
       last 3 log lines:
       > unpacking sources
       > unpacking source archive /nix/store/jpqz1p52xw3jlhc8w143v92k0iaar2i4-libpng-1.6.40.tar.xz
       > do not know how to unpack source archive /nix/store/jpqz1p52xw3jlhc8w143v92k0iaar2i4-libpng-1.6.40.tar.xz
       For full logs, run 'nix log /nix/store/vy12b7lmf3y548whsjfv3m81vlm8g2zz-libpng-apng-armv6l-unknown-linux-gnueabihf-1.6.40.drv'.
error: 1 dependencies of derivation '/nix/store/2q5j823rwvrr8431y8dyg9i2w3cdrbsr-freetype-armv6l-unknown-linux-gnueabihf-2.13.2.drv' failed to build
error: 1 dependencies of derivation '/nix/store/9v3h4811dqsqc65yrvwq4y7mbyz7s8k0-fontconfig-armv6l-unknown-linux-gnueabihf-2.14.2.drv' failed to build
error: 1 dependencies of derivation '/nix/store/kdw5vbdbnp6djlqggmcw02lniqkay0zd-fontconfig-conf.drv' failed to build
error: 1 dependencies of derivation '/nix/store/li54ky7kl6yhjq204rkba2aig66x2hqk-system-path.drv' failed to build
error (ignored): error: cannot unlink '/run/user/0/nix-build-binutils-armv6l-unknown-linux-gnueabihf-2.40.drv-0/binutils-2.40/ld/.deps': Directory not empty
error (ignored): error: cannot unlink '/run/user/0/nix-build-libtasn1-armv6l-unknown-linux-gnueabihf-4.19.0.drv-0': Directory not empty
error: 1 dependencies of derivation '/nix/store/ban8rmijp7l85kvhgy5jg3qsrynahgm1-nixos-system-nixos-23.11.20240115.b8dd8be.drv' failed to build
error: 1 dependencies of derivation '/nix/store/sg2x74hlkf721z4355s8sy3zs9sqwm1y-ext4-fs.img.zst-armv6l-unknown-linux-gnueabihf.drv' failed to build
error: 1 dependencies of derivation '/nix/store/34ywqprscl80bmpwcpc2ihggnrk021wa-nixos-sd-image-23.11.20240115.b8dd8be-armv6l-linux.img-armv6l-unknown-linux-gnueabihf.drv' failed to build

[root@ip-172-31-16-53:~/flake]# nix --extra-experimental-features nix-command  log /nix/store/vy12b7lmf3y548whsjfv3m81vlm8g2zz-libpng-apng-armv6l-unknown-linux-gnueabihf-1.6.40.drv
warning: The interpretation of store paths arguments ending in `.drv` recently changed. If this command is now failing try again with '/nix/store/vy12b7lmf3y548whsjfv3m81vlm8g2zz-libpng-apng-armv6l-unknown-linux-gnueabihf-1.6.40.drv^*'
@nix { "action": "setPhase", "phase": "unpackPhase" }
unpacking sources
unpacking source archive /nix/store/jpqz1p52xw3jlhc8w143v92k0iaar2i4-libpng-1.6.40.tar.xz
do not know how to unpack source archive /nix/store/jpqz1p52xw3jlhc8w143v92k0iaar2i4-libpng-1.6.40.tar.xz

Not sure about that error, I think I also saw this onetime, but don’t know anymore how I got it and also what I did to solve it. Could you maybe provide more logs (nix log /nix/store/vy12b7lmf3y548whsjfv3m81vlm8g2zz-libpng-apng-armv6l-unknown-linux-gnueabihf-1.6.40.drv) and the exact flake (of course redact secrets) you’re using?

Thank you very much for that tip. That did the trick in combination with my initial device tree overlay. I2C now works.

I then tried to analyze if I can get the &i2c1 version to work. Reading about your two links I now understand that these &[node] things are semantic sugar to extend other nodes. I then tried to use that in combination with the hardware.i2c.enable = true but I still had no success as I already predicted since this doesn’t change the dtb file (I already analyzed this in my last post). I then tried to remove the -i2c suffix and still had the hardware.i2c.enable = true set which works now. So

I2C TL;DR

To enable i2c on a raspberry pi zero, use:

          hardware.deviceTree = {
            enable = true;
            overlays = [
              {
                name = "enable-i2c1-device";
                dtsText = ''
                  /dts-v1/;
                  /plugin/;
                  / { compatible = "brcm,bcm2835"; };
                  &i2c1 { status = "okay"; };
                '';
              }
            ];
          };
          hardware.i2c.enable = true;

WIFI

I now also wifi to work by either

wpa_supplicant:

          hardware.firmware = [ pkgs.raspberrypiWirelessFirmware ];
          networking = {
            wireless = {
              enable = true;

              networks = {
                [SSID] = {
                  psk = "[PSK]";
                };
              };
            };
          };

or using iwd:

          systemd.services.iwd.serviceConfig.Restart = "always";
          hardware.firmware = [ pkgs.raspberrypiWirelessFirmware ];
          networking = {
            networkmanager.wifi.backend = "iwd";
            wireless.iwd.enable = true;
          };
          boot.extraModprobeConfig = ''
            options cfg80211 ieee80211_regdom="DE"
          '';

And then connect to the wifi using the following commands:

$ iwctl
# list
# station wlan0 scan
# station wlan0 get-networks
# station wlan0 connect [ssid]

This will save the config for the next boot to: /var/lib/iwd.

3 Likes

@rnhmjoj do you have, by any chance, an idea about my compile issue in Cross-compile cups for armv6l-linux . I have no idea what is happening there.

Output of nix log is provided in previous comment.
Just:
do not know how to unpack source archive /nix/store/jpqz1p52xw3jlhc8w143v92k0iaar2i4-libpng-1.6.40.tar.xz

Not sure if xz missing in system or something.

And my flake is basically yours:

{
  description = "Build image";
  inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11";
  outputs = { self, nixpkgs }: rec {
    nixosConfigurations.rpi2 = nixpkgs.lib.nixosSystem {
      modules = [
        "${nixpkgs}/nixos/modules/installer/sd-card/sd-image-raspberrypi.nix"
        ({ lib, pkgs, modulesPath, ... }: {
          nixpkgs.config.allowUnsupportedSystem = true;
          nixpkgs.hostPlatform.system = "armv6l-linux";
          nixpkgs.buildPlatform.system = "aarch64-linux"; #If you build on x86 other wise changes this.
          # ... extra configs as above

          system.stateVersion = "23.11";

          nixpkgs.overlays = [
            (final: super: {
              llvmPackages = super.llvmPackages_14;
              cmake = super.cmake.overrideAttrs (old: { env.NIX_CFLAGS_COMPILE = "-latomic"; });
            })
          ];

          boot = {
            initrd.includeDefaultModules = false;
            initrd.kernelModules = [ "ext4" "mmc_block" ];
            supportedFilesystems = lib.mkForce [ "vfat" "ext4" ];
          };
          disabledModules = [
            "${modulesPath}/profiles/all-hardware.nix"
            "${modulesPath}/profiles/base.nix"
          ];


          # setup a user
          users.mutableUsers = false;
          users.users = {
            user = {

...
no chsnges here

I’m building it with:
nix build --extra-experimental-features nix-command --extra-experimental-features flakes --impure --update-input nixpkgs .#images.rpi2

Message came from:
pkgs/stdenv/generic/setup.sh

However, I need to dig around more. For example where unpackCmd come from.

UPDATE:
Seems like I’m not only one stuck with libpng :).

UPDATE 2:
Should be solved but…
https://github.com/NixOS/nixpkgs/issues/278130

What worked for me was to write an overlay using the .tar.gz packaged sources:

(final: prev: {libpng = prev.libpng.overrideAttrs ( old: { src = prev.fetchurl {
      url = "mirror://sourceforge/libpng/libpng-1.6.40.tar.gz";
      hash = "sha256-j3ILNjqghoPJvypWMjb0UxOvLFXVQrVIGuF92NGDu0I=";
    };});})

With this change, I was able to get the sd-image to build (cross-compilation on a x86-64). I updated the nixpkgs bug with the same information.

1 Like

Ok. I ran build of nixos (unstable channel) image on aws arm64 machine and I just ended with:

error: builder for '/nix/store/2d9f8f3gl4hpacfvcr4zj28n0q1mj64b-linux-armv6l-unknown-linux-gnueabihf-6.1.63-stable_20231123.drv' failed with exit code 2;
       last 10 log lines:
       >   LD      vmlinux.o
       >   OBJCOPY modules.builtin.modinfo
       >   GEN     modules.builtin
       >   GEN     .vmlinux.objs
       >   MODPOST Module.symvers
       > ERROR: modpost: "__aeabi_uldivmod" [drivers/pwm/pwm-rp1.ko] undefined!
       > ERROR: modpost: "__aeabi_uldivmod" [drivers/media/platform/raspberrypi/rp1_cfe/rp1-cfe.ko] undefined!
       > ERROR: modpost: "__aeabi_ldivmod" [drivers/media/platform/raspberrypi/rp1_cfe/rp1-cfe.ko] undefined!
       > make[1]: *** [../scripts/Makefile.modpost:126: Module.symvers] Error 1
       > make: *** [../Makefile:1966: modpost] Error 2
       For full logs, run 'nix log /nix/store/2d9f8f3gl4hpacfvcr4zj28n0q1mj64b-linux-armv6l-unknown-linux-gnueabihf-6.1.63-stable_20231123.drv'.
error: 1 dependencies of derivation '/nix/store/314ixmfr3brnjkbxn0ka9lx8rcwy2sgl-nixos-system-handaki-24.05.20240229.1536926.drv' failed to build
error: 1 dependencies of derivation '/nix/store/4jylgypyvdgpjqsnqwq51jingmkalgmd-ext4-fs.img.zst-armv6l-unknown-linux-gnueabihf.drv' failed to build
error: 1 dependencies of derivation '/nix/store/k6jk2vz7zav6ga6bmb60jf87asyn68gc-nixos-sd-image-24.05.20240229.1536926-armv6l-linux.img-armv6l-unknown-linux-gnueabihf.drv' failed to build

I enabled:
boot.binfmt.emulatedSystems = [ "arm7l-linux" ];

However, I’m not sure how to solve missing symbols in kernel modules.

Have you encountered a similar problem?

Ok. Solved. Wrong kernel were on duty.

1 Like

Could you elaborate? I’m running into the same problem.

Hello. Not sure if bug or feature, however I switched to:

system.stateVersion = "nixos-unstable";
boot.kernelPackages = lib.mkForce pkgs.linuxPackages_latest;

And yes, upstream kernel will not have all drivers, but I just don’t need HATs supports etc…

It booted fine, but some time ago. I need to build and test new unstable image.

1 Like

hi together - has anyone experience with this built: GitHub - cyber-murmel/nixos-rpi-zero-w: NixOS on the Raspberry Pi Zero W