# 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/Adafruit_TinyUSB@3.1.0 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