MIDI Breadboard Instrument
+MIDI Breadboard Instrument — Desk Rave & 16-Track Symphony Controller
- A breadboard-based project that plays MIDI melodies by converting MIDI data into C code for the device. + A standalone, dual-core ESP32 MIDI synthesizer, drum sequencer, and 30 FPS SPI OLED visualizer written in ESP-IDF v5.2.2 (C99 / FreeRTOS), powered by a custom Run-Length Encoding (RLE) Python MIDI-to-C compiler.
- The hardware currently includes two active buzzers, one passive buzzer, a rotary encoder, a 1.3-inch AMOLED display, two buttons, and two LEDs. + What started as making a passive piezo buzzer squeak evolved into a full 16-track embedded music box with live kick/hi-hat drum transients, hardware PCNT quadrature rotary controls for real-time tempo and volume scaling, and dual-core FreeRTOS task isolation.
midi_to_c.py) generating flash-resident SequenceStep struct arrays for the dual-core ESP32 breadboard synthesizer.
Breadboard Prototype Photos
+
+
+ WHISP), rotary mode (SPD / VOL), kick/hi-hat drum boxes, and dynamic frequency bars.
+
+ Combining Hardware, Software, and Creativity
+ 02 // Engineering Challenges Solved +From a Squeaking Buzzer to a Dual-Core Synthesizer
- This project represents the kind of work I enjoy most: combining hardware, software, music, experimentation, and creativity in a practical and unusual system on a breadboard. -
-- To get melodies onto the hardware, I wrote a script that converts MIDI data into C code for the device. That generated C code drives the breadboard setup to play MIDI sounds while providing physical controls and visual feedback. + Pushing a bare-metal breadboard setup to play recognizable, energetic multi-track music required solving several real embedded systems challenges:
+-
+
- The Strobe-Light Bug & RLE Compression (
midi_to_c.py): Raw MIDI files often slice sustained notes into microscopic fragments, causing the LEDs and OLED UI to strobe at 30+ Hz while wasting flash memory. We built a custom Python Run-Length Encoding (RLE) compressor usingmidothat merges contiguous identical pitch/drum states into solidSequenceStepblocks and routes bass/percussion channels to dedicated drum triggers.
+ - Acoustic Hardware Limits (+2 Octave Transposition): Passive piezo buzzers physically struggle to resonate at low bass frequencies. Tracks with deep basslines like Seven Nation Army were transposed up two full octaves so the piezo disc resonates clearly while the active buzzers carry the rhythmic punch. +
- Dual-Core FreeRTOS Lockless IPC: Core 0 runs
oled_visualizer_task(30 FPS SPI DMA framebuffer engine) andbutton_task(debounced input polling), while Core 1 exclusively runsbuzzer_sequence_taskfor microsecond-accurate LEDC PWM pitch generation and hardware PCNT rotary encoder scaling.
+
Confirmed Components on the Breadboard
+ 03 // Confirmed Hardware & Pinout +ESP32 Pinout & Architecture Specifications
| Component / Layer | -Confirmed Specification | +Component / Peripheral | +ESP32 Pin / Interface | +Function |
|---|---|---|---|---|
| Conversion Workflow | -Custom script that converts MIDI data into C code for the device | +Microcontroller | +ESP32 (Dual-Core Xtensa @ 160 MHz) | +ESP-IDF v5.2.2 + FreeRTOS dual-core execution |
| Device Code Output | -C code | +Rotary Encoder CLK / DT | +GPIO 32 / GPIO 33 |
+ Hardware PCNT quadrature tracking (1000 ns glitch filter) |
| Active Sound Components | -2 × Active buzzers | +Rotary Encoder SW | +GPIO 16 (Active Low) |
+ Toggles knob & OLED HUD between Speed (0.2x–3.0x) and Volume (0%–100%) |
| Passive Sound Component | -1 × Passive buzzer | +Pause / Next Buttons | +GPIO 13 / GPIO 14 (Active Low) |
+ Play/Pause toggle and 16-track playlist cycling |
| Display | -1 × 1.3-inch AMOLED screen | +Passive Buzzer (Melody) | +GPIO 25 (LEDC 13-bit PWM) |
+ Main melody synthesizer driven by hardware LEDC Timer 0 |
| Rotational Input | -1 × Rotary encoder | +Active Buzzer 1 & 2 (Drums) | +GPIO 26 (Kick) / GPIO 27 (Hi-Hat) |
+ Physical percussion transients on drum steps |
| Push Inputs | -2 × Buttons | +Kick & Hi-Hat LEDs | +GPIO 21 (Kick) / GPIO 22 (Hi-Hat) |
+ Visual beat indicators synchronized to drum hits |
| Visual Indicators | -2 × LEDs | -|||
| Additional Circuit Details | -Details will be documented as the project develops. | +1.3" 7-Pin SPI OLED | +CS: 5, DC: 2, RES: 4, MOSI: 23, SCK: 18 |
+ 10 MHz SPI2 display running custom 30 FPS visualizer on Core 0 |
The 16-Track Symphony
+
+ All 16 tracks are stored in ESP32 flash memory as SequenceStep arrays featuring frequency, duration, kick_drum, and hi_hat triggers:
+
-
+
- Careless Whisper (Extended Club Edit) +
- Take On Me (EDM Build-up Edition) +
- Never Gonna Give You Up / Rickroll (Double Chorus) +
- Can't Help Falling In Love (Wedding Mix) +
- Gimme! Gimme! Gimme! (Midnight Rave) +
- Für Elise (Beethoven Trance) +
- Symphony No. 40 (Mozart Intense Strings) +
- Super Mario Bros. (Main Theme) +
- Pirates of the Caribbean 1 (He's a Pirate) +
- Pirates of the Caribbean 2 (The Black Pearl) +
- Rondo Alla Turca (Mozart) +
- Tetris (Korobeiniki) +
- Sonic the Hedgehog (Green Hill Zone) +
- Castlevania (Vampire Killer) +
- Seven Nation Army (Transposed +2 Octaves for piezo resonance) +
- Mega Man 2 (Dr. Wily Stage) +
What I Am Currently Learning & Next Steps
+ 05 // Workshop Notes & Next Steps +Current State & Roadmap
- How data conversion scripts bridge file formats (like MIDI) into low-level C structures, and how multiple physical outputs (two active buzzers, one passive buzzer, two LEDs, and a 1.3-inch AMOLED screen) can be coordinated alongside user inputs (a rotary encoder and two buttons). + The firmware is rock-solid on the breadboard: the rotary encoder dynamically scales volume and tempo via lockless IPC variables, all 16 tracks play from flash memory, and the 1.3" SPI OLED renders real-time frequency bars and drum hits at 30 FPS.
- Next Step + Next Steps- Documenting how the MIDI-to-C conversion script formats note events and adding further hardware notes as the breadboard prototype evolves. + Migrating the dual-breadboard circuit onto a soldered perfboard or custom KiCad PCB, and optionally re-integrating a WS2812B addressable RGB strip when expanding the enclosure.