Skip to content

Pi Image Setup Guide

Jasper van Loenen edited this page Apr 29, 2026 · 29 revisions

Little Printer Zigbee Bridge - Pi Image Setup Guide

This page describes how I create the image file used on the Raspberry Pi. Regular users don't need this, but I've put this here for reference and because others might find this useful.

All steps are performed on a Linux machine with the Raspberry Pi SD card mounted via a card reader. The root partition is assumed to be mounted at /media/$USER/rootfs and the boot partition at /media/$USER/bootfs.

Requirements: This guide targets Raspberry Pi OS Trixie Lite. Both 32-bit and 64-bit are supported. Use the 32-bit image for the Pi Zero W, and the 64-bit image for the Pi Zero 2W, Pi 3, Pi 4, or Pi 5.


How it works

Trixie includes cloud-init, which handles first-boot configuration via two files on the boot partition:

  • user-data - creates the user, sets the hostname, installs Python dependencies, and starts our bridge service
  • network-config - configures WiFi

Users only need to edit network-config to enter their WiFi credentials before first boot. Both files live on the FAT32 boot partition, so they are editable on Windows, Mac, and Linux without needing root.


1. Find The SD Card Mount Points

lsblk

Identify the device (e.g. /dev/sda) and note where the partitions are mounted, for example:

  • Root partition -> /media/$USER/rootfs
  • Boot partition -> /media/$USER/bootfs

If the partitions are not mounted yet:

sudo mkdir -p /media/$USER/rootfs /media/$USER/bootfs
sudo mount /dev/sda2 /media/$USER/rootfs
sudo mount /dev/sda1 /media/$USER/bootfs

2. Write the cloud-init Configuration Files

These two files replace the wifi-setup service, user creation, and first-boot service from the old approach. They go on the boot partition and are processed automatically on first boot by cloud-init.

2a. network-config - WiFi credentials (user-editable)

This is the file end users edit to connect to their WiFi network. Replace the existing network-config on the boot partition:

cat > /media/$USER/bootfs/network-config << 'EOF'
network:
  version: 2
  wifis:
    wlan0:
      dhcp4: true
      # Change this to your country code (ISO 3166-1 alpha-2)
      regulatory-domain: "NL"
      access-points:
        "YourNetworkName":
          password: "YourPassword"
      optional: true
EOF

2b. user-data - system configuration

This creates the bridge user, sets the hostname, installs Python dependencies on first boot, enables the bridge service, and disables cloud-init after it has run. Replace the existing user-data on the boot partition:

cat > /media/$USER/bootfs/user-data << 'EOF'
#cloud-config

hostname: little-printer-bridge
manage_etc_hosts: false

timezone: Europe/Amsterdam

users:
  - name: bridge
    groups: users,adm,dialout,audio,netdev,video,plugdev,cdrom,games,input,gpio,spi,i2c,render,sudo
    shell: /bin/bash
    lock_passwd: false
    plain_text_password: littleprinterlives

enable_ssh: false

rpi:
  i2c: true
  spi: true

# Install system-level dependencies
packages:
  - libopenjp2-7
  - python3-pip

# Run once on first boot after network is up
runcmd:
  # Install Python dependencies
  - cd /home/bridge/little-printer-zigbee-bridge && pip3 install -r bridge/requirements.txt --break-system-packages
  # Fix ownership of project files
  - chown -R bridge:bridge /home/bridge
  # Enable and start the bridge service
  - systemctl enable little-printer-zigbee-bridge.service
  - systemctl start little-printer-zigbee-bridge.service
  # Disable cloud-init so it does not run again on subsequent boots
  - touch /etc/cloud/cloud-init.disabled
EOF

3. Add the Bridge Service File

This is the only systemd service we still need to create manually, since cloud-init does not have a built-in way to define persistent services.

sudo tee /media/$USER/rootfs/etc/systemd/system/little-printer-zigbee-bridge.service << 'EOF'
[Unit]
Description=Little Printer Zigbee Bridge Script
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=bridge
WorkingDirectory=/home/bridge/little-printer-zigbee-bridge
ExecStartPre=/bin/sh -c 'until ping -c1 1.1.1.1 > /dev/null 2>&1; do sleep 2; done'
ExecStart=/usr/bin/env python3 -m bridge.main --sirius
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

Note: the service is not enabled here - cloud-init's runcmd enables it on first boot after dependencies are installed.


4. Copy Project Files

Copy the bridge project onto the image. Ownership will be fixed by cloud-init's runcmd on first boot.

sudo cp -r /path/to/little-printer-zigbee-bridge \
           /media/$USER/rootfs/home/bridge/little-printer-zigbee-bridge

5. Clean Private Data Before Creating Image

WiFi credentials

Restore network-config to placeholder values - the same file from step 2a but with your actual credentials replaced:

cat > /media/$USER/bootfs/network-config << 'EOF'
network:
  version: 2
  wifis:
    wlan0:
      dhcp4: true
      # Change this to your country code (ISO 3166-1 alpha-2)
      regulatory-domain: "NL"
      access-points:
        "YourNetworkName":
          password: "YourPassword"
      optional: true
EOF

Shell history

sudo rm -f /media/$USER/rootfs/root/.bash_history
sudo rm -f /media/$USER/rootfs/home/bridge/.bash_history

Machine ID

sudo rm -f /media/$USER/rootfs/etc/machine-id
sudo rm -f /media/$USER/rootfs/var/lib/dbus/machine-id
sudo touch /media/$USER/rootfs/etc/machine-id

An empty /etc/machine-id triggers a fresh ID to be generated on first boot.

Logs

sudo rm -rf /media/$USER/rootfs/var/log/journal/*
sudo rm -rf /media/$USER/rootfs/var/log/*.log
sudo rm -rf /media/$USER/rootfs/tmp/*
sudo rm -rf /media/$USER/rootfs/var/tmp/*

6. Create the .img File

Unmount the SD card partitions (keep the card inserted)

sudo umount /media/$USER/rootfs
sudo umount /media/$USER/bootfs

Image the SD card with dd

Replace /dev/sda with your actual device from lsblk:

sudo dd if=/dev/sda of=~/little-printer-zigbee-bridge.img bs=4M status=progress

Shrink and compress with PiShrink

wget https://raw.githubusercontent.com/Drewsif/PiShrink/master/pishrink.sh
chmod +x pishrink.sh
sudo ./pishrink.sh -z ~/little-printer-zigbee-bridge.img

This creates little-printer-zigbee-bridge.img.gz, compressed and shrunk to the minimum size needed.


7. User Instructions

  1. Download little-printer-zigbee-bridge.img.gz and flash it to an SD card using Raspberry Pi Imager or balenaEtcher
  2. After flashing, open the boot partition of the SD card (visible on Windows, Mac, and Linux as a small FAT drive)
  3. Open network-config and replace YourNetworkName and YourPassword with your WiFi credentials
  4. Optionally update regulatory-domain to your country code (e.g. GB, US, DE)
  5. Save the file, eject the SD card, and insert it into the Raspberry Pi
  6. Power on - the Pi will configure WiFi, install Python dependencies, and start the bridge automatically on first boot. The first boot takes a little longer than usual while dependencies are installed.

Troubleshooting

Bridge does not start Check the cloud-init log to see if first-boot setup completed:

sudo cat /var/log/cloud-init-output.log

WiFi never comes up Check NetworkManager and the network-config that was applied:

sudo systemctl status NetworkManager
sudo cat /var/log/cloud-init-output.log | grep -i network

Script starts before network is ready The ExecStartPre ping loop should make the script wait until internet is reachable.

Check bridge service status

sudo systemctl status little-printer-zigbee-bridge.service
sudo journalctl -u little-printer-zigbee-bridge.service

Clone this wiki locally