Add comprehensive README documenting project architecture, features, configuration, and customization
This commit is contained in:
@@ -0,0 +1,467 @@
|
||||
# Loopy MIDI Controller - USB MIDI Foot Controller
|
||||
|
||||
A compact ESP32-S3 based USB MIDI foot controller with hardware acceleration, designed to work seamlessly with Loopy Pro.
|
||||
|
||||
## Overview
|
||||
|
||||
This project implements a USB MIDI device that accepts foot pedal inputs and converts them to MIDI Continuous Controller (CC) messages. It's designed to be a reliable replacement for the MIDI-to-USB adapters used with Loopy Pro, offering low latency (1-5ms) and USB compatibility without Bluetooth interference.
|
||||
|
||||
**Key Features:**
|
||||
- **USB MIDI Interface**: Uses ESP32-S3's native USB MIDI support for stable, reliable connectivity
|
||||
- **Expression Pedal Support**: Read analog input with configurable calibration
|
||||
- **Launchpad X Compatible**: Per-pad LED colors matching Novation Launchpad hardware
|
||||
- **Low Latency Design**: 1-5ms response time for responsive control
|
||||
- **MIDI Clock Sync**: Pixel 6 pulses in time with Loopy Pro tempo
|
||||
- **Hardware Acceleration**: Uses multiple cores efficiently without cross-core LED issues
|
||||
|
||||
## Hardware Requirements
|
||||
|
||||
### PCB/Layout Notes
|
||||
- **ESP32-S3-WROOM-1** microcontroller
|
||||
- **Daisy-chained 74HC165** shift registers for button inputs (16 buttons total)
|
||||
- **2x RMT WS2812C** LED drivers for 10 programmable RGB pixels
|
||||
- **ADC on GPIO4** for expression pedal input
|
||||
- **WS2812 external LED** for boot animation (GPIO12)
|
||||
|
||||
### Required Components
|
||||
- ESP32-S3 development board (ESP32-S3-WROOM-1 preferred)
|
||||
- 74HC165 shift register (2x for 16 buttons)
|
||||
- WS2812C LED strip (10 pixels)
|
||||
- PWM/ DAC capable for expression pedal
|
||||
- Level shifters if needed for input voltages
|
||||
|
||||
## Software Architecture
|
||||
|
||||
### Core Design
|
||||
```
|
||||
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
||||
│ USB MIDI │ │ MIDI Task │ │ LED Driver │
|
||||
│ (Core 1) │ │ (Core 0) │ │ (Core 0) │
|
||||
│ - Loopy Pro │◄──►│ - Clock Parity │◄──►│ - Pixel Pulse │
|
||||
│ recognition │ │ Detection │ │ on beats │
|
||||
│ - Note/CC │ │ - MIDI update │◄──►│ - Button LED │
|
||||
│ processing │ │ processing │ │ feedback │
|
||||
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Expression │
|
||||
│ Pedal (Core 0)│
|
||||
│ - ADC Read │
|
||||
│ - CC 4 output │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
### Key Subsystems
|
||||
|
||||
#### 1. USB MIDI Transport (Core 1)
|
||||
- Adafruit TinyUSB library integration
|
||||
- Proper VID/PID spoofing (Novation Launchpad X: 0x1235/0x0103)
|
||||
- MIDI event parsing and routing to Core 0
|
||||
- Re-enumeration support for stable device connection
|
||||
|
||||
#### 2. MIDI Task (Core 0)
|
||||
- Core 0 exclusive for all FastLED operations (prevents crashes)
|
||||
- MIDI clock (0xF8) detection and counting
|
||||
- Beat detection using tick count / 24 (24 PPQN)
|
||||
- Pixel 6 pulsing on every MIDI beat
|
||||
- Button-to-CC message conversion
|
||||
|
||||
#### 3. LED Driver (Core 0)
|
||||
- Per-pad color configuration (Launchpad X compatibility)
|
||||
- Velocity-based color mapping
|
||||
- Dim off-state for power efficiency
|
||||
- Launchpad-style startup animation
|
||||
|
||||
#### 4. Expression Pedal (Core 0)
|
||||
- ADC input on GPIO4 (calibrated min/max)
|
||||
- CC 4 output on MIDI channel 1
|
||||
- Hysteresis filtering for stable readings
|
||||
- 5ms update interval for responsive control
|
||||
|
||||
#### 5. Switch Driver (Core 0)
|
||||
- 16 buttons via daisy-chained 74HC165
|
||||
- 5ms debounce for reliable input
|
||||
- Direct button-to-CC mapping
|
||||
- Per-pad LED feedback
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### MIDI Mapping
|
||||
|
||||
The controller uses a Launchpad X-style mapping for intuitive control:
|
||||
|
||||
| Pad | Hardware Button | MIDI Channel | Note/Pitch | CC | Default Color |
|
||||
|-----|-----------------|--------------|------------|----|---------------|
|
||||
| 0-1 | 0-1 | 1 | 36-45 | - | Blue (0x0000FF) |
|
||||
| 2-4 | 2-4 | 1 | 36-45 | - | Orange (0xFF6600) |
|
||||
| 5 | 5 | 1 | 36 | - | Red (0xFF0000) |
|
||||
| 6 | 6 | 1 | 36 | - | White (0xFFFFFF) |
|
||||
| 7 | 7 | 1 | 36 | - | Amber (0xFFBF00) |
|
||||
| 8-9 | 8-9 | 1 | 36-37 | - | Purple (0x9900FF) |
|
||||
|
||||
**For Loopy Pro Users:**
|
||||
- Buttons generate CC messages (not Note On/Off)
|
||||
- CC values: 2-11 for buttons 0-9
|
||||
- CC 4 for expression pedal
|
||||
- Pixel colors provide visual feedback
|
||||
|
||||
### MIDI Clock Synchronization
|
||||
|
||||
Pixel 6 provides visual feedback synchronized to Loopy Pro's tempo:
|
||||
|
||||
1. **Configure Loopy Pro**: Enable MIDI Clock output targeting JOC Midi
|
||||
2. **Device Recognition**: Loopy Pro detects the VID/PID (0x1235/0x0103)
|
||||
3. **Visual Feedback**: Pixel 6 pulses white on each MIDI Clock beat (0xF8)
|
||||
4. **Pulse Animation**: Quadratic fade (80ms duration) between beats
|
||||
|
||||
**Troubleshooting**: If pixel 6 doesn't pulse:
|
||||
- Verify Loopy Pro has MIDI Clock enabled
|
||||
- Ensure JOC Midi is the clock target
|
||||
- Check MIDI device permissions
|
||||
- Verify MIDI input in Loopy Pro shows "JOC Midi"
|
||||
|
||||
### PlatformIO Configuration
|
||||
|
||||
#### Build Options (`platformio.ini`)
|
||||
|
||||
```ini
|
||||
[env]
|
||||
platform = espressif32
|
||||
board = esp32-s3-devkitc-1
|
||||
framework = arduino
|
||||
build_type = release
|
||||
monitor_speed = 115200
|
||||
upload_speed = 921600
|
||||
|
||||
build_flags =
|
||||
-DCORE_DEBUG_LEVEL=0
|
||||
-DARDUINO_USB_MODE=0
|
||||
-DARDUINO_USB_LAUNCHER_MODE=0
|
||||
|
||||
lib_deps =
|
||||
adafruit/[email protected]
|
||||
https://github.com/FastLED/FastLED/archive/refs/tags/3.6.0.zip
|
||||
|
||||
extra_scripts =
|
||||
pre_build.py
|
||||
extra_script.py
|
||||
```
|
||||
|
||||
#### Build Flags Explanation
|
||||
|
||||
- `-DARDUINO_USB_MODE=0`: USB in CDC/MIDI mode (no virtual serial)
|
||||
- `-DARDUINO_USB_LAUNCHER_MODE=0`: No USB launcher mode
|
||||
- `-DCORE_DEBUG_LEVEL=0`: Disable debug output
|
||||
- `--allow-multiple-definition`: Required for Adafruit TinyUSB compatibility
|
||||
|
||||
#### Build Scripts
|
||||
|
||||
**`pre_build.py`**: Patches board definitions with Launchpad X VID/PID
|
||||
**`extra_script.py`**: Ensures proper TinyUSB linking order
|
||||
|
||||
## Usage
|
||||
|
||||
### Initial Setup
|
||||
|
||||
1. **Flash Firmware**
|
||||
```bash
|
||||
platformio run --target upload
|
||||
```
|
||||
|
||||
2. **Open Serial Monitor** (115200 baud)
|
||||
- Shows startup sequence
|
||||
- MIDI activity diagnostics
|
||||
- System status
|
||||
|
||||
3. **Available Commands** (type in serial)
|
||||
- `help` - Show all commands
|
||||
- `dump` - Display button states
|
||||
- `probe` - Hardware diagnostics
|
||||
- `ledon`/`ledoff` - Turn all LEDs on/off
|
||||
- `ledtest` - Color cycle test
|
||||
- `exp` - Show expression pedal ADC/value
|
||||
- `usb` - USB status
|
||||
- `gpiotest` - Raw GPIO diagnostics
|
||||
- `miditest` - Simulate MIDI input
|
||||
- `padtest` - Test individual pads
|
||||
- `mapping` - Show current pad mapping
|
||||
|
||||
### MIDI Configuration in Loopy Pro
|
||||
|
||||
1. **System Settings**
|
||||
- Name: "JOC Midi"
|
||||
- Manufacturer: "JOC"
|
||||
- Model: "JOC Midi"
|
||||
|
||||
2. **MIDI Setup**
|
||||
- Port 1: Enabled
|
||||
- Input Channel: All
|
||||
- Output Channel: 1 (or preferred)
|
||||
|
||||
3. **Sync Configuration**
|
||||
- Sync Master: LOOPY (if Loopy Pro is master)
|
||||
- Clock Output: Enabled
|
||||
- Clock Targets: JOC Midi
|
||||
|
||||
### Operation
|
||||
|
||||
1. **Button Presses**
|
||||
- Press any button to send corresponding CC message
|
||||
- Pixel color changes to match button state
|
||||
|
||||
2. **Expression Pedal**
|
||||
- Connect foot pedal to ADC input
|
||||
- Calibrate min (heel) and max (toe) positions
|
||||
- Watch pixel 5 for pedal value feedback
|
||||
|
||||
3. **Visual Feedback**
|
||||
- Buttons: Color indicates CC state
|
||||
- Expression pedal: Pixel 5 brightness reflects CC value
|
||||
- Sync: Pixel 6 pulses with MIDI Clock
|
||||
|
||||
## Customization
|
||||
|
||||
### Adding New MIDI Functions
|
||||
|
||||
1. **Add New CC Mappings**
|
||||
```cpp
|
||||
// In switch_stub.h or app_task.cpp
|
||||
static const uint8_t BUTTONS_TO_CC[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
|
||||
```
|
||||
|
||||
2. **Custom LED Patterns**
|
||||
```cpp
|
||||
// In led_stub.cpp
|
||||
void apply_custom_color(uint8_t index, uint8_t velocity) {
|
||||
if (index == 6) { // Example: Custom color for expression pedal
|
||||
mux_ptr->set_led_color(index, 0, 255, 0); // Green
|
||||
return;
|
||||
}
|
||||
apply_pad_color(index, velocity);
|
||||
}
|
||||
```
|
||||
|
||||
### Changing MIDI Clock Behavior
|
||||
|
||||
1. **Modify Beat Detection**
|
||||
```cpp
|
||||
// In main.cpp, midi_task
|
||||
uint32_t beat_interval = 24; // PPQN for MIDI Clock
|
||||
|
||||
if (tick / beat_interval != last_beat) {
|
||||
last_beat = tick / beat_interval;
|
||||
// Trigger pulse
|
||||
}
|
||||
```
|
||||
|
||||
2. **Change Pulse Animation**
|
||||
```cpp
|
||||
// In main.cpp, midi_task
|
||||
float pulse_speed = 1.0f / 1.0; // Adjust pulse speed
|
||||
mux.set_led_color(6, v * pulse_speed, v * pulse_speed, v * pulse_speed);
|
||||
```
|
||||
|
||||
### Modifying Expression Pedal
|
||||
|
||||
1. **Change ADC Pin**
|
||||
```cpp
|
||||
// In expression_pedal.h/expression_pedal.cpp
|
||||
static const uint8_t EXP_PEDAL_PIN = 4; // or another GPIO
|
||||
```
|
||||
|
||||
2. **Adjust Calibration**
|
||||
```cpp
|
||||
// In main.cpp
|
||||
default exp_pedal.cal_min(0); // Set based on testing
|
||||
default exp_pedal.cal_max(1023); // Set based on testing
|
||||
```
|
||||
|
||||
3. **Change MIDI CC**
|
||||
```cpp
|
||||
// In expression_pedal.cpp
|
||||
send_cc(1, 4, value); // Keep existing
|
||||
// Or change:
|
||||
send_cc(1, 7, value); // Volume instead of expression
|
||||
```
|
||||
|
||||
### Custom Pad Layout
|
||||
|
||||
1. **Map Pads Differently**
|
||||
```cpp
|
||||
// In app_task.h/app_task.cpp
|
||||
#define PAD_NOTE_MAPPING {36, 37, 38, 39, 40, 41, 42, 43, 44, 45}
|
||||
#define PAD_CC_MAPPING {2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
|
||||
```
|
||||
|
||||
2. **Add Special Functions**
|
||||
```cpp
|
||||
// In app_task.cpp
|
||||
void process_special_button(uint8_t led_index) {
|
||||
if (led_index == 5) { // Expression pedal button
|
||||
// Toggle pedal mode
|
||||
}
|
||||
if (led_index == 6) { // MIDI Clock toggle
|
||||
// Toggle clock visualization
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing and Diagnostics
|
||||
|
||||
### Serial Commands for Debugging
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `probe` | Test all buttons and LEDs |
|
||||
| `gpiotest` | Check GPIO pin states |
|
||||
| `ledtest` | Cycle through all LED colors |
|
||||
| `miditest` | Simulate MIDI input for testing |
|
||||
| `dump` | Show current button states |
|
||||
|
||||
### Troubleshooting Common Issues
|
||||
|
||||
#### Issue: Pixel 6 doesn't pulse
|
||||
**Cause**: MIDI Clock not arriving
|
||||
**Solution**:
|
||||
1. Check Loopy Pro sync settings
|
||||
2. Verify MIDI Clock target is JOC Midi
|
||||
3. Examine `[CLK] !! NO MIDI CLOCK RECEIVED !!` in serial
|
||||
|
||||
#### Issue: Buttons not sending MIDI
|
||||
**Cause**: Shift register issues
|
||||
**Solution**:
|
||||
1. Run `probe` command
|
||||
2. Check wiring and pin connections
|
||||
3. Verify shift register operation
|
||||
|
||||
#### Issue: Expression pedal unresponsive
|
||||
**Cause**: ADC calibration incorrect
|
||||
**Solution**:
|
||||
1. Use `exp` command to see raw ADC values
|
||||
2. Adjust calibration min/max values
|
||||
3. Check voltage range and connection
|
||||
|
||||
#### Issue: USB connection drops
|
||||
**Cause**: Re-enumeration issues
|
||||
**Solution**:
|
||||
1. Check mounted() status in `[MIDI] USB mounted:` messages
|
||||
2. Ensure proper VID/PID values in build
|
||||
3. Verify TinyUSB initialization
|
||||
|
||||
## Build Instructions
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. **PlatformIO IDE** or
|
||||
2. **Arduino CLI** with ESP32 core support
|
||||
|
||||
### Quick Build
|
||||
|
||||
```bash
|
||||
# Using PlatformIO
|
||||
platformio run
|
||||
|
||||
# Using Arduino CLI
|
||||
cd your-project
|
||||
arduino-cli compile --builder chitrak/micropython-builder --fqbn esp32-s3-devkitc-1 .
|
||||
```
|
||||
|
||||
### Upload
|
||||
|
||||
```bash
|
||||
# Using PlatformIO
|
||||
platformio upload
|
||||
|
||||
# Using Arduino CLI
|
||||
arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32-s3-devkitc-1 .
|
||||
```
|
||||
|
||||
### Troubleshooting Build Issues
|
||||
|
||||
#### Common Build Errors
|
||||
|
||||
1. **Adafruit TinyUSB conflicts**
|
||||
- Solution: Use the provided `extra_script.py` with `--allow-multiple-definition`
|
||||
- Ensure build_flags match exactly
|
||||
|
||||
2. **Memory Issues**
|
||||
- Solution: Reduce debug output, use release build type
|
||||
- Check stack sizes in platformio.ini
|
||||
|
||||
3. **Pin Conflicts**
|
||||
- Solution: Adjust GPIO pins in configuration files
|
||||
- Verify all components use different pins
|
||||
|
||||
#### Hardware Issues
|
||||
|
||||
1. **LED Driver Problems**
|
||||
- Solution: Test with simple color output commands
|
||||
- Check wiring and power supply
|
||||
|
||||
2. **Button Issues**
|
||||
- Solution: Use `probe` command regularly
|
||||
- Ensure proper pull-up/pull-down configurations
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Planned Features
|
||||
|
||||
1. **USB MIDI Sysex Support**
|
||||
- Launchpad X programmer mode
|
||||
- Bank select and patch change
|
||||
|
||||
2. **Advanced Expression Pedal**
|
||||
- Rotary encoding support
|
||||
- Multiple pedal modes (CC1/CC4)
|
||||
|
||||
3. **Enhanced LED Effects**
|
||||
- Breathing animations
|
||||
- SOS patterns for diagnostics
|
||||
- Battery level indicator
|
||||
|
||||
4. **Additional MIDI Functions**
|
||||
- Pitch Bend support
|
||||
- Aftertouch
|
||||
- Poly Pressure
|
||||
|
||||
### Custom Configuration Examples
|
||||
|
||||
#### For Loopy Pro DJs
|
||||
```cpp
|
||||
// Loopy-specific CC mappings
|
||||
static const uint8_t LOOPY_CC_MAP[10] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
|
||||
```
|
||||
|
||||
#### For External Controllers
|
||||
```cpp
|
||||
// CC-based interaction
|
||||
static const uint8_t EXTERNAL_CC_TARGET = 1;
|
||||
send_cc(EXTERNAL_CC_TARGET, 7, value); // Volume
|
||||
```
|
||||
|
||||
#### For Recording Studios
|
||||
```cpp
|
||||
// Note-based for drum triggers
|
||||
static const uint8_t DRUM_NOTES[10] = {36, 38, 40, 42, 44, 45, 47, 48, 50, 52};
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This project is provided as-is with no explicit license. The code is intended for educational and personal use. Modifications and distributions should respect original authors' intentions where specified.
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
- **Novation Launchpad**: Inspiration for layout and color scheme
|
||||
- **ESP32-S3**: Powerful microcontroller with native USB MIDI
|
||||
- **Adafruit TinyUSB**: Reliable USB MIDI stack
|
||||
- **FastLED**: Efficient LED control library
|
||||
- **Contributors**: All who tested and provided feedback
|
||||
|
||||
## Contact
|
||||
|
||||
For issues or questions:
|
||||
1. Check the project documentation
|
||||
2. Review serial output for diagnostic messages
|
||||
3. Use available testing commands
|
||||
4. Submit issues with complete build logs if encountering problems
|
||||
Reference in New Issue
Block a user