MIDI Controller
DIY USB MIDI Controller
Author: Gheorghita Vlad-Gabriel
GitHub Project Link: link
Description
A custom USB MIDI controller built using a Raspberry Pi Pico 2W and tactile switches to detect and transmit MIDI signals (Note On/Off + Velocity) to a DAW using the USB MIDI protocol.
Motivation
As someone passionate about composing music and making beats, I've always been curious about how physical MIDI instruments work under the hood. Instead of buying an off-the-shelf MIDI controller, I decided to build one myself to combine my love for music with my skills in electronics and programming. This project allows me to deepen my understanding of how musical interfaces communicate with digital audio workstations and explore creative, cost-effective solutions for real-world problems.
Architecture
Log
Week 5 - 11 May
- Studied all the components to make them work
- Done draft documentation
- First order of hardware
- Done key logic and hardware prototype on breadboard
- Made measurements for 3D printed case, caps and keys
Week 12 - 18 May
- Finished hardware:
- Soldered wires and components on Final PCB
- Connected components on pre-planned pins (check schematic)
- Done final schematic
- 3D printed main case
- Start implementing software:
- Done logic for multiplexing of keys
- Done logic for filter knob (potentiometer)
- Done logic for display
- Done MIDI communication between device and controller
Week 19 - 25 May
- Finished software:
- Tweaked different values (voltage levels for key press, velocity computing etc.)
- Refined logic for each component
- Organized code
- Finished final product:
- 3D printed missing parts (keys, button caps and cover)
- Glued everything together
Hardware
The DIY MIDI controller is built around the Raspberry Pi Pico 2W, with custom-designed circuitry implemented on homemade PCB boards. All components are connected using solid-core wires stripped from a UTP (Ethernet) cable, ensuring reliable point-to-point wiring across the board.
The controller features 12 keys, each equipped with two tactile switches mounted at different elevations. This physical arrangement enables velocity sensitivity by measuring the time interval between the activation of the first and second switches. In total, 24 switches are scanned.
To efficiently read these switches using minimal GPIOs, a 16-channel analog multiplexer is used (12 channels active). Each switch is wired into a voltage divider, producing distinct analog voltage levels depending on press state:
- more than 2650: no press (send Note OFF)
- between 2000–2500: one pressed (start timer)
- less than 1000: both pressed (stop timer, compute velocity, send Note ON)
The multiplexer’s selector lines are controlled via four GPIOs, while the SIG pin is connected to an ADC input on the Pico. Each key is scanned sequentially in a loop, with debouncing and averaging applied.
A potentiometer is connected directly to another ADC pin, providing continuous control input which is mapped to a MIDI filter parameter (e.g., cutoff frequency). Its value is read periodically and translated into MIDI Control Change (CC) messages.
For user interaction, the controller includes:
- An SSD1306 OLED display, driven via I2C, used to render a menu interface for settings such as octave selection and filter CC channel.
- Four momentary buttons connected to GPIOs:
- Menu: enter/exit menu mode
- Select: short/long presses toggle modes or confirm selections
- Up/Down: navigate between options
The menu uses a carousel-style interface, offering intuitive cycling through options with feedback on the screen.
Schematics
Software
The firmware for the MIDI controller is written in Rust, using the embassy async runtime for embedded systems. It targets the Raspberry Pi Pico 2W (RP2040) microcontroller and uses the following major components:
- Note Detection with Velocity
Reads from the buttons under each key via analog multiplexer. Measures time between two switch presses for each key to estimate velocity and sends MIDI note-on events accordingly. - Filter Control via Potentiometer
Continuously samples the potentiometer and sends MIDI CC messages on a user-configurable channel (70–79) if the value has changed significantly. - User Menu System via Button Interface & OLED Display
Uses four buttons (Menu, Select, Up, Down) and an I2C SSD1306 OLED screen to:- Select between Octave and Filter modes.
- Modify current octave (C0–C9).
- Modify the active MIDI filter channel.
- USB MIDI Output
Implements a class-compliant USB MIDI device that sends:- Note on/off messages.
- Control Change messages for the filter.
Library | Description | Usage |
---|---|---|
embassy-rp | Rust + async embedded | Framework for embedded applications using async features on the Raspberry Pi Pico 2W |
ssd1306 | Driver for SSD1306 display | Display menu for filter and octave selection |
embedded-graphics | 2D graphics library | Drawing primitives and text rendering for embedded displays |
static-cell | Allows defining static, lazily initialized values safely | Store USB Config information |
embassy-usb | USB device stack for async embedded Rust | Configure USB connection to device |
defmt | Logging framework for embedded development (formatted debug output) | Debugging Software and Hardware |
Bill of Materials
Device | Usage | Price |
---|---|---|
Raspberry Pi Pico 2W | Main and debug microcontroller | 79.32 RON |
Breadboard Kit HQ 830p | Prototyping and testing | 22.00 RON |
6 x 6 x 6 Push Button | Dual-switch per key for velocity detection | 10.80 RON |
CD74HC4067 16 Channel Analog Multiplexer | Multiplexing of keys | 5.77 RON |
100k Mono Potentiometer | Optional control knob for filter adjustments | 1.49 RON |
Universal PCB Prototype Board 8x12cm | Soldering base for final PCB | 7.98 RON |
Universal PCB Prototype Board 6x8cm | Soldering base for final PCB | 4.98 RON |
Colored 40p 2.54 mm Pitch Male Pin Header | Wiring headers for connections | 2.97 RON |
20p Female Pin Header 2.54 mm | Wiring headers for connections | 12.27 RON |
4p Female Pin Header 2.54 mm | Wiring headers for connections | 0.98 RON |
SSD1306 0.96" OLED DISPLAY | For showing filter and octave menu | 27.37 |
3D Printer materials | Case and keys | 50 RON |
Total | - | 225.93 RON |