Custom firmware for the Patch.Init Eurorack module using DaisySP DSP library.
This project provides a custom firmware implementation for the Electro-Smith Patch.Init Eurorack module. The Patch.Init is a versatile eurorack module featuring:
- 2x CV inputs (±5V)
- 2x CV outputs (±5V)
- 2x audio inputs
- 2x audio outputs
- Multiple GPIO pins for buttons, LEDs, and encoders
- STM32H7 microcontroller with floating-point unit
DuoPulse v5 is an opinionated 2-voice percussion sequencer designed for electronic music from club-ready techno to experimental IDM. Version 5 features zero shift layers - every parameter is directly accessible through 8 knobs across two modes.
- Zero shift layers: Every parameter directly accessible
- CV law: CV1-4 always modulate performance parameters regardless of mode
- Knob pairing: Related functions across Performance/Config modes
- Deterministic variation: Same settings + seed = identical output
| Knob | Parameter | 0% | 100% |
|---|---|---|---|
| K1 | ENERGY | Sparse | Busy |
| K2 | SHAPE | Stable (euclidean) | Wild (weighted random) |
| K3 | AXIS X | Grounded (downbeats) | Floating (offbeats) |
| K4 | AXIS Y | Simple | Complex |
| Knob | Parameter | 0% | 100% |
|---|---|---|---|
| K1 | CLOCK DIV | Ă·4 (slow) | Ă—4 (fast) |
| K2 | SWING | Straight | Heavy swing |
| K3 | DRIFT | Locked (same each phrase) | Evolving |
| K4 | ACCENT | Flat (all hits equal) | Dynamic (ghosts to accents) |
SHAPE Zones: Three-way pattern character blending
- 0-30% STABLE: Humanized euclidean, techno, four-on-floor
- 30-70% SYNCOPATED: Funk, displaced, tension
- 70-100% WILD: IDM, chaos, weighted random
Voice Relationship (COMPLEMENT): Voice 2 (shimmer) fills gaps in Voice 1 (anchor) pattern. DRIFT controls placement variation.
ACCENT Velocity: Position-aware dynamics from ghost notes to accents based on metric weight.
AUX Output: Fill Gate (default) or secret Hat Burst mode via Hold+Switch gesture.
| CV | Modulates | Range |
|---|---|---|
| CV 1 | ENERGY | ±50% |
| CV 2 | SHAPE | ±50% |
| CV 3 | AXIS X | ±50% |
| CV 4 | AXIS Y | ±50% |
Fresh-start boot: All settings reset to musical defaults
- Performance knobs read from hardware immediately
- ENERGY=50%, SHAPE=30%, AXIS X/Y=50%
- CLOCK DIV=Ă—1, SWING=50%, DRIFT=0%, ACCENT=50%
- Nothing persists across power cycles
See docs/specs/ for the complete specification.
- Hardware: Electro-Smith Patch.Init Eurorack module
- Software:
- ARM GCC toolchain (for STM32)
arm-none-eabi-gcccompilermakebuild systemdfu-utilfor firmware deployment (or STM32CubeProgrammer)- For testing: Catch2 testing framework (optional)
- DaisySP: Included as a git submodule
macOS (using Homebrew):
brew install arm-none-eabi-gcc dfu-utilLinux (Debian/Ubuntu):
sudo apt-get update
sudo apt-get install gcc-arm-none-eabi dfu-util makeCatch2 (for unit testing):
# Option 1: Install system-wide (recommended)
git clone https://github.com/catchorg/Catch2.git
cd Catch2
cmake -Bbuild -H. -DBUILD_TESTING=OFF
sudo cmake --build build/ --target install
# Option 2: Use package manager
# macOS: brew install catch2
# Linux: sudo apt-get install catch2 (if available).
├── DaisySP/ # DaisySP library (git submodule)
├── src/ # Source files
│ ├── main.cpp # Main firmware entry point
│ └── ...
├── inc/ # Header files
│ └── ...
├── tests/ # Unit tests
│ ├── test_main.cpp # Test runner
│ └── ...
├── Makefile # Build configuration
├── setup.sh # Setup script for initial configuration
├── .cursor/ # Cursor IDE rules
│ └── rules
└── README.md # This file
Quick Setup (recommended):
# Clone the repository
git clone --recursive https://github.com/yourusername/duopulse.git
cd duopulse
# Run setup script (initializes submodules and builds DaisySP)
./setup.sh
# Build the firmware
makeManual Setup:
-
Clone the repository (including submodules):
git clone --recursive https://github.com/yourusername/duopulse.git cd duopulseIf you've already cloned without submodules:
git submodule update --init --recursive
-
Build DaisySP library:
cd DaisySP make cd ..
-
Build the firmware:
make
makeormake all- Build the firmwaremake clean- Remove build artifactsmake rebuild- Clean and rebuildmake test- Build and run unit testsmake program- Flash firmware to Patch.Init module (requires DFU mode)make daisy-update- Update DaisySP submodule to latest version
The Makefile supports the following variables:
DAISYSP_PATH- Path to DaisySP (default:./DaisySP)BUILD_DIR- Build output directory (default:build)TARGET- Target board (default:patch)DEBUG- Enable debug symbols (setDEBUG=1)
Example:
make DEBUG=1 BUILD_DIR=debug_buildThe project includes a unit test framework using Catch2:
-
Run all tests:
make test -
Run specific test:
./build/test_runner [test_name]
-
Run tests with verbose output:
./build/test_runner --success
Tests are located in the tests/ directory. Each test file should:
- Include the Catch2 header:
#include <catch2/catch.hpp> - Use
TEST_CASE()macro for test cases - Use
REQUIRE()orCHECK()for assertions
Example:
#include <catch2/catch.hpp>
#include "../src/my_module.h"
TEST_CASE("MyModule processes audio correctly")
{
MyModule module;
module.Init(48000.0f);
float input = 0.5f;
float output = module.Process(input);
REQUIRE(output >= -1.0f);
REQUIRE(output <= 1.0f);
}To generate test coverage reports:
make test-coverageThis requires gcov and lcov to be installed.
-
Enter DFU (Device Firmware Update) mode:
- Power off the module
- Hold the BOOT button (if available) or use the DFU jumper
- Power on the module while holding BOOT
- Release BOOT button
- The module should now be in DFU mode
-
Verify DFU mode:
dfu-util --list
You should see the STM32 device listed.
Method 1: Using Makefile (recommended)
make programMethod 2: Using dfu-util directly
dfu-util -a 0 -s 0x08000000:leave -D build/patch-init-firmware.binMethod 3: Using STM32CubeProgrammer
- Open STM32CubeProgrammer
- Select "USB" connection
- Connect to the device
- Load the
.binor.hexfile frombuild/ - Click "Download"
After flashing:
- Power cycle the module (or use
:leaveflag with dfu-util) - The module should boot with the new firmware
- Test audio and CV I/O to verify functionality
- Device not found: Ensure module is in DFU mode and USB cable is connected
- Permission denied: Add udev rules for STM32 DFU devices (Linux) or run with sudo
- Flash fails: Verify module is in DFU mode and try resetting the module
-
Navigate to DaisySP directory:
cd DaisySP -
Fetch and checkout latest version:
git fetch origin git checkout master # or specific tag/commit git pull origin master -
Rebuild DaisySP:
make clean make
-
Return to project root and rebuild:
cd .. make clean make
cd DaisySP
git fetch --tags
git checkout v1.0.0 # Replace with desired version
make clean
make
cd ..
make clean
makeAfter updating DaisySP, commit the submodule reference:
git add DaisySP
git commit -m "Update DaisySP to version X.X.X"cd DaisySP
git describe --tags
cd ..-
Create a feature branch:
git checkout -b feature/my-feature
-
Make changes and test:
make clean make make test -
Flash and test on hardware:
make program
-
Commit changes:
git add . git commit -m "Description of changes"
-
Push and create pull request
Refer to the Patch.Init documentation for specific pin assignments. Common configurations:
- CV Inputs: ADC channels 0-1
- CV Outputs: DAC channels 0-1
- Audio Inputs: I2S/SAI interface
- Audio Outputs: I2S/SAI interface
- GPIO: Configurable via
daisy::GPIO
Default sample rate: 48kHz
To change:
patch.SetAudioSampleRate(daisy::SaiHandle::Config::SampleRate::SAI_96KHZ);Default block size: 4 samples
To change:
patch.SetAudioBlockSize(8);- Follow the coding standards in
.cursor/rules - Write unit tests for new features
- Update documentation as needed
- Test on hardware before submitting PR
[Specify your license here]
- Missing toolchain: Install ARM GCC toolchain
- DaisySP not found: Run
git submodule update --init --recursive - Linker errors: Ensure DaisySP is built (
cd DaisySP && make)
- No audio output: Check audio callback registration and hardware connections
- CV not working: Verify ADC/DAC configuration and pin assignments
- Module crashes: Check for stack overflow, uninitialized variables, or division by zero
Enable debug output:
make DEBUG=1Use a debugger (OpenOCD + GDB) for hardware debugging:
make debugThe firmware includes a USB serial logging system for debugging without a hardware debugger.
Viewing Log Output:
# macOS/Linux - using screen
screen /dev/tty.usbmodem* 115200
# Alternative - using make target (saves to /tmp)
make listen
# Exit screen: Ctrl-A then \ then yLog Levels:
TRACE: Verbose debuggingDEBUG: Development info (mode changes, events)INFO: Normal operation (boot, config)WARN: WarningsERROR: Critical issues
Configuration:
Adjust log levels in Makefile:
# Development: DEBUG+ logs, default INFO
CXXFLAGS += -DLOG_COMPILETIME_LEVEL=1
CXXFLAGS += -DLOG_DEFAULT_LEVEL=2
# Release: WARN/ERROR only
CXXFLAGS += -DLOG_COMPILETIME_LEVEL=3
CXXFLAGS += -DLOG_DEFAULT_LEVEL=3See CLAUDE.md for complete logging documentation.
For issues and questions:
- Open an issue on GitHub
- Check the DaisySP documentation
- Visit the Daisy Forum