Skip to content

sauloverissimo/ESP32_Host_MIDI

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

122 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

ESP32_Host_MIDI

Piano Visualizer Chord Detection BLE MIDI Receiver




Sponsor

πŸ‡§πŸ‡· PortuguΓͺs (Brasil)


English

The universal MIDI hub for ESP32 β€” 9 transports, one API.

ESP32_Host_MIDI turns your ESP32 into a full-featured, multi-protocol MIDI hub. Connect a USB keyboard, receive notes from an iPhone via Bluetooth, bridge your DAW over WiFi with RTP-MIDI (Apple MIDI), control Max/MSP via OSC, reach 40-year-old synths through a DIN-5 serial cable, and link multiple ESP32 boards wirelessly with ESP-NOW β€” all simultaneously, all through the same clean event API.

#include <ESP32_Host_MIDI.h>

void setup() { midiHandler.begin(); }

void loop() {
    midiHandler.task();
    for (const auto& ev : midiHandler.getQueue()) {
        char noteBuf[8];
        Serial.printf("%-12s %-4s ch=%d  vel=%d\n",
            MIDIHandler::statusName(ev.statusCode),
            MIDIHandler::noteWithOctave(ev.noteNumber, noteBuf, sizeof(noteBuf)),
            ev.channel0 + 1, ev.velocity7);
    }
}

What can you build?

A partial list. Every combination of transports opens a new instrument, tool, or installation.

Wireless MIDI interfaces

  • USB keyboard β†’ ESP32 β†’ WiFi β†’ macOS (Logic Pro / GarageBand) β€” no drivers, no cables to the Mac
  • iPhone / iPad BLE app β†’ ESP32 β†’ USB MIDI Device β†’ DAW port β€” iOS apps become studio controllers
  • ESP32 presents itself as a USB MIDI Class Compliant interface β€” plug into any computer, it just works

Custom hardware instruments

  • Effects pedal board β€” ESP32 sends Program Change / CC messages to a Daisy Seed or hardware multi-effects unit; display shows preset name and bank
  • MIDI drum pad β€” piezo sensors on ADC inputs β†’ velocity-sensitive MIDI notes β†’ USB or BLE
  • Custom synthesizer β€” ESP32 receives MIDI and controls an external DAC + VCO/VCA analog circuit, or triggers a Daisy Seed running a synth engine
  • MIDI controller β€” encoders, faders, buttons, touchpads β†’ USB MIDI Device β†’ any DAW
  • MIDI to CV converter β€” ESP32 + external DAC (MCP4728, MCP4921) β†’ 0–5 V CV / gate for Eurorack and analog synths
  • Wireless expression pedal β€” foot controller with ESP-NOW β†’ central ESP32 hub β†’ CC messages
  • Smart metronome / clock β€” generates MIDI Clock at precise BPM, sent simultaneously over USB, BLE, DIN-5, and WiFi
  • Theremin with MIDI output β€” ultrasonic sensors or capacitive touch β†’ pitch + volume β†’ MIDI notes
  • MIDI accordion or wind controller β€” pressure sensors + buttons β†’ ESP32 β†’ BLE β†’ iPad instrument

Bridges and routers

  • DIN-5 vintage synth β†’ ESP32 β†’ USB Device β†’ modern DAW β€” zero-driver adapter
  • Wireless stage rig: ESP-NOW mesh of performers β†’ single USB output to FOH computer Creative software integration
  • Max/MSP / Pure Data / SuperCollider ↔ ESP32 over OSC β€” bidirectional, address-mapped
  • TouchOSC tablet β†’ ESP32 β†’ DIN-5 hardware synth β€” touchscreen for vintage gear
  • Algorithmic composition in Max β†’ OSC β†’ ESP32 β†’ BLE β†’ iOS instrument app

Monitoring and education

  • Live piano roll: keys lit as you play, scrollable view on a 1.9" display
  • Real-time chord detection: plays a chord, see its name instantly ("Cmaj7", "Dm7β™­5")
  • MIDI event logger with timestamps, channel, velocity, and chord grouping

Transport Matrix

Transport Protocol Physical Latency Requires
USB Host USB MIDI 1.0 USB-OTG cable < 1 ms ESP32-S3 / S2 / P4
USB Host MIDI 2.0 USB MIDI 2.0 (UMP) USB-OTG cable < 1 ms ESP32-S3 / S2 / P4
BLE MIDI BLE MIDI 1.0 Bluetooth LE 3–15 ms Any ESP32 with BT
USB Device USB MIDI 1.0 USB-OTG cable < 1 ms ESP32-S3 / S2 / P4
ESP-NOW MIDI ESP-NOW 2.4 GHz radio 1–5 ms Any ESP32
RTP-MIDI (WiFi) AppleMIDI / RFC 6295 WiFi UDP 5–20 ms Any ESP32 with WiFi
Ethernet MIDI AppleMIDI / RFC 6295 Wired (W5x00 / native) 2–10 ms W5500 SPI or ESP32-P4
OSC Open Sound Control WiFi UDP 5–15 ms Any ESP32 with WiFi
UART / DIN-5 Serial MIDI 1.0 DIN-5 connector < 1 ms Any ESP32

All transports share a single MIDIHandler event queue and the same send API. Mix and match at will.


Quick Start

#include <ESP32_Host_MIDI.h>
// Arduino IDE: Tools > USB Mode β†’ "USB Host"

void setup() {
    Serial.begin(115200);
    midiHandler.begin();
}

void loop() {
    midiHandler.task();
    for (const auto& ev : midiHandler.getQueue()) {
        char noteBuf[8];
        Serial.println(MIDIHandler::noteWithOctave(ev.noteNumber, noteBuf, sizeof(noteBuf)));
    }
}

Access individual fields:

for (const auto& ev : midiHandler.getQueue()) {
    ev.statusCode;   // MIDI_NOTE_ON | MIDI_NOTE_OFF | MIDI_CONTROL_CHANGE | ...
    ev.channel0;     // 0–15 (MIDI spec)
    ev.noteNumber;   // MIDI note number (0–127)
    ev.velocity7;    // 0–127 (MIDI 1.0)
    ev.velocity16;   // 0–65535 (MIDI 2.0, scaled)
    ev.pitchBend14;  // 0–16383 (center = 8192)
    ev.pitchBend32;  // 0–0xFFFFFFFF (MIDI 2.0, center = 0x80000000)
    ev.chordIndex;   // groups simultaneous notes
    ev.timestamp;    // millis() at arrival

    // Static helpers (zero allocation):
    MIDIHandler::noteName(ev.noteNumber);       // "C", "C#", "D" ...
    MIDIHandler::noteOctave(ev.noteNumber);     // -1 to 9
    MIDIHandler::statusName(ev.statusCode);     // "NoteOn", "ControlChange" ...
}

Gallery

Β  Β 

RTP-MIDI / Apple MIDI connecting to macOS Β· 25-key scrolling piano roll

Β 

25-key scrolling piano roll Β· Real-time chord name (Gingoduino) Β· Event queue debug

Β  Β 

BLE Receiver (iPhone β†’ ESP32) Β· BLE Sender Β· Piano debug view

Videos β€” each example folder contains an .mp4 demo inside examples/<name>/images/.


Architecture

╔══════════════════════════════════════════════════════════════════════╗
β•‘  INPUTS                          MIDIHandler             OUTPUTS    β•‘
β•‘                                                                      β•‘
β•‘  USB keyboard ──[USBConnection]────►  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β•‘
β•‘  USB MIDI 2.0 ──[USBMIDI2Conn]────►  β”‚              β”‚              β•‘
β•‘  iPhone BLE   ──[BLEConnection]────►  β”‚              β”‚              β•‘
β•‘  macOS WiFi   ──[RTPMIDIConn.]─────►  β”‚  Event Queue │──► getQueue()β•‘
β•‘  DAW USB out  ──[USBDeviceConn]────►  β”‚  (ring buf,  β”‚              β•‘
β•‘  Max/MSP OSC  ──[OSCConnection]────►  β”‚  thread-safe)│──► Active    β•‘
β•‘  W5500 LAN    ──[EthernetMIDI]─────►  β”‚              β”‚    notes     β•‘
β•‘  DIN-5 serial ──[UARTConnection]───►  β”‚  Chord       β”‚              β•‘
β•‘  ESP32 radio  ──[ESPNowConn.]──────►  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜β”€β”€β–Ί Chord     β•‘
β•‘                                              β”‚            names     β•‘
β•‘                                              β”‚                      β•‘
β•‘                                              β–Ό                      β•‘
β•‘                                     sendMidiMessage()               β•‘
β•‘                                  (broadcasts to ALL transports)     β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

Core 0 β€” USB Host task, BLE stack, radio / network drivers (FreeRTOS tasks) Core 1 β€” midiHandler.task() + your loop() code Thread safety β€” ring buffers + portMUX spinlocks on every transport

Every transport implements the same MIDITransport abstract interface. Adding a new transport is one line: midiHandler.addTransport(&myTransport).


Transports

USB Host (OTG)

Connects any class-compliant USB MIDI device β€” keyboards, pads, interfaces, drum machines, controllers β€” directly to the ESP32's USB-OTG port. No hub, no driver, no OS configuration.

Boards: ESP32-S3, ESP32-S2, ESP32-P4 Β· Arduino IDE: Tools > USB Mode β†’ "USB Host"

#include <ESP32_Host_MIDI.h>
void setup() { midiHandler.begin(); }

Examples: T-Display-S3, T-Display-S3-Queue, T-Display-S3-Piano, T-Display-S3-Gingoduino

MIDI 2.0: Use USBMIDI2Connection for native USB MIDI 2.0 with UMP. See the MIDI 2.0 section below.


BLE MIDI

The ESP32 advertises as a BLE MIDI 1.0 peripheral. macOS (Audio MIDI Setup β†’ Bluetooth), iOS (GarageBand, AUM, Loopy, Moog), and Android connect without any pairing ritual. Also supports Central (scanner) mode to connect to another BLE MIDI device.

Boards: Any ESP32 with Bluetooth Β· Range: ~30 m Β· Latency: 3–15 ms

#include <ESP32_Host_MIDI.h>
void setup() { midiHandler.begin(); }  // BLE advertises automatically

Examples: T-Display-S3-BLE-Sender, T-Display-S3-BLE-Receiver


USB Device

The ESP32-S3 presents itself as a class-compliant USB MIDI interface to the host computer. macOS, Windows, and Linux recognise it instantly β€” no driver. Acts as a transparent bridge: any MIDI from BLE, WiFi, UART, or ESP-NOW is forwarded to the DAW via USB, and vice versa.

Boards: ESP32-S3, ESP32-S2, ESP32-P4 Β· Arduino IDE: Tools > USB Mode β†’ "USB-OTG (TinyUSB)"

Cannot coexist with USB Host β€” both use the OTG port.

#include "src/USBDeviceConnection.h"

USBDeviceConnection usbMIDI("ESP32 MIDI");   // name shown in DAW MIDI port list

void setup() {
    midiHandler.addTransport(&usbMIDI);
    usbMIDI.begin();
    midiHandler.begin();
}

Examples: USB-Device-MIDI, T-Display-S3-USB-Device


ESP-NOW MIDI

Ultra-low-latency (~1–5 ms) wireless MIDI between ESP32 boards via Espressif's proprietary peer-to-peer radio. No WiFi router, no handshake, no pairing. Broadcast mode (all boards receive everyone's notes) or unicast.

Boards: Any ESP32 Β· Range: ~200 m open air Β· Infrastructure: none

#include "src/ESPNowConnection.h"

ESPNowConnection espNow;

void setup() {
    espNow.begin();
    midiHandler.addTransport(&espNow);
    midiHandler.begin();
}

Examples: ESP-NOW-MIDI, T-Display-S3-ESP-NOW-Jam


RTP-MIDI / Apple MIDI

Implements Apple MIDI (RTP-MIDI, RFC 6295) over WiFi UDP. macOS and iOS discover the ESP32 automatically via mDNS Bonjour β€” it appears in Audio MIDI Setup β†’ Network with no manual configuration. Compatible with Logic Pro, GarageBand, Ableton, and any CoreMIDI app.

Requires: lathoub/Arduino-AppleMIDI-Library v3.x

#include <WiFi.h>
#include "src/RTPMIDIConnection.h"

RTPMIDIConnection rtpMIDI;

void setup() {
    WiFi.begin("YourSSID", "YourPassword");
    while (WiFi.status() != WL_CONNECTED) delay(500);
    midiHandler.addTransport(&rtpMIDI);
    rtpMIDI.begin();
    midiHandler.begin();
}

Β 

Examples: RTP-MIDI-WiFi


Ethernet MIDI

Same RTP-MIDI / AppleMIDI protocol over a wired W5x00 SPI Ethernet module or ESP32-P4 native Ethernet MAC. Lower and more consistent latency than WiFi. Ideal for studio racks and live venues with managed networks.

Requires: lathoub/Arduino-AppleMIDI-Library v3.x + Arduino Ethernet library

#include <SPI.h>
#include "src/EthernetMIDIConnection.h"

EthernetMIDIConnection ethMIDI;
static const uint8_t MAC[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

void setup() {
    midiHandler.addTransport(&ethMIDI);
    ethMIDI.begin(MAC);   // DHCP β€” pass a static IPAddress as second arg for fixed IP
    midiHandler.begin();
}

Examples: Ethernet-MIDI


OSC

Bidirectional OSC ↔ MIDI bridge over WiFi UDP. Receives OSC messages from Max/MSP, Pure Data, SuperCollider, and TouchOSC and converts them to MIDI events β€” and sends every MIDI event out as an OSC message.

Address map: /midi/noteon, /midi/noteoff, /midi/cc, /midi/pc, /midi/pitchbend, /midi/aftertouch

Requires: CNMAT/OSC library

#include <WiFi.h>
#include "src/OSCConnection.h"

OSCConnection oscMIDI;

void setup() {
    WiFi.begin("YourSSID", "YourPassword");
    while (WiFi.status() != WL_CONNECTED) delay(500);
    midiHandler.addTransport(&oscMIDI);
    oscMIDI.begin(8000, IPAddress(192, 168, 1, 100), 9000);
    midiHandler.begin();
}

Examples: OSC-MIDI-WiFi, T-Display-S3-OSC


UART / DIN-5

Standard MIDI serial (31250 baud, 8N1) for connecting vintage hardware β€” synthesizers, drum machines, mixers, sequencers, anything with a DIN-5 connector. Supports running status, real-time messages (Clock, Start, Stop), and multiple simultaneous UART ports (ESP32-P4 has five hardware UARTs).

Hardware: TX β†’ DIN-5 pin 5 via 220 Ξ©; PC-900V / 6N138 optocoupler on RX β†’ DIN-5 pin 4

#include "src/UARTConnection.h"

UARTConnection uartMIDI;

void setup() {
    uartMIDI.begin(Serial1, /*RX=*/16, /*TX=*/17);
    midiHandler.addTransport(&uartMIDI);
    midiHandler.begin();
}

Examples: UART-MIDI-Basic, P4-Dual-UART-MIDI


USB Host MIDI 2.0

Native USB MIDI 2.0/UMP support with automatic protocol negotiation. USBMIDI2Connection extends USBConnection β€” scans the device's configuration descriptor for both Alt 0 (MIDI 1.0) and Alt 1 (MIDI 2.0), preferring MIDI 2.0 when available. Falls back to MIDI 1.0 transparently.

After selecting MIDI 2.0, performs the mandatory UMP Endpoint Discovery and Protocol Negotiation sequence. Raw UMP words (32-bit) are delivered via callback β€” no conversion to MIDI 1.0.

#include "src/USBMIDI2Connection.h"

USBMIDI2Connection usb;

void onUMP(void*, const uint32_t* words, uint8_t count) {
    // Raw UMP words β€” MIDI 2.0 native resolution
}

void setup() {
    usb.setUMPCallback(onUMP, nullptr);
    usb.setMidiCallback(onMidi, nullptr);  // MIDI 1.0 fallback
    midiHandler.addTransport(&usb);
    midiHandler.begin();
}

Query device capabilities after negotiation:

if (usb.isMIDI2() && usb.isNegotiated()) {
    auto& ep = usb.getEndpointInfo();
    Serial.printf("UMP v%d.%d, %d function blocks\n",
        ep.umpVersionMajor, ep.umpVersionMinor, ep.numFunctionBlocks);
}

Boards: ESP32-S3, ESP32-S2, ESP32-P4 Β· Examples: T-Display-S3-AMoled-MIDI2-UMP


Bridge Use Cases

Any MIDI arriving on any transport is automatically forwarded to all others β€” no extra code.

Bridge Diagram
Wireless keyboard β†’ DAW iPhone BLE β†’ ESP32 β†’ USB Device β†’ Logic Pro
USB keyboard β†’ WiFi USB keyboard β†’ ESP32 β†’ RTP-MIDI β†’ macOS
Legacy to modern DIN-5 synth β†’ ESP32 β†’ USB Device β†’ any DAW
Modern to legacy macOS β†’ RTP-MIDI β†’ ESP32 β†’ DIN-5 β†’ 1980s drum machine
Wireless stage mesh ESP-NOW nodes β†’ ESP32 hub β†’ USB β†’ FOH computer
Creative software Max/MSP OSC β†’ ESP32 β†’ BLE β†’ iPad instrument app

Full hub β€” receive everything, send everywhere:

USB keyboard ─┐
iPhone BLE   ──
macOS RTP   ──┼──► MIDIHandler ──► USB Device (DAW)
DIN-5 synth ──               ──► BLE (iOS app)
TouchOSC    ──               ──► DIN-5 (drum machine)
ESP-NOW     β”€β”˜               ──► ESP-NOW (stage nodes)

Hardware Ecosystem

ESP32_Host_MIDI acts as the MIDI brain and protocol hub. It connects and communicates with a broad ecosystem of boards and devices.

Boards you can connect to the ESP32

Board Connection Use case
Daisy Seed (Electro-Smith) UART / DIN-5 or USB DSP audio synthesis engine; ESP32 sends MIDI, Daisy plays notes
Teensy 4.x (PJRC) UART serial or USB Host Complex MIDI routing or synthesis; excellent USB MIDI native support
Arduino UNO / MEGA / Nano UART serial (DIN-5) Classic MIDI projects; ESP32 is the wireless gateway
Raspberry Pi RTP-MIDI, OSC, or USB DAW host, audio processing, generative composition
Eurorack / modular synths MIDI DIN-5 β†’ CV/gate interface Pitch CV, gate, velocity β†’ analogue voltage via converter module
Hardware synthesizers DIN-5 Any keyboard, rack synth, or effects unit with MIDI In/Out/Thru
iPad / iPhone BLE MIDI GarageBand, AUM, Moog apps, NLog, Animoog β€” all CoreMIDI-compatible
Computer DAW USB Device or RTP-MIDI Logic Pro, Ableton, Bitwig, FL Studio, Reaper, Pro Tools

Daisy Seed + ESP32 is a particularly powerful combination: the ESP32 handles all MIDI connectivity (USB, BLE, WiFi, DIN-5) and the Daisy Seed processes audio in real time at 48 kHz / 24-bit with its ARM Cortex-M7 DSP. They talk over a single UART/DIN-5 cable.

Teensy 4.1 can run complex MIDI logic, arpeggiators, chord voicers, or sequencers, while ESP32 handles wireless transport β€” each board doing what it does best.

Hardware projects you can build

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  PROJECT               β”‚  COMPONENTS                                 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Wireless MIDI pedal   β”‚  ESP32 + buttons + enclosure β†’ ESP-NOW     β”‚
β”‚  board                 β”‚  β†’ central hub β†’ DIN-5 / USB to amp rack   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  MIDI drum pad         β”‚  ESP32 + piezo sensors + ADC β†’ velocity-   β”‚
β”‚                        β”‚  sensitive MIDI notes over USB or BLE       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Synthesizer           β”‚  ESP32 + Daisy Seed: ESP32 bridges all     β”‚
β”‚                        β”‚  MIDI protocols, Daisy generates audio      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  MIDI to CV converter  β”‚  ESP32 + MCP4728 DAC β†’ 0–5 V pitch CV +   β”‚
β”‚                        β”‚  gate for Eurorack / analogue synths        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Custom MIDI           β”‚  ESP32-S3 + encoders + faders + OLED β†’     β”‚
β”‚  controller            β”‚  USB MIDI Device recognized by any DAW      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Piano learning aid    β”‚  ESP32 + RGB LEDs on piano keys + display  β”‚
β”‚                        β”‚  β†’ lights the correct key for each note     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Wireless expression   β”‚  ESP32 + FSR / potentiometer in foot       β”‚
β”‚  pedal                 β”‚  enclosure β†’ CC messages via ESP-NOW        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  MIDI arpeggiator /    β”‚  ESP32 receives chords, generates          β”‚
β”‚  sequencer             β”‚  arpeggiated patterns, sends to DIN-5       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Theremin / air synth  β”‚  Ultrasonic sensors β†’ pitch + volume β†’     β”‚
β”‚                        β”‚  MIDI notes via BLE or USB                  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Interactive art       β”‚  Motion / proximity / touch sensors β†’      β”‚
β”‚  installation          β”‚  MIDI β†’ generative music / light control    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Display Examples (T-Display-S3)

The LilyGO T-Display-S3 has a 1.9" 170Γ—320 ST7789 display + ESP32-S3. These examples show a live MIDI dashboard in landscape mode (320Γ—170 after setRotation(2)).

Example Transport What the display shows
T-Display-S3 USB Host Active notes + event log
T-Display-S3-Queue USB Host Full event queue debug view
T-Display-S3-Piano USB Host 25-key scrollable piano roll
T-Display-S3-Piano-Debug USB Host Piano roll + extended debug info
T-Display-S3-Gingoduino USB Host + BLE Chord names (music theory engine)
T-Display-S3-BLE-Sender BLE Send mode status + event log
T-Display-S3-BLE-Receiver BLE Receive mode + note log
T-Display-S3-ESP-NOW-Jam ESP-NOW Peer status + jam events
T-Display-S3-OSC OSC + WiFi WiFi status + OSC bridge log
T-Display-S3-USB-Device BLE + USB Device Dual status + bridge log

Gingoduino β€” Music Theory on Embedded Systems

Gingoduino is a music theory library for embedded systems β€” the same engine that powers the T-Display-S3-Gingoduino example. When integrated via GingoAdapter.h, it listens to the same MIDI event stream and continuously analyses the active notes to produce:

  • Chord name β€” "Cmaj7", "Dm7β™­5", "G7", "Am" with extensions and alterations
  • Root note β€” identified root pitch of the chord
  • Active note set β€” structured list of currently pressed notes
  • Interval analysis β€” intervals between notes (M3, m7, P5, etc.)
  • Scale matching β€” identifies likely scale (major, minor, modes)

Everything runs on-device at interrupt speed β€” no cloud, no network, no latency.

#include "src/GingoAdapter.h"  // requires Gingoduino β‰₯ v0.2.2

void loop() {
    midiHandler.task();

    // Chord name updates automatically as notes arrive and are released:
    std::string chord = gingoAdapter.getChordName();  // "Cmaj7", "Dm", "G7sus4" …
    std::string root  = gingoAdapter.getRootNote();    // "C", "D", "G" …

    display.setChord(chord.c_str());
}

T-Display-S3-Gingoduino: chord name, root note, and active keys updated in real time

β†’ github.com/sauloverissimo/gingoduino


Gingo β€” Music Theory for Python and Desktop

Gingo is the desktop and Python counterpart of Gingoduino β€” the same music theory concepts ported to Python for use in scripts, DAW integrations, MIDI processors, composition tools, and web apps.

Use it to:

  • Analyse MIDI files and extract chord progressions
  • Build Python MIDI processors that recognise chords on the fly
  • Create web applications with real-time music theory annotation
  • Prototype music theory algorithms before porting them to Gingoduino
  • Generate chord charts, lead sheets, and educational exercises
from gingo import Gingo

g = Gingo()
chord = g.identify([60, 64, 67, 71])   # C E G B
print(chord.name)   # "Cmaj7"
print(chord.root)   # "C"

β†’ github.com/sauloverissimo/gingo β†’ sauloverissimo.github.io/gingo

ESP32 + Gingo workflow: prototype music theory algorithms in Python with Gingo β†’ port the logic to Gingoduino on ESP32 β†’ display chord names live on T-Display-S3.


Hardware Compatibility

Chip β†’ available transports

Chip USB Host BLE USB Device WiFi Ethernet (native) UART ESP-NOW
ESP32-S3 βœ… βœ… βœ… βœ… ❌ (W5500 SPI) βœ… βœ…
ESP32-S2 βœ… ❌ βœ… βœ… ❌ (W5500 SPI) βœ… ❌
ESP32-P4 βœ… ❌ βœ… ❌ βœ… βœ… Γ—5 ❌
ESP32 (classic) ❌ βœ… ❌ βœ… ❌ (W5500 SPI) βœ… βœ…
ESP32-C3 / C6 / H2 ❌ βœ… ❌ βœ… ❌ βœ… βœ…

W5500 SPI Ethernet works on any ESP32 via EthernetMIDIConnection.

Recommended boards

Use case Board
Best all-round (USB Host + BLE + WiFi + display) LilyGO T-Display-S3
Full USB Host + USB Device + BLE Any ESP32-S3 DevKit
Ultra-low-latency wireless stage mesh ESP32 DevKit (ESP-NOW)
Wired studio rack ESP32-P4 native Ethernet or any ESP32 + W5500
DIN-5 MIDI gateway Any ESP32 + UART optocoupler

Installation

Arduino IDE: Sketch β†’ Include Library β†’ Manage Libraries β†’ search ESP32_Host_MIDI

PlatformIO:

[env:esp32-s3-devkitc-1]
platform = espressif32
board    = esp32-s3-devkitc-1
framework = arduino

lib_deps =
    sauloverissimo/ESP32_Host_MIDI
    # lathoub/Arduino-AppleMIDI-Library  ; RTP-MIDI + Ethernet MIDI
    # arduino-libraries/Ethernet          ; Ethernet MIDI
    # CNMAT/OSC                           ; OSC
    # sauloverissimo/gingoduino           ; Chord names

Board package: Tools > Boards Manager β†’ "esp32" by Espressif β†’ β‰₯ 3.0.0 USB Host and USB Device require arduino-esp32 β‰₯ 3.0 (TinyUSB MIDI).

Transport Required library
RTP-MIDI / Ethernet MIDI lathoub/Arduino-AppleMIDI-Library
Ethernet MIDI arduino-libraries/Ethernet
OSC CNMAT/OSC
Chord names sauloverissimo/gingoduino
USB Host / Device / BLE / ESP-NOW Built into arduino-esp32

API Reference

// Setup
midiHandler.begin();               // start built-in transports (USB, BLE, ESP-NOW)
midiHandler.begin(cfg);            // with custom config
midiHandler.addTransport(&t);      // register external transport

// Receive
const auto& q = midiHandler.getQueue();                        // event ring buffer
std::vector<std::string> n = midiHandler.getActiveNotesVector(); // ["C4","E4","G4"]
std::string chord = midiHandler.getChordName();                 // "Cmaj7"

// Send (broadcasts to ALL transports simultaneously)
midiHandler.sendNoteOn(ch, note, vel);
midiHandler.sendNoteOff(ch, note, vel);
midiHandler.sendControlChange(ch, ctrl, val);
midiHandler.sendProgramChange(ch, prog);
midiHandler.sendPitchBend(ch, val);          // 0–16383, center = 8192

MIDIHandlerConfig:

MIDIHandlerConfig cfg;
cfg.maxEvents      = 20;    // queue capacity
cfg.enableHistory  = true;  // keep full history
cfg.chordDetection = true;  // group simultaneous notes

Custom transport interface:

class MyTransport : public MIDITransport {
public:
    void task() override;
    bool isConnected() const override;
    bool sendMidiMessage(const uint8_t* data, size_t len) override;
protected:
    void dispatchMidiData(const uint8_t* data, size_t len); // inject received MIDI
    void dispatchConnected();
    void dispatchDisconnected();
};
midiHandler.addTransport(&myTransport);

File Structure

ESP32_Host_MIDI/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ ESP32_Host_MIDI.h             ← main include (USB + BLE + ESP-NOW built-in)
β”‚   β”œβ”€β”€ MIDIHandler.h / .cpp          ← event queue, chord detection, active notes
β”‚   β”œβ”€β”€ MIDITransport.h               ← abstract transport interface
β”‚   β”œβ”€β”€ MIDIHandlerConfig.h           ← config struct
β”‚   β”œβ”€β”€ USBConnection.h / .cpp        ← USB Host OTG
β”‚   β”œβ”€β”€ USBMIDI2Connection.h / .cpp   ← USB Host MIDI 2.0 (UMP negotiation)
β”‚   β”œβ”€β”€ MIDI2Support.h                ← UMP types, scaler, builder, parser
β”‚   β”œβ”€β”€ BLEConnection.h / .cpp        ← BLE MIDI
β”‚   β”œβ”€β”€ ESPNowConnection.h / .cpp     ← ESP-NOW MIDI
β”‚   β”œβ”€β”€ UARTConnection.h / .cpp       ← UART / DIN-5
β”‚   β”œβ”€β”€ USBDeviceConnection.h         ← USB MIDI Device (header-only)
β”‚   β”œβ”€β”€ RTPMIDIConnection.h / .cpp    ← RTP-MIDI over WiFi (header-only)
β”‚   β”œβ”€β”€ EthernetMIDIConnection.h      ← AppleMIDI over Ethernet (header-only)
β”‚   β”œβ”€β”€ OSCConnection.h               ← OSC ↔ MIDI bridge (header-only)
β”‚   └── GingoAdapter.h                ← Gingoduino chord integration
β”œβ”€β”€ extras/
β”‚   └── tests/
β”‚       β”œβ”€β”€ test_native.cpp           ← MIDI2Support + MIDITransport tests (32)
β”‚       β”œβ”€β”€ test_handler.cpp          ← MIDIHandler tests (99)
β”‚       └── test_midi2_scan.cpp       ← USB MIDI 2.0 descriptor tests (120)
└── examples/
    β”œβ”€β”€ T-Display-S3/                 T-Display-S3-Queue/
    β”œβ”€β”€ T-Display-S3-Piano/           T-Display-S3-Piano-Debug/
    β”œβ”€β”€ T-Display-S3-Gingoduino/      T-Display-S3-BLE-Sender/
    β”œβ”€β”€ T-Display-S3-BLE-Receiver/    T-Display-S3-ESP-NOW-Jam/
    β”œβ”€β”€ T-Display-S3-OSC/             T-Display-S3-USB-Device/
    β”œβ”€β”€ ESP-NOW-MIDI/                 UART-MIDI-Basic/
    β”œβ”€β”€ P4-Dual-UART-MIDI/           RTP-MIDI-WiFi/
    β”œβ”€β”€ Ethernet-MIDI/               OSC-MIDI-WiFi/
    └── USB-Device-MIDI/

License

MIT β€” see LICENSE


Built with ❀️ for musicians, makers, and researchers.
Issues and contributions welcome: github.com/sauloverissimo/ESP32_Host_MIDI