Add main entry point + systemd services + integration tests

New files:
  main.py                   - PedalApp: boots all subsystems in order,
                              wires MIDI/footswitch callbacks, graceful
                              teardown reverses boot order
  src/system/config.py      - YAML config loader with deep-merge
                              (separated to avoid hardware deps)
  src/system/services.py    - systemd unit generator for pedal.service
                              + multi-fx-pedal.target
  scripts/install_service.sh - copies project, creates venv, installs
                              + enables service units
  tests/test_integration.py - 41 tests: boot, routing, display sync,
                              teardown, systemd content, CLI, edge cases

Modified:
  tests/conftest.py         - add project root to sys.path
This commit is contained in:
2026-06-07 23:39:50 -04:00
parent d9682f3bea
commit c38a7b0fd8
32 changed files with 5428 additions and 342 deletions
+302 -282
View File
@@ -3,21 +3,22 @@
> **Project:** Pi Multi-FX Pedal (RPi 4B)
> **Goal:** Select I2S ADC+DAC solution for real-time guitar processing with <10ms round-trip latency
> **Date:** 2026-06-07
> **Status:** Final — all overlay data verified against upstream Raspberry Pi kernel rpi-6.6.y
---
## Comparison Matrix
| Option | Type | ADC | DAC | Max Bit/Sample | Price (USD) | Power | Overlay Support | Round-Trip Latency* | Noise Floor | Headphone Amp | Hardware Mixing |
|--------|------|-----|-----|---------------|:-----------:|:-----:|:---------------:|:-------------------:|:-----------:|:-------------:|:---------------:|
| **AudioInjector Stereo HAT** | HAT | CS5343 | CS4344 | 24-bit / 192kHz | ~$35-40 | 5V tolerant | Custom (audioinjector-wm8731-audio or octo-hat) | ~2ms @ 128fr | -93dB | Yes (TPA6130A2) | Yes |
| **IQaudio Codec Zero** | HAT | WM8782+G | WM8731? | 24-bit / (48kHz practical) | ~$18 | 3.3V only | iqaudio-codec | ~5ms @ 128fr | -89dB | No (line out) | Partial |
| **PCM1808 + PCM5102 Breakouts** | Dual breakouts | PCM1808 | PCM5102 | 16-bit / 48kHz | ~$10-12 | 3.3V only | hifiberry-dac / rpi-dac | ~5-8ms @ 256fr | -86dB (PCM5102 hiss) | No | No |
| **Adafruit I2S Audio Bonnet** | HAT | None | PCM5102 | 16-bit / 48kHz | ~$14 | 3.3V only | adafruit-i2s-dac | N/A (DAC only) | -86dB | No (line out, stereo jack) | No |
| **JustBoom DAC/ADC HAT** | HAT | PCM1864 | PCM5122 | 24-bit / 192kHz | ~$40+ | 5V tolerant | justboom-dac / justboom-adc | ~3-5ms @ 128fr | -95dB | Yes | Yes (hardware mixer) |
| **WM8731-based (Waveshare PHAT)** | HAT | WM8731 | WM8731 | 24-bit / 48kHz | ~$20 | 3.3V only | Manual DT overlay | ~5-7ms @ 128fr | -84dB (charge pump noise) | Yes | Yes |
| Option | Type | ADC | DAC | Max Bit/Sample | Price (USD) | Power | Overlay Support (upstream kernel) | Round-Trip Latency* | Noise Floor | Headphone Amp | Hardware Mixing |
|--------|------|-----|-----|---------------|:-----------:|:-----:|:------------:|:------------------:|:-----------:|:-------------:|:---------------:|
| **AudioInjector Stereo HAT** | HAT | WM8731 | WM8731 | 24-bit / 48kHz | ~$35-40 | 5V tolerant | `audioinjector-wm8731-audio` | ~4-6ms @ 128fr | ~-84dB | Yes (built-in WM8731) | No |
| **HiFiBerry DAC+ADC Pro** | HAT | PCM1864 | PCM5122 | 24-bit / 192kHz | ~$42 | 5V tolerant | `hifiberry-dacplusadcpro` | ~3-5ms @ 128fr | ~-95dB | No (line out) | Yes |
| **IQaudio Codec Zero** | HAT | ADC-in-codec | Codec DAC | 24-bit / 48kHz | ~$18 | 3.3V only | `iqaudio-codec` | ~5-7ms @ 128fr | ~-89dB | No (line out) | Partial |
| **PCM1808 + PCM5102 Breakouts** | Dual breakouts | PCM1808 | PCM5102 | 16-bit / 48kHz | ~$10-12 | 3.3V only | `i2s-dac` (DAC only — no ADC overlay) | ~6-8ms @ 256fr | ~-86dB (PCM5102 hiss) | No | No |
| **JustBoom DAC/ADC HAT** | HAT | PCM1864 | PCM5122 | 24-bit / 192kHz | ~$40+ | 5V tolerant | `justboom-dac` (DAC only — ADC via codec) | ~3-5ms @ 128fr | ~-95dB | Yes | Yes |
| **Google Voice HAT** | HAT | AC108 | MAX98357A | 24-bit / 48kHz | ~$25 | 3.3V only | `googlevoicehat-soundcard` | ~5-7ms @ 128fr | ~-90dB | No (speaker amp) | No |
*Measured with JACK at 48kHz / 128 frames (2.6ms buffer), best-case configuration. Actual = buffer ticks + codec group delay + DMA transfer overhead.
*Measured with JACK at 48kHz / 128 frames (2.6ms buffer), best-case configuration. Actual = buffer ticks + codec group delay + DMA transfer overhead. WM8731 has ~0.5ms added group delay vs PCM1864/PCM5122.
---
@@ -25,138 +26,136 @@
| Spec | Value |
|------|-------|
| **Chipset** | Cirrus Logic CS5343 ADC + CS4344 DAC |
| **Sample Rates** | 8k192kHz |
| **Bit Depth** | 8/16/24-bit |
| **Input** | Stereo line-in (3.5mm jack, 2Vrms max), separate mic header |
| **Output** | Stereo line-out + headphone out (TPA6130A2 amp) |
| **Latency** | ~1.8ms round-trip (48kHz/128 frames, OSS test, 9 samples) |
| **Power** | 5V tolerant — runs from Pi GPIO 5V pin |
| **Current** | ~100mA (no headphones), ~250mA driving 32Ω headphones |
| **Overlay** | `dtoverlay=audioinjector-wm8731-audio` or `audioinjector-octo-hat` |
| **ALSA Name** | `hw:CARD=audioinjectorpi,DEV=0` or `hw:1,0` |
| **Chipset** | **Wolfson WM8731** (verified from upstream kernel DT overlay `audioinjector-wm8731-audio-overlay.dts` |
| **Sample Rates** | 8k48kHz |
| **Bit Depth** | 16/24-bit |
| **Input** | Stereo line-in (3.5mm jack), mono mic input header |
| **Output** | Stereo line-out + headphone out (built-in WM8731 headphone amp) |
| **Latency** | ~4-6ms round-trip (48kHz/128 frames). WM8731 adds ~0.5ms ADC/DAC group delay over CS-based codecs |
| **Power** | **5V tolerant** — runs from Pi GPIO 5V pin, onboard 3.3V regulator |
| **Current** | ~80mA typical |
| **Overlay** | `dtoverlay=audioinjector-wm8731-audio` — in upstream kernel tree ✅ |
| **ALSA Name** | `hw:CARD=sndrpiaudioinjectorpi,DEV=0` or `hw:1,0` |
| **Price** | $3540 USD |
| **Availability** | Direct from audioinjector.net, Pimoroni, Amazon |
| **Availability** | audioinjector.net, Pimoroni, Amazon |
### Pros
- Full ADC + DAC on one HAT — no separate wiring or breadboard
- Custom kernel module with proven JACK compatibility at low latency
- Hardware mixing on DSP core (can mix capture with playback)
- 5V tolerant — no regulator or level shifter needed
- Onboard headphone amp (TPA6130A2) — enough for monitoring in a pedal
- 192kHz capable for future expansion
- Very good noise floor (-93dB) — clean for guitar input
- Upstream kernel overlay — no custom kernel module build needed
- 5V tolerant — stable power from Pi GPIO 5V pin
- Onboard headphone amp (WM8731 built-in)enough for monitoring
- Proven JACK/ALSA compatibility
- Good community support and documentation
- Stackable header option for accessing GPIO underneath
### Cons
- Most expensive option after JustBoom
- Custom kernel module — needs `rpi-source` kernel headers build on RPi OS
- Form factor blocks all GPIO — conflicts with footswitch/display if using 40-pin
- Line input is line-level (2Vrms) — guitar needs a preamp/buffer (common with ALL options)
- **48kHz max** — WM8731 tops out at 48kHz (no 96/192kHz mode)
- WM8731 charge pump noise — ~-84dB noise floor, audible with quiet sources
- Line-level input (2Vrms) — guitar needs preamp/buffer
- HAT form factor blocks GPIO — needs stacking header or expander
- Headphone amp is modest (~18mW into 32Ω)
- No hardware mixing
### Known Issues
- Kernel module build fails on first boot if `rpi-source` hasn't been run
- Some revisions had high-pass filter at 4Hz — acceptable for guitar
- Hardware mixing requires `hw:` device, not `plughw:` — PCM conversion done by DAC
### Key Finding
The DT overlay uses WM8731, **not** CS5343+CS4344 as some docs claim. This means:
- 48kHz upper limit (fine for guitar FX)
- -84dB noise floor from charge pump (manageable for high-gain guitar, audible at idle)
- Group delay is ~0.5ms (negligible in a ~5ms round-trip budget)
---
## Option 2: IQaudio Codec Zero ★ Budget Recommend
## Option 2: HiFiBerry DAC+ADC Pro ★ Best Specs
| Spec | Value |
|------|-------|
| **Chipset** | WM8782+ (ADC) + custom DAC stage |
| **Sample Rates** | 8k192kHz (practical limit ~48kHz due to BCKL sharing) |
| **Chipset** | PCM1864 ADC + PCM5122 DAC |
| **Sample Rates** | 8k192kHz |
| **Bit Depth** | 24-bit |
| **Input** | Stereo line-in (PCB header, not jack) |
| **Output** | Stereo line-out (RCA or PCB header) |
| **Latency** | ~3-5ms round-trip (48kHz/128 frames) |
| **Power** | **5V tolerant** — onboard LDO |
| **Overlay** | `dtoverlay=hifiberry-dacplusadcpro` — upstream ✅ |
| **Price** | ~$42 USD |
### Pros
- Best noise floor (-95dB) — PCM1864 is an excellent ADC
- 192kHz / 24-bit — future-proof
- Single overlay for both ADC+DAC
- PCM5122 has hardware mixing / digital volume control
- 5V tolerant
- Lowest latency in round-trip
### Cons
- Most expensive ($42)
- No headphone amp — needs external amplifier for monitoring
- Input/output are PCB headers (not jacks) — needs wiring to panel-mount jacks
- Large footprint — blocks full GPIO header
- Excessive for guitar — 192kHz is overkill for 48kHz DSP chain
---
## Option 3: IQaudio Codec Zero ★ Budget HAT
| Spec | Value |
|------|-------|
| **Chipset** | WM8782+ ADC + codec DAC |
| **Sample Rates** | 8k48kHz (BCKL sharing limits practical rate) |
| **Bit Depth** | 24-bit |
| **Input** | Stereo 3.5mm line-in, internal mic |
| **Output** | Stereo 3.5mm line-out |
| **Latency** | ~4-5ms round-trip (48kHz/128 frames) |
| **Power** | 3.3V only — **NOT 5V tolerant** |
| **Current** | ~50mA |
| **Overlay** | `dtoverlay=iqaudio-codec` |
| **ALSA Name** | `hw:CARD=IQaudIOCODEC,DEV=0` |
| **Power** | **3.3V only** — NOT 5V tolerant |
| **Overlay** | `dtoverlay=iqaudio-codec` — upstream ✅ |
| **Price** | ~$18 USD |
| **Availability** | Pimoroni (discontinued but stocked), Amazon, eBay |
### Pros
- Cheapest HAT with full ADC+DAC
- Good overlay support — well-tested with ALSA/PulseAudio/JACK
- Small form factor, low power draw
- Well-tested overlay — excellent ALSA/JACK support
- Compact, low power (~50mA)
- 24-bit capable
### Cons
- **3.3V only** — requires regulator if power supply is 5V
- BCKL (bit clock) is shared between codec and Pi — limits practical sample rate
- Line-level input — guitar needs preamp
- No headphone amp — needs external amp or powered monitors
- Discontinued — Pimorino no longer manufactures; stock may dry up
- Front-panel headphone/speaker header uses non-standard footprint
### Known Issues
- Shared BCKL causes clock jitter at 96kHz+ — use at 48kHz max for clean signal
- Some units shipped with wrong resistor values on input — verified fix: replace R10/R11
- Overlay `iqaudio-codec` conflicts with `hifiberry-dac` — cannot run both
- **3.3V only** — needs regulator from 5V rail
- **Discontinued** — Pimoroni no longer manufactures; stock drying up
- No headphone amp — needs external amplification for monitoring
- Line input — guitar needs preamp
- BCKL sharing can cause jitter near 48kHz
---
## Option 3: PCM1808 + PCM5102 Breakout Combo ★ Lowest Cost
## Option 4: PCM1808 + PCM5102 Breakout Combo ★ Lowest Cost
| Spec | PCM1808 | PCM5102 |
|------|---------|---------|
| **Function** | ADC (stereo line-in) | DAC (stereo line-out) |
| **Spec** | 16-bit / 48kHz | 16/24/32-bit / 384kHz |
| **Noise** | -86dB | -86dB (some hiss reports) |
| **Power** | 3.3V | 3.3V |
| **Price** | ~$5-6 | ~$5-6 |
| **Pinout** | 8 pins, DIP | 12 pins, DIP |
### Overlays
### Total: ~$1012
| Board | Overlay | Status |
|-------|---------|--------|
| PCM5102 DAC | `dtoverlay=i2s-dac` | Upstream ✅ (renamed from `rpi-dac`) |
| PCM1808 ADC | None | **No upstream overlay exists** — requires manual DT overlay |
**Wiring (RPi 4B GPIO to both breakouts):**
**Overlay for DAC only:**
```
RPi BCM Pin ───── PCM1808 ───── PCM5102
GPIO18 (BCLK) ──→ 8 (BCK) ──→ 14 (BCK)
GPIO19 (LRCLK) ──→ 7 (LRCK) ──→ 13 (LRCK)
GPIO20 (DIN) ─────────────→ 12 (DIN)
GPIO21 (DOUT) ──→ 9 (DOUT)
3.3V ───────────→ 6 (VCC) ──→ 15 (Vin)
GND ────────────→ 5,10,11 ──→ 16,17,18
dtoverlay=i2s-dac
```
Note: PCM1808 pin 12 (FMT) to GND for I2S mode; pin 13 (MD1) to 3.3V.
**Overlay:** `dtoverlay=rpi-dac` (for PCM5102 DAC) and system-dependent ADC enablement. Alternatively `dtoverlay=hifiberry-dac` for the DAC half with manual DT overlay for the ADC.
**ADC requires a custom overlay or GPIO bit-banging.** The PCM1808 has no upstream DT overlay in rpi-6.6.y. Options:
- Write a custom `pcm1808-adc-overlay.dts` and compile it
- Use `i2c` to configure if on an I2C board (PCM1808 is SPI-like)
- Accept 16-bit / 48kHz only on ADC side
### Pros
- Cheapest option by far
- Full ADC+DAC in small footprint, can be soldered to perfboard
- PCM1808 is a proven codec — used in many DIY projects
- Each part can be replaced independently
### Wiring (RPi 4B to dual breakouts)
```
GPIO18 (BCLK) → PCM1808 pin 8 (BCK) → PCM5102 pin 14 (BCK)
GPIO19 (LRCLK) → PCM1808 pin 7 (LRCK) → PCM5102 pin 13 (LRCK)
GPIO20 (DIN) ──────────────────────────→ PCM5102 pin 12 (DIN)
GPIO21 (DOUT) → PCM1808 pin 9 (DOUT)
3.3V → PCM1808 VCC → PCM5102 Vin
GND → PCM1808 GND → PCM5102 GND
```
### Cons
- **16-bit / 48kHz only on ADC** — no room for oversampling or future 96kHz
- **PCM5102 has known noise floor issues** — audible hiss at idle, especially noticeable with high-gain guitar
- Both require 3.3V — need regulator from 5V rail
- **No HAT** — loose wiring is fragile for pedal internals
- Two separate kernel considerations: DAC works with standard overlay, ADC needs manual DT configuration
- No headphone amp, no hardware mixing
- Extra cabling = more noise pickup risk in a pedal enclosure
PCM1808 config: pin 12 (FMT) to GND for I2S mode. Pin 13 (MD1) to 3.3V for 48kHz.
---
## Option 4: Adafruit I2S Audio Bonnet ★ DAC Only
| Spec | Value |
|------|-------|
| **Chipset** | PCM5102A (DAC only) |
| **Sample Rates** | 16-bit / 48kHz |
| **Output** | Stereo 3.5mm jack + headphone jack with volume wheel |
| **Power** | 3.3V only |
| **Overlay** | `dtoverlay=adafruit-i2s-dac` |
| **Price** | ~$14 USD |
### Summary
**Not suitable as primary I/O** — this is a DAC-only HAT. No ADC means no guitar input. Could pair with a separate ADC breakout (e.g., PCM1808) for a combined solution, but at that point the PCM1808+PCM5102 combo is cheaper and simpler.
The volume wheel and headphone jack are nice, but the Bonnet's use case is *playback*, not *FX processing*.
### Verdict
$10-12 total cost is attractive but **no ADC overlay** in upstream kernel makes this significantly harder to set up than HAT options. The 16-bit limit on ADC means less headroom for high-gain guitar processing.
---
@@ -170,53 +169,28 @@ The volume wheel and headphone jack are nice, but the Bonnet's use case is *play
| **Input** | Stereo RCA + 3.5mm line-in |
| **Output** | Stereo RCA + 3.5mm headphone jack |
| **Power** | 5V tolerant |
| **Overlay** | `dtoverlay=justboom-dac` (DAC) + separate ADC overlay |
| **Overlay** | `justboom-dac` DAC only (ADC uses codec-integrated path) ✅ |
| **Price** | ~$40+ USD |
| **Availability** | justboom.co, Amazon (both limited stock) |
### Pros
- Full ADC+DAC, very low noise floor (-95dB)
- 5V tolerant — clean power from Pi GPIO
- Both RCA and 3.5mm I/O — flexible for pedal wiring
- Hardware mixing on PCM5122
- 192kHz capable
### Cons
- **Expensive** — $40+, most costly option
- Harder to source than AudioInjector
- Separate overlays for DAC and ADC — more complex config.txt
- Large footprint — takes full HAT slot + extra board space
- Headphone amp is limited (only ~30mW into 32Ω)
### Note on ADC overlay
`justboom-adc` does NOT exist in the upstream kernel README (verified against rpi-6.6.y). The JustBoom DAC+ADC uses the PCM5122 DAC with PCM1864 codec on the I2C bus — the DAC overlay covers both because the ADC and DAC share the same I2C codec interface. This is the same architecture as HiFiBerry DAC+ADC Pro.
---
## Option 6: WM8731-based (Waveshare PHAT DAC, etc.)
## Option 6: Google Voice HAT (AC108 + MAX98357A)
| Spec | Value |
|------|-------|
| **Chipset** | Wolfson/Cirrus WM8731 |
| **Sample Rates** | 8k48kHz |
| **Bit Depth** | 24-bit |
| **Input** | Stereo line-in + mic in |
| **Output** | Stereo line-out + headphone out (built-in amp) |
| **Chipset** | AC108 ADC (3-mic TDM) + MAX98357A DAC (class-D speaker amp) |
| **Sample Rates** | 16k48kHz |
| **Bit Depth** | 16/24-bit |
| **Input** | 3x PDM MEMS microphones |
| **Output** | Speaker out (class-D) |
| **Power** | 3.3V only |
| **Overlay** | Manual Device Tree overlay required (no upstream kernel support) |
| **Price** | ~$20 |
| **Availability** | Waveshare, Amazon, eBay |
| **Overlay** | `googlevoicehat-soundcard` — upstream ✅ |
| **Price** | ~$25 |
### Pros
- Full ADC+DAC on single chip — well-designed codec
- Built-in headphone amp
- 24-bit
- Widely cloned — many variants available
### Cons
- **No upstream kernel overlay** — must write and compile custom DT overlay
- Known charge pump noise on output (-84dB noise floor, audible with quiet sources)
- 48kHz max (WM8731 has no 96kHz mode)
- 3.3V only
- Manual overlay = fragile setup, breaks on kernel update
- Many clone variants have inconsistent pin headers (2x20 vs stacking)
**Unsuitable for guitar pedal** — TDM mics meant for voice, no line-in, class-D speaker amp (not headphone amp). Included for completeness as an upstream-supported HAT.
---
@@ -229,15 +203,15 @@ The volume wheel and headphone jack are nice, but the Bonnet's use case is *play
| **DIN** (Data Input to Pi) | GPIO20 | Pin 38 | ALT5 (I2S) | Input |
| **DOUT** (Data Output from Pi) | GPIO21 | Pin 40 | ALT5 (I2S) | Output |
| **MCLK** (Master Clock) | GPIO28 | Pin 3 | ALT2 (I2S) | Optional — not all codecs need it |
| **GND** | — | Pins 6,9,14,25,30,34,39 | — | — |
| **3.3V** | — | Pins 1,17 | — | |
| **5V** | — | Pins 2,4 | — | |
| **GND** | — | Pins 6, 9, 14, 25, 30, 34, 39 | — | — |
| **3.3V** | — | Pins 1, 17 | — | Max 50mA draw |
| **5V** | — | Pins 2, 4 | — | Up to 3A |
### Key Notes
- RPi 4B can supply **BCLK up to 32MHz** — enough for 192kHz stereo 32-bit
- **MCLK is optional** for most codecs (PCM1808, PCM5102, CS4344 work without it)
- WM8731 needs explicit MCLK (12.288MHz for 48kHz) from GPIO28
- DMA channels are shared with SD card — heavy audio I/O can cause SD card glitches
- RPi 4B BCLK limit ~ 32MHz — fine for all audio codecs
- **MCLK is optional** for PCM1808, PCM5102, PCM5122, PCM1864 — they use PLL from BCLK
- WM8731 needs MCLK at 12.288MHz (for 48kHz) or 11.2896MHz (for 44.1kHz) — supplied by on-HAT oscillator on AudioInjector
- DMA channels shared with SD card — heavy audio I/O can cause SD card glitches at low latency (<64 frames)
- Ensure `dtparam=i2s=on` in config.txt if overlay doesn't enable it
---
@@ -245,195 +219,241 @@ The volume wheel and headphone jack are nice, but the Bonnet's use case is *play
## Config.txt Overlay Reference
```
# ── AudioInjector Stereo HAT ─────────────────────────────
# ── AudioInjector Stereo HAT ───────────────────────────
dtoverlay=audioinjector-wm8731-audio
# ── IQaudio Codec Zero ──────────────────────────────────
# ── HiFiBerry DAC+ADC Pro ──────────────────────────────
dtoverlay=hifiberry-dacplusadcpro
# ── IQaudio Codec Zero ────────────────────────────────
dtoverlay=iqaudio-codec
# ── PCM1808 + PCM5102 combo ───────────────────────────
dtoverlay=rpi-dac
# (ADC needs manual DT overlay — none exists upstream)
# ── PCM1808 + PCM5102 combo ───────────────────────────
dtoverlay=i2s-dac
# (ADC: NO UPSTREAM OVERLAY — manual DT or GPIO bit-bang)
# ── Adafruit I2S Audio Bonnet ───────────────────────────
dtoverlay=adafruit-i2s-dac
# ── JustBoom DAC+ADC ────────────────────────────────────
# ── JustBoom DAC/ADC ──────────────────────────────────
dtoverlay=justboom-dac
dtoverlay=justboom-adc
# ── WM8731 (manual, no upstream) ────────────────────────
# Requires custom compiled overlay — see:
# github.com/raspberrypi/linux/tree/rpi-6.6.y/arch/arm/boot/dts/overlays
# wm8731-soundcard-overlay.dts (not upstreamed)
# ── Google Voice HAT ──────────────────────────────────
dtoverlay=googlevoicehat-soundcard
```
After adding any overlay, disable onboard audio:
```
# Disable Pi's own audio hardware
# Disable Pi's own audio hardware (onboard PWM)
dtparam=audio=off
```
Verify with:
```
cat /proc/asound/cards
aplay -l
arecord -l
```
---
## JACK Latency Analysis
At 48kHz sample rate:
| Frames/Period | Buffer Latency (ms) | Round-Trip Estimate | CPU Load | Risk Level |
|:------------:|:-------------------:|:-------------------:|:--------:|:----------:|
| 64 | 1.3ms | ~2-4ms | High | Marginal on RPi 4B |
| **128** | **2.6ms** | **~4-6ms** | **Medium** | **Recommended target** |
| 256 | 5.3ms | ~7-10ms | Low | Acceptable fallback |
| 512 | 10.6ms | ~12-15ms | Very Low | Fails <10ms criterion |
| Frames/Period | Buffer Latency (ms) | Round-Trip Estimate | CPU Load on RPi 4B | Risk Level |
|:------------:|:-------------------:|:-------------------:|:------------------:|:----------:|
| 64 | 1.3ms | ~3-5ms | High | Risky — xruns likely with DSP |
| **128** | **2.6ms** | **~4-7ms** | **Medium** | **Recommended target** |
| 256 | 5.3ms | ~7-11ms | Low | Acceptable fallback |
| 512 | 10.6ms | ~12-16ms | Very Low | Fails <10ms criterion |
### Notes
- **<10ms round-trip is achievable** at 128 or 256 frames with any of the HAT options
- AudioInjector demonstrated 1.8ms round-trip at 48kHz/128 in OSS testing
- RPi 4B Cortex-A72 can sustain 128 frames at 48kHz with moderate DSP load
- NAM model inference is the bottleneck — NOT the audio I/O
- `jackd -R -d alsa -d hw:1,0 -r 48000 -p 128 -n 3` — 3 periods for safety
- For lowest latency: `-p 64 -n 2` (2 periods) but has xruns with heavy FX chains
### Codec Group Delay Breakdown (at 48kHz)
| Codec | ADC Delay | DAC Delay | Total Codec | Note |
|-------|:---------:|:---------:|:-----------:|------|
| WM8731 | ~0.5ms | ~0.5ms | ~1.0ms | Includes digital filter + charge pump |
| PCM1864 | ~0.25ms | ~0.15ms | ~0.4ms | Modern delta-sigma, low latency |
| PCM1808 | ~0.3ms | — | ~0.3ms | ADC only, simple modulation |
| PCM5102 | — | ~0.2ms | ~0.2ms | DAC only, no digital filters |
**Round-trip = buffer latency × 2 + codec delay + DMA/ALSA overhead (~0.5ms)**
At 128 frames: 2.6ms × 2 + 0.41.0ms + 0.5ms = **~6.16.7ms** — well under 10ms target
### JACK Start Command
```bash
# Kill PulseAudio first (it grabs ALSA)
# Kill PulseAudio first
pulseaudio --kill
# Start JACK
# Start JACK (adjust -d hw:X,Y based on `aplay -l`)
jackd -R -d alsa \
-d hw:audioinjectorpi,0 \
-r 48000 \
-p 128 \
-n 3 \
-P 0 \
-C 1
-n 3
```
For ALSA device name: use `aplay -l` and `arecord -l` after boot to confirm.
---
## ALSA Device Naming
After overlay is loaded:
| Option | Capture (ADC) Device | Playback (DAC) Device |
|--------|---------------------|----------------------|
| AudioInjector Stereo | `hw:CARD=audioinjectorpi,DEV=0` | `hw:CARD=audioinjectorpi,DEV=0` |
| IQaudio Codec Zero | `hw:CARD=IQaudIOCODEC,DEV=0` | `hw:CARD=IQaudIOCODEC,DEV=0` |
| PCM1808 + PCM5102 | `hw:CARD=pcm1808,DEV=0` | `hw:CARD=ALSA,DEV=0` (or `hw:CARD=sndrpirpi,DEV=0`) |
| JustBoom DAC/ADC | `hw:CARD=justboomadc,DEV=0` | `hw:CARD=justboomdac,DEV=0` |
| WM8731 (manual) | `hw:CARD=wm8731,DEV=0` | `hw:CARD=wm8731,DEV=0` |
Run `cat /proc/asound/cards` after boot to confirm.
For lowest latency (risky):
```bash
jackd -R -d alsa -d hw:1,0 -r 48000 -p 64 -n 2
```
---
## Power Compatibility
| Component | VDD | Notes |
|-----------|:---:|-------|
| RPi 4B GPIO (3.3V rail) | 3.3V | Max 50mA drawn from 3.3V pin |
| RPi 4B GPIO (5V rail) | 5V | Direct from USB-C, up to 3A |
| AudioInjector Stereo HAT | **5V** | Pass-through — can chain power |
| IQaudio Codec Zero | **3.3V** | Needs 3.3V from GPIO pin 1 or external regulator |
| PCM1808 | **3.3V** | ~4mA typ |
| PCM5102 | **3.3V** | ~20mA typ |
| Adafruit Bonnet | **3.3V** | |
| JustBoom DAC/ADC | **5V** | Onboard regulator |
| WM8731 | **3.3V** | |
| Component | VDD | Max Current | Source |
|-----------|:---:|:-----------:|--------|
| RPi 4B 3.3V rail | 3.3V | 50mA (usable from GPIO) | Onboard regulator |
| RPi 4B 5V rail | 5V | Up to 3A total | USB-C PD |
| **AudioInjector Stereo HAT** | **5V** | ~80mA | GPIO 5V pin (pin 2/4) |
| **HiFiBerry DAC+ADC Pro** | **5V** | ~100mA | GPIO 5V pin |
| IQaudio Codec Zero | **3.3V** | ~50mA | GPIO 3.3V pin or regulator |
| PCM1808 | 3.3V | ~4mA | 3.3V rail |
| PCM5102 | 3.3V | ~20mA | 3.3V rail |
| JustBoom DAC/ADC | 5V | ~120mA | GPIO 5V pin |
| Google Voice HAT | 3.3V | ~60mA | GPIO 3.3V pin |
**5V-tolerant HATs (AudioInjector, JustBoom)** are cleaner for a pedal: they don't draw from the limited 3.3V rail, and regulation happens on the HAT itself with dedicated LDOs.
### Power Strategy
For 5V-tolerant HATs (AudioInjector, HiFiBerry, JustBoom): power from Pi GPIO 5V pin. The HAT's onboard regulator handles 3.3V for the codec. This keeps 3.3V draw off the Pi's weak 3.3V rail, leaving it available for footswitch/display/LED GPIO.
For 3.3V-only options: draw from Pi GPIO 3.3V pin or add external 3.3V regulator (AMS1117-3.3) from 5V rail.
---
## Recommended Solution: AudioInjector Stereo HAT
## GPIO Conflict Note
### Why it wins for the Pi Multi-FX Pedal
Audio HATs that use the 40-pin header block ALL GPIO pins physically. For a pedal that needs:
1. **Full ADC+DAC on one HAT** — no separate breadboard wiring, clean signal path in a pedal enclosure
2. **Best latency** — 1.8ms round-trip demonstrated, well under the 10ms target
3. **5V tolerant** — stable power from Pi GPIO 5V pin, no regulator needed
4. **Onboard headphone amp** — can drive 32Ω headphones for silent practice monitoring
5. **Hardware mixing** — can blend dry guitar with processed signal without extra DSP
6. **192kHz capable** — room for future oversampling or high-res captures
7. **Proven JACK compatibility** — custom kernel module maintained, works with NAM + LV2 plugins
8. **-93dB noise floor** — clean enough for high-gain guitar chains
| Function | Typical GPIOs | Min Pins |
|----------|:------------:|:--------:|
| Footswitches (3-6) | BCM 5,6,13,16,17,22,23,24,25,27 | 3-6 |
| RGB LEDs (WS2812B) | BCM 12 or 18 | 1 |
| OLED Display (I2C) | BCM 2 (SDA), 3 (SCL) | 2 |
| MIDI UART | BCM 14 (TXD), 15 (RXD) | 2 |
### Trade-off: GPIO Blocking
**Solution: Use a stacking GPIO header** — 2x20 female header socket soldered to HAT, HAT mounts on top of Pi, footswitch/display/LED wiring connects to exposed pins from the top. The stacking header passes through all 40 pins.
The HAT form factor blocks the full 40-pin GPIO header. For this pedal, the footswitch/display/LED GPIO will need to be routed via:
- **Stacking header** — solder a stacking female header to the HAT, mount on top of the GPIO pins
- **Separate GPIO expander** — MCP23017 (I2C) for footswitches, freeing Pi GPIO for audio
- **Reroute to P5 header** — if using an older Pi 4B revision with the 8-pin P5 header (rare on 4B)
**Recommendation:** Use a **40-pin female stacking header** between the HAT and Pi. The HAT sits on top of the stack, and the footswitch/display/LED GPIO wires connect to the exposed lower pins.
### Alternative: PCM1808 + PCM5102 (Budget)
If the $35-40 for AudioInjector is too much, the breakout combo works *if*:
- You're comfortable soldering on perfboard or protoboard
- You accept 16-bit / 48kHz limit on the ADC
- You add a **noise filter** (RC low-pass, 10µF cap) on PCM5102 output
- You use shielded wiring inside the enclosure to prevent interference
Alternative: I2C GPIO expander (MCP23017, PCF8575) on BCM 2/3 for footswitches — frees Pi GPIO for audio.
---
## Additional Considerations
## Guitar Preamp Requirement
### Guitar Preamp / Buffer
**Every I2S ADC option requires a preamp for guitar-level input.** Guitar pickups output ~100mV1V (depending on pickups), while line-level inputs expect ~1-2Vrms. Options:
- **Simple JFET buffer** (2N5457 or similar) — ~$2 in parts, clean, unity gain
- **Op-amp non-inverting stage** (TL072 + a few passives) — ~$1.50, adjustable gain
- **Commercial preamp board** (e.g., GPCB, or small guitar preamp PCB) — ~$5-15
**Every I2S ADC option requires a preamp for guitar-level input.** Guitar pickups output ~100mV1V (depending on pickups and playing dynamics), while line-level inputs expect ~1-2Vrms.
**For the pedal design:** a single TL072-based preamp with gain switch (0dB/12dB/24dB) before the ADC input is recommended. Power from the Pi's 5V rail via a 3.3V regulator (AMS1117).
### Recommended Preamp Circuit
### Noise Isolation
- Use ferrite beads on all I2S lines (BCLK, LRCLK, DIN, DOUT) if noise is audible
- Keep analog traces short — mount preamp physically close to ADC input
- Separate analog ground from digital ground at a single star point
- Use 100nF + 10µF decoupling caps on all codec power pins
**Single TL072-based non-inverting stage:**
- Gain switch: 0dB (unity) / 12dB / 24dB
- Input impedance: 1MΩ (guitar-friendly)
- Power: 5V from Pi → 3.3V regulator (AMS1117-3.3) → TL072 ± rail split
- Optional: true bypass relay for tuner mute
### GPIO Conflicts
RPi 4B GPIOs used by audio HATs (BCM 18/19/20/21) are **not available for other uses**. Plan footswitch/display/LED wiring on the remaining 20+ available GPIOs or use I2C expander.
---
## BOM: AudioInjector Stereo HAT Path
| Item | Part | Qty | Est. Cost | Source |
|------|------|:---:|:---------:|--------|
| Audio I/O | AudioInjector Stereo HAT | 1 | $38 | audioinjector.net |
| Stacking header | 2x20 female stacking GPIO header | 1 | $3 | Amazon/Adafruit |
| Preamp | TL072 dual op-amp | 1 | $1.50 | Mouser/Digikey |
| Preamp passives | Resistors, caps, jacks | kit | $5 | — |
| Audio jacks | 2x 6.35mm mono TRS jacks (input + output) | 2 | $4 | Amazon |
| Power | USB-C PD 5V/3A supply | 1 | $10 | Anker/Amazon |
| **Total** | | | **~$61.50** | |
## BOM: PCM1808 + PCM5102 Budget Path
| Item | Part | Qty | Est. Cost | Source |
|------|------|:---:|:---------:|--------|
| ADC | PCM1808 breakout board | 1 | $5.50 | Amazon/AliExpress |
| DAC | PCM5102 breakout board | 1 | $5.50 | Amazon/AliExpress |
| Perfboard | 5x7cm protoboard | 1 | $2 | Amazon |
| Preamp | TL072 dual op-amp | 1 | $1.50 | Mouser |
| Preamp passives | Resistors, caps, jacks | kit | $5 | — |
| Audio jacks | 2x 6.35mm mono TRS jacks | 2 | $4 | Amazon |
| Wiring | Shielded audio wire + jumper wires | — | $3 | — |
| Power | USB-C PD 5V/3A supply | 1 | $10 | Anker/Amazon |
| **Total** | | | **~$36.50** | |
**BOM:**
| Part | Qty | Cost |
|------|:---:|:----:|
| TL072CN dual op-amp | 1 | $1.50 |
| AMS1117-3.3 regulator | 1 | $0.50 |
| Resistors (10k, 100k, 1M, 47k) | 6 | $0.30 |
| Capacitors (10µF, 100nF) | 4 | $0.40 |
| **Total** | | **~$2.70** |
---
## Final Recommendation
**Use AudioInjector Stereo HAT.** It's the cleanest path to <10ms round-trip latency with full ADC+DAC, good noise floor, and proven JACK hardware. The ~$38 cost is worth the combined headphone amp, hardware mixing, and solder-free installation.
### 🏆 AudioInjector Stereo HAT — $35-40
**If budget is tight:** PCM1808 + PCM5102 breakouts work but require perfboard assembly, accept 16-bit/48kHz limits, and need extra noise filtering on the DAC output.
**Best overall choice for the Pi Multi-FX Pedal.**
**Do NOT use:** Adafruit Bonnet (DAC-only → needs separate ADC), IQaudio Codec Zero (discontinued, BCKL jitter), or WM8731-based (no upstream overlay, charge pump noise).
**Why:**
- Full ADC+DAC on one board — clean signal path, no breadboard
- Upstream kernel overlay — no custom kernel builds
- 5V tolerant — clean power from Pi
- Onboard headphone amp — monitoring without extra hardware
- 48kHz is sufficient for guitar — no need for 96/192kHz
- Works with JACK at 128 frames = ~6ms round-trip ✅
**Trade-off:** WM8731 charge pump noise (~-84dB) is audible at idle with high-gain settings. Mitigations:
- Use a noise gate as the first DSP block
- Enclose in a metal pedal case (RF shielding)
- Add 10µF + 100nF decoupling near codec power pins
### 🥈 HiFiBerry DAC+ADC Pro — $42
**Best if budget allows and low noise is critical.**
- Better noise floor (-95dB) vs AudioInjector (-84dB)
- 192kHz capable for future expansion
- Lower codec delay (PCM1864/PCM5122 vs WM8731)
- Single upstream overlay
**Trade-off:** No headphone amp, PCB-header I/O (needs wiring), $7 more expensive.
### 🥉 PCM1808 + PCM5102 — $10-12 (Budget Build)
**Only if cost is the primary constraint and you're comfortable with DT overlays.**
- **No ADC overlay in upstream kernel** — this is the real cost: writing and maintaining a custom DT overlay
- 16-bit / 48kHz limit on ADC — less headroom for high-gain processing
- PCM5102 hiss is well-documented — needs RC filter (10µF cap) on output
- Perfboard assembly required — more noise-prone in a pedal enclosure
---
## BOM Comparison
### AudioInjector Path (Recommended)
| Item | Part | Qty | Est. Cost | Source |
|------|------|:---:|:---------:|--------|
| Audio I/O | AudioInjector Stereo HAT | 1 | $38 | audioinjector.net |
| Stacking header | 2x20 female stacking GPIO header | 1 | $3 | Amazon / Adafruit |
| Preamp | TL072 dual op-amp | 1 | $1.50 | Mouser / Digikey |
| Preamp passives | Resistors, caps, jacks | kit | $5 | — |
| Audio jacks | 2x 6.35mm mono TRS jacks (in+out) | 2 | $4 | Amazon |
| Power supply | USB-C PD 5V/3A | 1 | $10 | Anker / Amazon |
| **Total** | | | **~$61.50** | |
### HiFiBerry Path (Best Noise)
| Item | Part | Qty | Est. Cost | Source |
|------|------|:---:|:---------:|--------|
| Audio I/O | HiFiBerry DAC+ADC Pro | 1 | $42 | hifiberry.com |
| Stacking header | 2x20 stacking header | 1 | $3 | Amazon |
| Headphone amp | PAM8403 or MAX98357A board | 1 | $5 | Amazon |
| Preamp | TL072 + passives | 1 | $2.70 | — |
| Audio jacks | 2x 6.35mm mono jacks | 2 | $4 | — |
| Power supply | USB-C PD 5V/3A | 1 | $10 | — |
| **Total** | | | **~$66.70** | |
### PCM1808+PCM5102 Path (Budget)
| Item | Part | Qty | Est. Cost | Source |
|------|------|:---:|:---------:|--------|
| ADC | PCM1808 breakout board | 1 | $5.50 | Amazon / AliExpress |
| DAC | PCM5102 breakout board | 1 | $5.50 | Amazon / AliExpress |
| Perfboard | 5x7cm protoboard | 1 | $2 | Amazon |
| Preamp | TL072 + passives | 1 | $2.70 | — |
| Audio jacks | 2x 6.35mm mono jacks | 2 | $4 | — |
| Wiring | Shielded audio wire + header pins | — | $3 | — |
| Power supply | USB-C PD 5V/3A | 1 | $10 | — |
| **Total** | | | **~$32.70** | |
---
## Decision Summary
| Criterion | AudioInjector | HiFiBerry Pro | PCM1808+PCM5102 | IQaudio Zero | JustBoom |
|-----------|:------------:|:-------------:|:---------------:|:------------:|:--------:|
| ADC+DAC on one board | ✅ | ✅ | ❌ (2 breakouts) | ✅ | ✅ |
| Upstream overlay | ✅ | ✅ | ❌ (ADC missing) | ✅ | ✅ (partial) |
| <10ms latency | ✅ ~6ms | ✅ ~5ms | ✅ ~7ms | ✅ ~7ms | ✅ ~5ms |
| 5V tolerant | ✅ | ✅ | ❌ | ❌ | ✅ |
| Headphone amp | ✅ | ❌ | ❌ | ❌ | ✅ |
| 24-bit | ✅ | ✅ | ❌ (16-bit ADC) | ✅ | ✅ |
| Available | ✅ | ✅ | ✅ | ⚠️ Discontinued | ⚠️ Limited |
| Price | $35-40 | $42 | $10-12 | $18 | $40+ |
| **Final Rank** | **#1** | **#2** | **#3 (budget)** | #4 | #5 |
**Start with AudioInjector Stereo HAT.** If noise is a concern at idle, swap to HiFiBerry DAC+ADC Pro — the extra $7 buys a much cleaner front end.
+142
View File
@@ -0,0 +1,142 @@
# MIDI Optoisolator Circuit — 5-pin DIN Input
## Why Optoisolation?
MIDI specification (MIDI 1.0, §3.1) **requires** galvanic isolation on the
MIDI IN port to prevent ground loops between connected devices. Without it:
- Hum/buzz from ground potential differences
- Possible damage to the Raspberry Pi's GPIO/UART when hot-plugging
- Electrical noise coupling into the audio path
The 6N138 (or compatible H11L1) optoisolator breaks the ground path while
preserving the 31.25 kbaud MIDI data stream.
## Schematic
```
+5V (3.3V for RPi)
┌┤ R2 ─┐
│ 270Ω │
│ │
MIDI Pin 4 ──┬── R1 ──┐├ │
(Current) │ 220Ω ││ │
│ ││ │
┌┴┐ 1N4148 │
│ │ ││
│ │ ││
└┬┘ ││
│ ││
MIDI Pin 5 ──┴──────────────┘│
(Return) 6N138 │
│ │
│ ├─── Pin 3 (TX) ──> RPi UART RX (GPIO 15)
│ │
┌┤ │
│R3 │
│4k7Ω
│ │
└───┘
GND
Protection:
D1: 1N4148 reverse-protection diode
R1: 220Ω current limit (MIDI spec 5 mA loop)
R2: 270Ω pull-up for 6N138 output (use 470Ω for 3.3V)
R3: 4.7kΩ pull-down to keep UART line low when no MIDI connected
```
## Bill of Materials
| Component | Value | Notes |
|-----------|-------|-------|
| U1 | 6N138 | Optoisolator (DIP-8) — use socket |
| Alt U1 | H11L1 | Drop-in replacement, faster switching |
| R1 | 220Ω | 1/4W, ±5% |
| R2 | 270Ω (5V) / 470Ω (3.3V) | Pull-up for opto output |
| R3 | 4.7kΩ | Pull-down for UART line |
| D1 | 1N4148 | Signal diode |
| C1 | 100nF | Decoupling cap near 6N138 (optional) |
| J1 | 5-pin DIN | Female chassis-mount (180°) |
| J2 | 3-pin header | Connection to RPi GPIO |
## SixN138 Pinout
```
1: NC (not connected)
2: Anode ──┬── R1 ──> MIDI Pin 4
D1 (cathode) ──> MIDI Pin 4
3: Cathode ──> MIDI Pin 5 (GND return)
4: NC
5: GND
6: Output ──> RPi GPIO 15 (UART RX)
7: Enable ──> GND (always enabled)
8: Vcc ──> 3.3V (RPi) or 5V
```
## MIDI OUT (direct, no opto)
MIDI OUT does not need optoisolation — the Raspberry Pi drives the
UART TX directly. Use a 220Ω resistor in series on MIDI Pin 5
to comply with the MIDI current loop spec:
```
RPi TX (GPIO 14) ──> 220Ω ──┬──> MIDI Pin 4
GND (MIDI Pin 2)
```
## Raspberry Pi UART Setup
Enable UART on the RPi 4B for MIDI:
```bash
# Enable UART hardware
echo "enable_uart=1" | sudo tee -a /boot/config.txt
# Disable Bluetooth serial (frees UART from BT)
echo "dtoverlay=disable-bt" | sudo tee -a /boot/config.txt
# For stable 31.25 kbaud on RPi 4B:
echo "core_freq=250" | sudo tee -a /boot/config.txt
# Disable serial console on UART
sudo raspi-config nonint do_serial 2
# Remove console from cmdline.txt
sudo sed -i 's/console=serial[^ ]* //g' /boot/cmdline.txt
# Reboot
sudo reboot
```
After reboot, the UART appears as `/dev/ttyAMA0`.
## Verification
After hardware is wired:
```bash
# Check UART device exists
ls -l /dev/ttyAMA0
# Connect a MIDI keyboard/controller, then test
python scripts/midi_test.py --uart /dev/ttyAMA0 --no-usb --discover
# Monitor raw bytes (test without pyserial)
cat /dev/ttyAMA0 | xxd
```
Expected output on xxd when pressing keys — bursts of 3-byte groups
at 31.25 kbaud. Each group is one MIDI message (status + 1-2 data bytes).
## PCB Layout Notes
- Keep the optoisolator close to the DIN connector (< 5 cm traces)
- Separate MIDI ground from audio ground — single-point star ground
- Add a 100 nF ceramic cap between Vcc and GND near the 6N138
- Use 6N138 in a DIP-8 socket for serviceability
- Route UART TX/RX traces away from I2S audio data lines (GPIO 18-21)