- build-guide.md: Boot medium options table, USB/NVMe flash instructions, EEPROM boot order setup, first-boot wizard boot config step - user-manual.md: Required hardware lists all 3 boot targets, EEPROM bootstrap instructions, NVMe HAT recommendation for live use - hardware-compatibility.md: New storage/boot media section with NVMe HAT compatibility table (official, Pimoroni, Geekworm) - Obsidian wiki: Build section with all 3 targets + EEPROM config - developer-guide.md: Added (from task completion)
13 KiB
Build Guide — Disk Image Builder (SD / USB / NVMe)
This guide walks through building a ready-to-flash Raspberry Pi disk image containing the full audio mixer stack with a PREEMPT_RT kernel. Supports SD card, USB SSD, and NVMe HAT boot targets.
Prerequisites
Host System Requirements
- OS: Any modern Linux distribution (x86_64) — Ubuntu 22.04+, Debian 12+, Fedora 38+
- CPU: 4+ cores recommended (cross-compilation is parallel)
- RAM: 4GB+ (8GB recommended for full PREEMPT_RT kernel build)
- Disk: 15GB free space (build artifacts + downloaded OS image)
- Architecture: x86_64 host for QEMU user-static chroot emulation
- macOS is supported via a Linux VM or Docker container
Install Build Dependencies
# Ubuntu / Debian
sudo apt update
sudo apt install -y \
wget xz-utils \
dosfstools e2fsprogs \
qemu-user-static \
parted \
git
# For PREEMPT_RT kernel cross-compilation (optional)
sudo apt install -y \
gcc-aarch64-linux-gnu \
build-essential \
flex bison \
libssl-dev \
bc \
rsync
# Verify QEMU binary is registered
sudo update-binfmts --enable qemu-aarch64
# Fedora
sudo dnf install -y wget xz dosfstools e2fsprogs qemu-user-static parted git
Build Modes
The build script supports three modes, controlled by command-line flags:
| Command | Description | Time | Requires |
|---|---|---|---|
./build/build.sh --skip-kernel |
Quick build, stock RPi kernel | 10-15 min | Basic deps only |
./build/build.sh |
Full build with PREEMPT_RT kernel | 30-45 min | + gcc-aarch64 + kernel deps |
./build/build.sh --kernel-only |
Build only the RT kernel .deb | 10-15 min | + gcc-aarch64 + kernel deps |
Step-by-Step: Quick Build (Recommended First Build)
This builds the mixer image using the stock Raspberry Pi OS kernel — fastest way to get a working image for testing. No cross-compilation toolchain needed.
# 1. Clone the repository
git clone https://github.com/your-org/rpi-audio-mixer.git
cd rpi-audio-mixer
# 2. Run the quick build
./build/build.sh --skip-kernel
# 3. The compressed image lands here:
ls -lh build/out/rpi-audio-mixer-*.img.xz
What happens during the build:
- Downloads Raspberry Pi OS Lite (64-bit, Bookworm) base image (~1.1 GB)
- Extracts and resizes the image (default 8 GB total)
- Mounts the image partitions via loopback
- Chroots into the ARM64 filesystem using QEMU user-static
- Installs audio packages (JACK2, Carla, ALSA tools, Python deps)
- Copies the mixer source code and systemd service files
- Configures real-time audio limits, CPU governor, udev rules
- Sets up first-boot wizard
- Unmounts and compresses the final image
Step-by-Step: Full PREEMPT_RT Build
The full build includes a custom PREEMPT_RT kernel cross-compiled for aarch64 on your x86_64 host. This is what you want for production use.
# 1. Ensure cross-compilation toolchain is installed
sudo apt install -y gcc-aarch64-linux-gnu build-essential flex bison libssl-dev bc
# 2. Run the full build
./build/build.sh
# This will:
# a. Cross-compile linux-rpi-6.12.y with PREEMPT_RT patch
# b. Package the kernel as a .deb
# c. Build the SD card image with the RT kernel installed
# d. Configure boot/config.txt to use the RT kernel
# 3. Output
ls -lh build/out/rpi-audio-mixer-*.img.xz
Kernel Cross-Compilation Details
The build script:
- Clones
raspberrypi/linuxat branchrpi-6.12.y - Applies the
PREEMPT_RTpatch for 6.12 - Uses
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig - Configures:
CONFIG_PREEMPT_RT=y,CONFIG_HZ=1000 - Builds with
make -j$(nproc)for parallel compilation - Packages into a
.debusingmake deb-pkg
Build Customization
Image Size
The default image is 8 GB. Adjust with --image-size (in MB):
./build/build.sh --skip-kernel --image-size 16384 # 16 GB image
./build/build.sh --skip-kernel --image-size 32768 # 32 GB image
Pre-configure WiFi
Set these environment variables before building:
MIXER_WIFI_SSID="MyNetwork" \
MIXER_WIFI_PASS="secret123" \
./build/build.sh --skip-kernel
Custom Hostname
MIXER_HOSTNAME="stage-mixer" ./build/build.sh --skip-kernel
Custom API Key for CI
MIXER_API_KEY="my-pre-shared-key" ./build/build.sh --skip-kernel
Custom OS Base Image
RPIOS_URL="https://downloads.raspberrypi.com/raspios_lite_arm64/images/..." \
./build/build.sh
Version Tagging
export MIXER_VERSION="$(git describe --tags --always)"
./build/build.sh --skip-kernel
Cleaning Up
# Remove all build artifacts
./build/build.sh --clean
# This deletes:
# build/work/ (mounted image, chroot state, temporary files)
# build/downloads/ (cached OS image downloads)
# build/out/ (built images)
# To preserve downloads (they're cached for faster rebuilds):
rm -rf build/work/ build/out/
Image Contents
Partition layout:
├── /boot (FAT32, 256 MB)
│ ├── kernel8.img Stock 64-bit kernel (fallback)
│ ├── kernel8-rt.img PREEMPT_RT kernel (default if built)
│ ├── config.txt GPU, boot, kernel selection
│ ├── cmdline.txt Kernel parameters (isolcpus, threadirqs, etc.)
│ └── *.dtb, overlays/ Device tree blobs
│
├── / (ext4, ~4 GB used of 6 GB)
│ ├── /opt/rpi-mixer/ Mixer Python source + virtualenv
│ ├── /etc/systemd/system/ Service unit files
│ │ ├── cpu-performance.service
│ │ ├── jackd.service
│ │ ├── mixer-api.service
│ │ ├── mixer-ui.service
│ │ └── mixer-firstboot.service
│ ├── /etc/security/limits.d/99-audio.conf
│ ├── /etc/modprobe.d/alsa-usb.conf
│ └── /home/pi/ Default user home
│
└── /data (ext4, remaining space)
├── sessions/ Saved mixer session JSON files
├── recordings/ Multi-track WAV recordings
├── presets/ Plugin presets
└── logs/ System and audio logs
Boot Medium Options
The same disk image works with three boot targets. Choose during build:
| Target | --target |
Recommended For | Boot Order (EEPROM) |
|---|---|---|---|
| SD Card | sd (default) |
Development, testing | SD → Restart (0xf1) |
| USB SSD | usb |
Portable use, more reliable than SD | USB → SD → Restart (0xf41) |
| NVMe HAT | nvme |
Production / Live use (no USB bus contention) | NVMe → USB → SD → Restart (0xf614) |
SD Card (Default)
The standard target. Simple and cheap. Works with any A2-class SD card. Limited write speed (~90 MB/s sequential) and wear on heavy recording.
./build/build.sh # or --target sd (same thing)
USB SSD
Boot from a USB 3.0 SSD via any USB port. Much faster and more durable than SD. But: Shares the USB bus with audio interfaces — can cause xruns under heavy I/O.
./build/build.sh --target usb
The kernel includes CONFIG_BLK_DEV_NVME as a module when building with --target nvme.
For USB target, standard USB mass storage drivers are already present in the stock kernel.
NVMe HAT
The recommended target for production/live use. Uses dedicated PCIe lanes — zero USB bus contention with audio interfaces. Requires a storage HAT:
| HAT | Interface | M.2 Size | Notes |
|---|---|---|---|
| Official RPi NVMe Base | PCIe Gen 2 x1 | 2230/2242 | Best compatibility |
| Pimoroni NVMe Base | PCIe Gen 2 x1 | 2230/2242 | Well-documented, same as official |
| Geekworm X1001 | PCIe Gen 2 x1 | 2230 | Low profile |
| Geekworm X1002 | PCIe Gen 2 x1 | 2242/2280 | Full length option |
./build/build.sh --target nvme
💡 Tip: Buy a 128GB or 256GB NVMe SSD. They're cheap (~$15-25) and dramatically more reliable than SD cards for live recording.
Build Time Comparison
| Target | Build Time | Notes |
|---|---|---|
| SD | ~30-45 min | Stock kernel + config |
| USB | ~30-45 min | Same as SD, different output docs |
| NVMe | ~31-46 min | +1 min for NVMe driver compilation |
Flashing to SD Card
Linux
# Decompress the image
xz -d build/out/rpi-audio-mixer-*.img.xz
# Identify your SD card
lsblk
# Look for /dev/sdb or /dev/mmcblk0 (NOT your system drive!)
# Flash the image
sudo dd if=build/out/rpi-audio-mixer-*.img \
of=/dev/sdX \
bs=4M \
status=progress \
conv=fsync
# Sync and eject
sync
sudo eject /dev/sdX
macOS
# Decompress
xz -d build/out/rpi-audio-mixer-*.img.xz
# Find the SD card
diskutil list
# Look for /dev/diskX (external, physical)
# Unmount
diskutil unmountDisk /dev/diskX
# Flash (use rdisk for faster writes)
sudo dd if=build/out/rpi-audio-mixer-*.img \
of=/dev/rdiskX \
bs=4m \
status=progress
# Eject
diskutil eject /dev/diskX
Windows
Use Raspberry Pi Imager or balenaEtcher:
- Decompress the
.img.xzfile with 7-Zip or WinRAR - Open Raspberry Pi Imager → "Choose OS" → "Use custom"
- Select the
.imgfile - Choose your SD card and flash
First Boot
Initial Setup
- Insert the boot media (SD card, USB SSD, or NVMe) into a Raspberry Pi 4B
- Connect your USB audio interface
- (Optional) Connect HDMI touchscreen + Ethernet
- Power on
USB/NVMe boot note: If booting from USB SSD or NVMe, you'll need to configure the EEPROM boot order first. Either:
- Boot from SD card once to run the setup wizard (it has an EEPROM config step)
- Or configure manually:
sudo rpi-eeprom-config --edit
- USB:
BOOT_ORDER=0xf41- NVMe:
BOOT_ORDER=0xf614
The first-boot setup wizard will appear on the HDMI display (or serial console). It walks through:
- Audio interface detection and selection
- WiFi network configuration
- Hostname setting
- API key generation (for web UI access)
- JACK buffer size and latency preferences
- Boot target configuration (USB/NVMe EEPROM setup — if accessible)
After the wizard completes, the system reboots into normal operation. The wizard auto-disables itself — it only runs once.
Default Access
| Service | URL / Command | Default Credentials |
|---|---|---|
| Web UI | http://pi-mixer.local:8080 |
API key from wizard |
| SSH | ssh pi@pi-mixer.local |
Password: raspberry |
| Touch UI | HDMI display (auto-starts) | — |
Important: Change the default password immediately:
ssh pi@pi-mixer.local
passwd
Service Management
# Check all mixer services
systemctl status jackd mixer-api mixer-ui
# View logs
journalctl -u mixer-api -f
journalctl -u jackd -f
# Restart services (must follow order: jackd → mixer-api → mixer-ui)
sudo systemctl stop mixer-ui mixer-api jackd
sudo systemctl start jackd
sleep 2
sudo systemctl start mixer-api mixer-ui
CI / Automated Builds
Build without a Raspberry Pi (CI-friendly) on any x86_64 host:
export MIXER_VERSION="$(git describe --tags --always)"
export MIXER_HOSTNAME="ci-mixer"
export MIXER_API_KEY="ci-test-key-12345"
./build/build.sh --skip-kernel
Example GitHub Actions / Gitea CI workflow:
- name: Build SD card image
run: |
sudo apt install -y wget xz-utils dosfstools e2fsprogs qemu-user-static
./build/build.sh --skip-kernel
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: sd-card-image
path: build/out/rpi-audio-mixer-*.img.xz
Kernel-Only Build
When you only need a new PREEMPT_RT kernel (e.g., to upgrade an existing image without rebuilding everything):
# Build only the kernel .deb package
./build/build.sh --kernel-only
# The .deb lands in build/out/
ls build/out/linux-image-*.deb
# Install on the RPi:
# scp build/out/linux-image-*.deb pi@pi-mixer.local:/tmp/
# ssh pi@pi-mixer.local
# sudo dpkg -i /tmp/linux-image-*.deb
# sudo reboot
Troubleshooting Build Issues
| Problem | Solution |
|---|---|
qemu-aarch64-static not found |
sudo apt install qemu-user-static && sudo update-binfmts --enable qemu-aarch64 |
apt fails in chroot |
DNS not resolving — check host /etc/resolv.conf is accessible |
No space left on device |
Increase image size: --image-size 16384 |
kernel build fails |
Install missing deps: sudo apt install gcc-aarch64-linux-gnu flex bison libssl-dev bc |
loop device busy |
Run sudo losetup -D to detach stale loop devices |
permission denied |
Build must run with passwordless sudo or as root |
For runtime issues after flashing, see docs/troubleshooting.md.
Architecture Decision
This project uses Raspberry Pi OS Lite (64-bit, Bookworm) + post-install configuration rather than Buildroot or Yocto. Rationale:
- Full Debian package ecosystem for rapid audio stack iteration
apt installfor JACK, Carla, debugging tools — no image rebuilds needed- Standard systemd-based system — familiar and well-documented
- PREEMPT_RT kernel built via cross-compilation on x86 host
- Buildroot/Yocto can be revisited for production appliance images
See docs/research/base-os-decision.md for the full analysis.