pi-Stomp Developers Guide
Software/Firmware Architecture
Core Architecture Layers
1. Entry Point & Main Loop (`modalapistomp.py`)
- Main entry point that initializes the system
- Event loop with polling:
- Every cycle (10ms): `poll_controls()` — hardware inputs
- Every 2 cycles: `poll_indicators()` — LEDs/VU meters
- Every 20 cycles: `poll_lcd_updates()` — LCD refresh
- Every 100 cycles: `poll_modui_changes()` — UI sync
- Every 200 cycles: `poll_wifi()` — WiFi status
- Every 60 seconds: `poll_system_info()` — system state
2. Handler Layer (Abstract Interface)
- Base Handler (`pistomp/handler.py`): Abstract interface defining hardware interaction methods
- Modhandler (`modalapi/modhandler.py`): Primary implementation that:
- Manages pedalboards, presets, and plugins
- Communicates with mod-host via HTTP REST API
- Manages WiFi, system state, and LCD updates
- Uses a `Current` class to track active pedalboard state
- Mod (`modalapi/mod.py`): Legacy handler for v1 hardware
3. Hardware Abstraction Layer
- Hardware Base Class (`pistomp/hardware.py`): Abstract base for hardware variants
- Hardware Variants:
- `pistomp.py` — v1 hardware (3 footswitches, 1 pot, 1 expression pedal, 2 encoders)
- `pistompcore.py` — v2 hardware
- `pistomptre.py` — v3 hardware
- Hardware Components:
- Footswitches: GPIO-based with LED feedback, tap tempo support
- Encoders: Rotary encoders with push switches
- Analog Controls: Potentiometers and expression pedals via SPI ADC (MCP3008)
- Relays: For bypass switching
- LED Strips: Visual feedback
- LCD: Various displays (320×240, 128×64, 135×240) via SPI (lcd320x240.py is the implementation used for v2 and v3 hardware)
4. Factory Pattern
- HandlerFactory (`pistomp/handlerfactory.py`): Creates appropriate handler based on config version
- HardwareFactory (`pistomp/hardwarefactory.py`): Creates hardware objects based on config version
- AudiocardFactory (`pistomp/audiocardfactory.py`): Manages audio card configuration
5. MOD Integration (`modalapi/`)
- Pedalboard (`pedalboard.py`): Manages pedalboard state using Lilv (LV2 plugin discovery)
- Plugin (`plugin.py`): Represents individual LV2 effects
- Parameter (`parameter.py`): Handles plugin parameter control
- WiFi (`wifi.py`): WiFi management for web UI access
6. UI Library (`uilib/`)
- Widget-based UI for LCD displays
- Components: panels, dialogs, menus, footswitch displays, icons, text
- Supports different LCD resolutions and color depths
- Panel stack layout allows overlays, dialogs, etc.
7. Configuration System
- YAML-based configuration (`data/config/`)
- Hardware descriptor JSON files
- Supports default + user + pedalboard-specific overrides
- Defines MIDI mappings, footswitch functions, encoder behaviors
8. MIDI System
- MIDI Output: Sends CC messages to mod-host for parameter control
- Supports omni mode (all channels)
9. Audio System
- Uses JACK audio server (mod-host)
- Audio card abstraction for different hardware (HiFiBerry, AudioInjector, IQAudio)
- Audio card factory manages configuration and restoration
Key Design Patterns
- Factory Pattern: For creating handlers and hardware objects
- Singleton Pattern: Handlers and factories are singletons
- Abstract Base Classes: Handler and Hardware provide interfaces
- Polling Architecture: Event-driven via polling rather than interrupts
Data Flow
- Hardware inputs (footswitches, encoders, pots) → Hardware layer polls
- Hardware events → Handler callbacks
- Handler → MIDI CC messages to mod-host OR direct HTTP API calls
- mod-host → Processes audio through LV2 plugins
- Handler → Monitors mod-host state via HTTP API
- Handler → Updates LCD display via UI library
This architecture separates hardware abstraction, audio processing (MOD), and UI management, making it extensible for different hardware variants while maintaining a consistent interface.
Tips
Most developers will find it easiest to code in-place by ssh'ing to the pi-Stomp
ssh pistomp@pistomp.local
To do live code changes, you'll need to stop the modalapistomp service so it doesn't conflict:
ps-stop
Then you can start the main python via:
ps-run
Or with optional logging:
ps-run -l info ps-run -l debug
Stop python:
<ctrl>C
When you're done with iterative edits, restart the service with:
ps-restart
Getting the code
Clone the repo
git clone -b pistomp-v3 https://github.com/TreeFallSound/pi-stomp.git
Contributing
General Rules
- All code changes must go through PRs — no direct pushes
- PRs must be approved by the maintainer before merging
- Please follow project coding style and conventions
1. Fork the repo
On GitHub, click the “Fork” button (top right of the project page). This creates a copy under your own GitHub account.
2. Clone your fork locally
Replace <your-username> with your GitHub username:
git clone -b pistomp-v3 https://github.com/<your-username>/pi-stomp.git cd pi-stomp
3. Add the original repo as a remote (called “upstream”)
This keeps your fork in sync with the original repo.
git remote add upstream https://github.com/TreeFallSound/pi-stomp.git
4. Create a new branch for your work
Don't work directly on the main pistomp-v3 branch. Instead create a new one, name it bugXYZ, featureABC, etc.
git checkout -b <branch-name>
5. Make changes and commit
Edit files, then:
git add . git commit -m "Add feature: ABC"
6. Push your branch to your fork
git push origin <branch-name>
7. Open a Pull Request (PR)
Go to your fork on GitHub, and you’ll see a prompt to open a Pull Request.
Choose pistomp-v3 as target branch
Describe your change
Submit PR
8. (Optional) Keep your fork up to date
If the main repo changes before your PR is merged:
git fetch upstream git checkout pistomp-v3 git merge upstream/pistomp-v3 git push origin pistomp-v3
Then rebase or merge those updates into your feature branch:
git checkout <branch-name> git rebase pistomp-v3 git push -f origin <branch-name>