ertuncozbay.dev/projects/midi-breadboard-instrument.html

231 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MIDI Breadboard Instrument (Desk Rave &amp; 16-Track Symphony) — Projects — Ertunç Özbay</title>
<meta name="description" content="Project notes for the MIDI Breadboard Instrument (Desk Rave & 16-Track Symphony Controller) by Ertunç Özbay: Dual-Core ESP32 synthesizer in ESP-IDF v5.2.2 with RLE Python MIDI-to-C compiler, 30 FPS SPI OLED visualizer, rotary encoder, and triple-buzzer acoustic stage.">
<link rel="icon" type="image/svg+xml" href="../assets/images/favicon.svg">
<link rel="stylesheet" href="../assets/css/style.css">
</head>
<body>
<a class="skip-link" href="#main-content">Skip to main content</a>
<header class="site-header">
<div class="container header-inner">
<a class="site-brand" href="../index.html">
<span class="brand-mark" aria-hidden="true">EÖ</span>
<span>
Ertunç Özbay
<span class="brand-subtitle">workshop // notebook // lab</span>
</span>
</a>
<button class="nav-toggle" type="button" data-nav-toggle aria-expanded="false" aria-controls="primary-navigation">
<span aria-hidden="true">☰</span>
<span data-nav-label>Menu</span>
</button>
<nav id="primary-navigation" class="site-nav" data-site-nav aria-label="Main navigation">
<ul class="nav-list">
<li><a class="nav-link" href="../index.html">Home</a></li>
<li><a class="nav-link" href="../about.html">About</a></li>
<li><a class="nav-link" href="../projects/index.html" aria-current="page">Projects</a></li>
<li><a class="nav-link" href="../blog/index.html">Blog</a></li>
<li><a class="nav-link" href="../lab.html">Lab</a></li>
<li><a class="nav-link" href="../contact.html">Contact</a></li>
</ul>
</nav>
</div>
</header>
<main id="main-content">
<div class="container">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="index.html">← Back to Projects</a>
</nav>
<article>
<header class="workshop-hero">
<div class="card-meta">
<span class="badge badge-experimental">Status: Active Breadboard Prototype</span>
<span>Project 03 // ESP32, ESP-IDF C99, FreeRTOS &amp; Python RLE Tooling</span>
</div>
<h1>MIDI Breadboard Instrument — Desk Rave &amp; 16-Track Symphony Controller</h1>
<p class="hero-lead">
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.
</p>
<p class="reading-width">
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.
</p>
<div class="button-row">
<a class="btn btn-primary" href="https://github.com/ertunc-ozbay/midi-breadboard-instrument" rel="noopener noreferrer">View GitHub Repository</a>
<a class="btn btn-secondary" href="index.html">All Projects</a>
<a class="btn btn-secondary" href="../lab.html">See Lab Experiments</a>
</div>
</header>
<figure class="schematic-frame" aria-label="MIDI Breadboard Instrument Component Diagram">
<img src="../assets/images/midi-breadboard-layout.svg" alt="Block diagram of the MIDI Breadboard Instrument showing the MIDI conversion script producing C code for the breadboard system, connected to two active buzzers, one passive buzzer, a 1.3-inch SPI OLED screen, one rotary encoder, two buttons, and two LEDs." width="760" height="320">
<figcaption class="schematic-caption">
Software-to-hardware pipeline: Python RLE MIDI compiler (<code>midi_to_c.py</code>) generating flash-resident <code>SequenceStep</code> struct arrays for the dual-core ESP32 breadboard synthesizer.
</figcaption>
</figure>
<section class="section-block" aria-labelledby="p3-gallery">
<span class="section-label">01 // Hardware Build Gallery</span>
<h2 id="p3-gallery">Breadboard Prototype Photos</h2>
<div class="card-grid">
<figure class="card" style="margin:0;">
<img src="../assets/images/MIDI_FIRST.jpg" alt="Full dual-breadboard overview of the ESP32 MIDI Breadboard Instrument" style="width:100%; height:auto; border-radius:6px; margin-bottom:0.75rem;">
<figcaption>
<strong>Full Breadboard Layout:</strong> Dual-breadboard build featuring the ESP32 DevKit, 1.3" 7-pin SPI OLED display, KY-040 rotary encoder, tactile buttons, passive melody piezo, and dual active drum buzzers.
</figcaption>
</figure>
<figure class="card" style="margin:0;">
<img src="../assets/images/MIDI_SECOND.jpg" alt="Active 1.3-inch SPI OLED visualizer running at 30 FPS on the breadboard" style="width:100%; height:auto; border-radius:6px; margin-bottom:0.75rem;">
<figcaption>
<strong>30 FPS Live OLED Visualizer:</strong> Custom 5×7 font renderer displaying track code (<code>WHISP</code>), rotary mode (<code>SPD</code> / <code>VOL</code>), kick/hi-hat drum boxes, and dynamic frequency bars.
</figcaption>
</figure>
<figure class="card" style="margin:0;">
<img src="../assets/images/MIDI_THIRD.jpg" alt="Close-up of the rotary encoder, tactile buttons, LEDs, and three buzzers" style="width:100%; height:auto; border-radius:6px; margin-bottom:0.75rem;">
<figcaption>
<strong>Controls &amp; Acoustic Stage:</strong> Close-up of the rotary encoder, Play/Pause and Next Track buttons, green/red drum LEDs, and the 3-buzzer audio stage.
</figcaption>
</figure>
</div>
</section>
<section class="section-block reading-width" aria-labelledby="p3-overview">
<span class="section-label">02 // Engineering Challenges Solved</span>
<h2 id="p3-overview">From a Squeaking Buzzer to a Dual-Core Synthesizer</h2>
<p>
Pushing a bare-metal breadboard setup to play recognizable, energetic multi-track music required solving several real embedded systems challenges:
</p>
<ul>
<li><strong>The Strobe-Light Bug &amp; RLE Compression (<code>midi_to_c.py</code>):</strong> 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 using <code>mido</code> that merges contiguous identical pitch/drum states into solid <code>SequenceStep</code> blocks and routes bass/percussion channels to dedicated drum triggers.</li>
<li><strong>Acoustic Hardware Limits (+2 Octave Transposition):</strong> Passive piezo buzzers physically struggle to resonate at low bass frequencies. Tracks with deep basslines like <em>Seven Nation Army</em> were transposed up two full octaves so the piezo disc resonates clearly while the active buzzers carry the rhythmic punch.</li>
<li><strong>Dual-Core FreeRTOS Lockless IPC:</strong> Core 0 runs <code>oled_visualizer_task</code> (30 FPS SPI DMA framebuffer engine) and <code>button_task</code> (debounced input polling), while Core 1 exclusively runs <code>buzzer_sequence_task</code> for microsecond-accurate LEDC PWM pitch generation and hardware PCNT rotary encoder scaling.</li>
</ul>
</section>
<section class="section-block reading-width" aria-labelledby="p3-components">
<span class="section-label">03 // Confirmed Hardware &amp; Pinout</span>
<h2 id="p3-components">ESP32 Pinout &amp; Architecture Specifications</h2>
<table class="spec-table">
<thead>
<tr>
<th scope="col">Component / Peripheral</th>
<th scope="col">ESP32 Pin / Interface</th>
<th scope="col">Function</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Microcontroller</strong></td>
<td>ESP32 (Dual-Core Xtensa @ 160 MHz)</td>
<td>ESP-IDF v5.2.2 + FreeRTOS dual-core execution</td>
</tr>
<tr>
<td><strong>Rotary Encoder CLK / DT</strong></td>
<td><code>GPIO 32</code> / <code>GPIO 33</code></td>
<td>Hardware PCNT quadrature tracking (1000 ns glitch filter)</td>
</tr>
<tr>
<td><strong>Rotary Encoder SW</strong></td>
<td><code>GPIO 16</code> (Active Low)</td>
<td>Toggles knob &amp; OLED HUD between Speed (<code>0.2x–3.0x</code>) and Volume (<code>0%–100%</code>)</td>
</tr>
<tr>
<td><strong>Pause / Next Buttons</strong></td>
<td><code>GPIO 13</code> / <code>GPIO 14</code> (Active Low)</td>
<td>Play/Pause toggle and 16-track playlist cycling</td>
</tr>
<tr>
<td><strong>Passive Buzzer (Melody)</strong></td>
<td><code>GPIO 25</code> (LEDC 13-bit PWM)</td>
<td>Main melody synthesizer driven by hardware LEDC Timer 0</td>
</tr>
<tr>
<td><strong>Active Buzzer 1 &amp; 2 (Drums)</strong></td>
<td><code>GPIO 26</code> (Kick) / <code>GPIO 27</code> (Hi-Hat)</td>
<td>Physical percussion transients on drum steps</td>
</tr>
<tr>
<td><strong>Kick &amp; Hi-Hat LEDs</strong></td>
<td><code>GPIO 21</code> (Kick) / <code>GPIO 22</code> (Hi-Hat)</td>
<td>Visual beat indicators synchronized to drum hits</td>
</tr>
<tr>
<td><strong>1.3" 7-Pin SPI OLED</strong></td>
<td><code>CS: 5, DC: 2, RES: 4, MOSI: 23, SCK: 18</code></td>
<td>10 MHz SPI2 display running custom 30 FPS visualizer on Core 0</td>
</tr>
</tbody>
</table>
</section>
<section class="section-block reading-width" aria-labelledby="p3-playlist">
<span class="section-label">04 // Flash Memory Playlist</span>
<h2 id="p3-playlist">The 16-Track Symphony</h2>
<p>
All 16 tracks are stored in ESP32 flash memory as <code>SequenceStep</code> arrays featuring <code>frequency</code>, <code>duration</code>, <code>kick_drum</code>, and <code>hi_hat</code> triggers:
</p>
<ol>
<li><strong>Careless Whisper</strong> (Extended Club Edit)</li>
<li><strong>Take On Me</strong> (EDM Build-up Edition)</li>
<li><strong>Never Gonna Give You Up / Rickroll</strong> (Double Chorus)</li>
<li><strong>Can't Help Falling In Love</strong> (Wedding Mix)</li>
<li><strong>Gimme! Gimme! Gimme!</strong> (Midnight Rave)</li>
<li><strong>Für Elise</strong> (Beethoven Trance)</li>
<li><strong>Symphony No. 40</strong> (Mozart Intense Strings)</li>
<li><strong>Super Mario Bros.</strong> (Main Theme)</li>
<li><strong>Pirates of the Caribbean 1</strong> (He's a Pirate)</li>
<li><strong>Pirates of the Caribbean 2</strong> (The Black Pearl)</li>
<li><strong>Rondo Alla Turca</strong> (Mozart)</li>
<li><strong>Tetris</strong> (Korobeiniki)</li>
<li><strong>Sonic the Hedgehog</strong> (Green Hill Zone)</li>
<li><strong>Castlevania</strong> (Vampire Killer)</li>
<li><strong>Seven Nation Army</strong> (Transposed +2 Octaves for piezo resonance)</li>
<li><strong>Mega Man 2</strong> (Dr. Wily Stage)</li>
</ol>
</section>
<section class="section-block reading-width" aria-labelledby="p3-learning">
<span class="section-label">05 // Workshop Notes &amp; Next Steps</span>
<h2 id="p3-learning">Current State &amp; Roadmap</h2>
<div class="callout-box">
<span class="callout-title">Current State</span>
<p>
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.
</p>
<span class="callout-title">Next Steps</span>
<p style="margin-bottom:0;">
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.
</p>
</div>
</section>
</article>
</div>
</main>
<footer class="site-footer">
<div class="container footer-inner">
<div>
<strong>Ertunç Özbay</strong> — Personal workshop, project lab, and learning notebook.
</div>
<ul class="footer-links" aria-label="Footer links">
<li><a href="../projects/index.html">Projects</a></li>
<li><a href="../blog/index.html">Blog</a></li>
<li><a href="../lab.html">Lab</a></li>
<li><a href="../contact.html">Contact</a></li>
<li><a href="https://github.com/ertunc-ozbay" rel="noopener noreferrer">GitHub</a></li>
</ul>
</div>
</footer>
<script src="../assets/js/main.js"></script>
</body>
</html>