Files
shawn e450189858
Lint & Validate / lint (push) Has been cancelled
Docs: Full USB/NVMe boot coverage across all documentation
- 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)
2026-05-19 22:58:49 -04:00

455 lines
13 KiB
Markdown

# 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
```bash
# 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
```
```bash
# 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.
```bash
# 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:**
1. Downloads Raspberry Pi OS Lite (64-bit, Bookworm) base image (~1.1 GB)
2. Extracts and resizes the image (default 8 GB total)
3. Mounts the image partitions via loopback
4. Chroots into the ARM64 filesystem using QEMU user-static
5. Installs audio packages (JACK2, Carla, ALSA tools, Python deps)
6. Copies the mixer source code and systemd service files
7. Configures real-time audio limits, CPU governor, udev rules
8. Sets up first-boot wizard
9. 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.
```bash
# 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/linux` at branch `rpi-6.12.y`
- Applies the `PREEMPT_RT` patch 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 `.deb` using `make deb-pkg`
## Build Customization
### Image Size
The default image is 8 GB. Adjust with `--image-size` (in MB):
```bash
./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:
```bash
MIXER_WIFI_SSID="MyNetwork" \
MIXER_WIFI_PASS="secret123" \
./build/build.sh --skip-kernel
```
### Custom Hostname
```bash
MIXER_HOSTNAME="stage-mixer" ./build/build.sh --skip-kernel
```
### Custom API Key for CI
```bash
MIXER_API_KEY="my-pre-shared-key" ./build/build.sh --skip-kernel
```
### Custom OS Base Image
```bash
RPIOS_URL="https://downloads.raspberrypi.com/raspios_lite_arm64/images/..." \
./build/build.sh
```
### Version Tagging
```bash
export MIXER_VERSION="$(git describe --tags --always)"
./build/build.sh --skip-kernel
```
## Cleaning Up
```bash
# 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.
```bash
./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.
```bash
./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 |
```bash
./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
```bash
# 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
```bash
# 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](https://www.raspberrypi.com/software/) or [balenaEtcher](https://www.balena.io/etcher/):
1. Decompress the `.img.xz` file with 7-Zip or WinRAR
2. Open Raspberry Pi Imager → "Choose OS" → "Use custom"
3. Select the `.img` file
4. Choose your SD card and flash
## First Boot
### Initial Setup
1. Insert the boot media (SD card, USB SSD, or NVMe) into a Raspberry Pi 4B
2. Connect your USB audio interface
3. (Optional) Connect HDMI touchscreen + Ethernet
4. 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:
1. Audio interface detection and selection
2. WiFi network configuration
3. Hostname setting
4. API key generation (for web UI access)
5. JACK buffer size and latency preferences
6. **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:
```bash
ssh pi@pi-mixer.local
passwd
```
### Service Management
```bash
# 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:
```bash
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:
```yaml
- 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):
```bash
# 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](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 install` for 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](research/base-os-decision.md) for the
full analysis.