Files
shawn c38a7b0fd8 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
2026-06-07 23:39:50 -04:00

4.2 KiB

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:

# 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:

# 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)