šŸ’” Electron Parade

Project 24: DIY Arduino MIDI Controller

Whether you’re producing beats or performing live, having physical knobs and sliders makes music much more intuitive. But commercial MIDI controllers are expensive and never have the exact layout you want. So, we are going to build a 100% custom USB MIDI Controller from scratch. You decide exactly how many buttons you want, wire them all up, and then immediately regret not just buying one.

Why Not the Arduino Uno?

Before we start, there is a crucial hardware requirement: We cannot use the standard Arduino Uno for this project.

The Uno uses an ATmega328P microcontroller, which does not have native USB communication capabilities. Instead, we will use the Arduino Pro Micro (or Leonardo), which is built around the ATmega32U4 chip. This chip has native USB, meaning when you plug it into your computer, it can easily pretend to be a mouse, a keyboard, or—in our case—a standard, plug-and-play USB MIDI Device. No extra drivers or middle-man software required!

Parts List

Here is the hardware you will need to build a versatile drum pad and mixing controller:

The Schematic & Wiring

Wiring a MIDI controller is highly repetitive but very simple.

1. Wiring the Potentiometers (Analog Inputs)

Both rotary knobs and slide faders are just variable resistors (potentiometers). They each have three main pins:

  1. GND: Connect to the Arduino’s GND pin.
  2. VCC: Connect to the Arduino’s VCC (5V) pin.
  3. Signal/Wiper: Connect to one of the Arduino’s Analog Input pins (e.g., A0, A1, A2, A3).

Tip: You can daisy-chain the 5V and GND lines across all your potentiometers to save wiring space.

2. Wiring the Arcade Buttons (Digital Inputs)

Arcade buttons are simple momentary switches.

  1. Connect one terminal of every button to the Arduino’s GND.
  2. Connect the other terminal of each button to individual Digital Input pins on the Arduino (e.g., D2, D3, D4, etc.).

We will use the Arduino’s internal pull-up resistors in the code (INPUT_PULLUP), so no external resistors are needed for the buttons!

The Code

To make the Arduino speak MIDI over USB, we need to install the MIDIUSB library.

  1. Open the Arduino IDE.
  2. Go to Sketch > Include Library > Manage Libraries…
  3. Search for MIDIUSB and install the one by Gary Grewal.

Here is a simplified version of the code that reads one button (sending a MIDI Note for a drum hit) and one potentiometer (sending a MIDI Control Change for a filter/volume fade):

#include "MIDIUSB.h"

// Define Pins
const int buttonPin = 2;
const int potPin = A0;

// Variables for state tracking
int lastButtonState = HIGH; 
int lastPotValue = -1;

void setup() {
  // Setup button with internal pull-up
  pinMode(buttonPin, INPUT_PULLUP);
}

void loop() {
  readButtons();
  readPots();
  delay(5); // Small delay to debounce and prevent USB flooding
}

void readButtons() {
  int currentState = digitalRead(buttonPin);
  
  // Button was just pressed (goes LOW because of pull-up)
  if (currentState == LOW && lastButtonState == HIGH) {
    noteOn(0, 36, 127);   // Channel 0, Note 36 (Kick Drum), Velocity 127
    MidiUSB.flush();
    lastButtonState = LOW;
  } 
  // Button was just released
  else if (currentState == HIGH && lastButtonState == LOW) {
    noteOff(0, 36, 0);    // Channel 0, Note 36, Velocity 0
    MidiUSB.flush();
    lastButtonState = HIGH;
  }
}

void readPots() {
  // Read analog value (0-1023)
  int currentPotValue = analogRead(potPin);
  
  // Map the 10-bit analog value to 7-bit MIDI value (0-127)
  int midiValue = map(currentPotValue, 0, 1023, 0, 127);
  
  // Only send a new MIDI message if the value has changed
  if (midiValue != lastPotValue) {
    controlChange(0, 10, midiValue); // Channel 0, CC 10 (Pan/Volume), Value
    MidiUSB.flush();
    lastPotValue = midiValue;
  }
}

// --- MIDI Protocol Helper Functions ---

void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
}

void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
  MidiUSB.sendMIDI(event);
}

Expanding the Code

To add more buttons and knobs, simply turn buttonPin, potPin, lastButtonState, and lastPotValue into arrays, and use for loops inside the readButtons() and readPots() functions to iterate through them.

Wrapping It Up

Once you upload the code, plug the Pro Micro into your computer, open your favorite Digital Audio Workstation (DAW), and go to your MIDI settings. Your Arduino will show up automatically as a generic MIDI device! You can use your DAW’s ā€œMIDI Learnā€ feature to instantly map your new arcade buttons to drum samples and your faders to track volumes or synthesizer filters.

The best part of this project is the physical build. Drill holes into a cigar box, 3D print an angled mixing console, or mount it into an old vintage suitcase. The layout is entirely in your hands!