Files

14 KiB

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)

[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
platformio run --target upload
  1. Open Serial Monitor (115200 baud)

    • Shows startup sequence
    • MIDI activity diagnostics
    • System status
  2. 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
// 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};
  1. Custom LED Patterns
// 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
// 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
}
  1. Change Pulse Animation
// 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
// In expression_pedal.h/expression_pedal.cpp
static const uint8_t EXP_PEDAL_PIN = 4; // or another GPIO
  1. Adjust Calibration
// In main.cpp
default exp_pedal.cal_min(0); // Set based on testing
default exp_pedal.cal_max(1023); // Set based on testing
  1. Change MIDI CC
// 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
// 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}
  1. Add Special Functions
// 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

# Using PlatformIO
platformio run

# Using Arduino CLI
cd your-project
arduino-cli compile --builder chitrak/micropython-builder --fqbn esp32-s3-devkitc-1 .

Upload

# 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

// 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

// CC-based interaction
static const uint8_t EXTERNAL_CC_TARGET = 1;
send_cc(EXTERNAL_CC_TARGET, 7, value); // Volume

For Recording Studios

// 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