As a musician who has amassed a collection of musical instruments and noise boxes, the humble Arduino is the perfect tool to create a custom MIDI controller. Whilst the Raspberry Pi may have taken the crown for Internet of Things (IoT) projects, a simple Arduino Uno (what are the different types of Arduino?) has more than enough power for this project.
First time using an Arduino? No worries, we've got a complete Arduino beginner's guide to read through before you tackle this project.
What is MIDI?
MIDI stands for Musical Instrument Digital Interface. It outlines a standard way for musical devices to communicate with each other. If you own an electronic keyboard you probably have a MIDI interface. Whilst there are a few technical details involved in the implementation of MIDI, it's important to remember that MIDI is not audio! MIDI data is a simple set of instructions (one instruction is called a "message") that another device may implement to make different sounds or control parameters.
MIDI supports 16 channels. This means that each cable can support 16 different devices communicating independently with each other. Devices are connected using a 5-pin DIN cable. DIN stands for "German Institute for Standardization", and is simply a cable with five pins inside the connector. USB is often used in place of 5-pin DIN, or a USB-MIDI interface can be used.
Control Change and Program Change
There are two main types of MIDI message: Control Change, and Program Change.
Control Change (CC) messages contain a controller number and a value between 0 and 127. CC messages are often used to change settings such as volume or pitch. Devices that accept MIDI should come with a manual explaining what channels and messages are setup by default, and how to change them (known as MIDI mapping).
Program Change (PC) messages are simpler than CC messages. PC messages consist of a single number, and are used to change the preset or patch on a device. PC messages are sometimes known as "Patch Change". Similar to CC messages, manufacturers should provide a document outlining what presets are changed by a particular message.
What You Will Need
- Arduino
- 5-pin DIN female socket
- 2 x 220 ohm resistors
- 2 x 10k ohm resistors
- 2 x momentary switches
- Hook-up wires
- Breadboard
- MIDI cable
- MIDI device or USB interface
Build Plan
This project will be quite simple. You can of course add more buttons or hardware to suit your needs. Almost any Arduino will be suitable -- only three pins are needed for this example. This project consists of two buttons to control the program, a MIDI port to send the data, and a device to receive the messages. This circuit has been built on a breadboard here, however it is possible to transfer it to a project box and soldered connectors for a robust solution.
Circuit Assembly
MIDI Connection
Wire up your MIDI socket as follows:
- MIDI pin 5 to Arduino Transmit (TX) 1 via a 220 ohm resistor
- MIDI pin 4 to Arduino +5V via a 220 ohm resistor
- MIDI pin 2 to Arduino ground
Button Connection
The buttons work by changing the resistance the Arduino "sees". The Arduino pin goes through the switch straight to ground (LOW) via a 10k ohm resistor (a "pull down" resistor, ensuring the value stays low). When the button is pressed, the value seen by the circuit changes to +5v without a resistor (HIGH). The Arduino can detect this change using the digitalRead(pin) command. Connect the buttons to pins 6 and 7 on the Arduino digital input/output (I/O). Connect both buttons:
- Left side of button to +5V
- Right side of button to Arduino Ground via a 10k ohm resistor
- Right side of button to Arduino pin (6 or 7)
MIDI Testing
Now that all the hardware is finished, it's time to test it. You will need a USB-MIDI interface (many audio interfaces can do this) and a MIDI cable. The MIDI port wired up on the breadboard is sending data, so it is the output. Your computer is receiving the data, therefore it is the input. This project uses the excellent Arduino MIDI Library v4.2 by Forty Seven Effects. Once you have installed the Library, you can include it in your code by going to Sketch > Include Library > MIDI.
You'll also need a program to monitor the incoming MIDI data:
- MIDI Monitor for OS X
- MIDI-OX for Windows
- KMidimon for Linux
Connect the Arduino to your computer and upload the following test code (don't forget to select the correct board and port from the Tools > Board and Tools > Port menus).
#include <midi.h>
#include <midi_defs.h>
#include <midi_message.h>
#include <midi_namespace.h>
#include <midi_settings.h>
MIDI_CREATE_INSTANCE(HardwareSerial,Serial, midiOut); // create a MIDI object called midiOut
void setup() {
Serial.begin(31250); // setup serial for MIDI
}
void loop() {
midiOut.sendControlChange(56,127,1); // send a MIDI CC -- 56 = note, 127 = velocity, 1 = channel
delay(1000); // wait 1 second
midiOut.sendProgramChange(12,1); // send a MIDI PC -- 12 = value, 1 = channel
delay(1000); // wait 1 second
}
This code will send a CC message, wait 1 second, send a PC message then wait 1 second indefinitely. If everything is working correctly you should see a message appear in your MIDI monitor.
If nothing happens, don't panic! Try troubleshooting:
- Ensure all the connections are correct
- Check the MIDI port is wired correctly - there should be 2 spare pins on the outside edges
- Double-check the circuit is correct
- Verify the circuit is connected to a USB-MIDI interface with a MIDI cable
- Check your MIDI cable is connected to the input on your USB-MIDI interface
- Make sure the Arduino has power
- Install the correct driver for your USB-MIDI interface
If you are still having problems it might be worth checking your breadboard. Cheap boards can sometimes be very inconsistent and low-quality -- it happened to me whilst working on this project.
Button Testing
Now it's time to test the buttons are working correctly. Upload the following test code. MIDI does not need to be connected to test this part.
const int buttonOne = 6; // assign button pin to variable
const int buttonTwo = 7; // assign button pin to variable
void setup() {
Serial.begin(9600); // setup serial for text
pinMode(buttonOne,INPUT); // setup button as input
pinMode(buttonTwo,INPUT); // setup button as input
}
void loop() {
if(digitalRead(buttonOne) == HIGH) { // check button state
delay(10); // software de-bounce
if(digitalRead(buttonOne) == HIGH) { // check button state again
Serial.println("Button One Works!"); // log result
delay(250);
}
}
if(digitalRead(buttonTwo) == HIGH) { // check button state
delay(10); // software de-bounce
if(digitalRead(buttonTwo) == HIGH) { // check button state again
Serial.println("Button Two Works!"); // log result
delay(250);
}
}
}
Run this code (but keep the USB cable connected) and open the Serial Monitor (Top Right > Serial Monitor). When you press a button you should see "Button One Works!" or "Button Two Works!" depending on the button you pressed.
There is one important note to take-away from this example - the software de-bounce. This is a simple 10 millisecond (ms) delay between checking the button and then checking the button again. This increases the accuracy of the button press and helps prevent noise triggering the Arduino. You do not have to do this, although it is recommended.
Creating the Controller
Now that everything is wired and working, it's time to assemble the full controller.
This example will send a different CC message for each button that is pressed. I'm using this to control Ableton Live 9.6 on OS X. The code is similar to both the testing samples above.
#include <MIDI.h>
#include <midi_Defs.h>
#include <midi_Message.h>
#include <midi_Namespace.h>
#include <midi_Settings.h>
const int buttonOne = 6; // assign button pin to variable
const int buttonTwo = 7; // assign button pin to variable
MIDI_CREATE_INSTANCE(HardwareSerial,Serial, midiOut); // create a MIDI object called midiOut
void setup() {
pinMode(buttonOne,INPUT); // setup button as input
pinMode(buttonTwo,INPUT); // setup button as input
Serial.begin(31250); // setup MIDI output
}
void loop() {
if(digitalRead(buttonOne) == HIGH) { // check button state
delay(10); // software de-bounce
if(digitalRead(buttonOne) == HIGH) { // check button state again
midiOut.sendControlChange(56,127,1); // send a MIDI CC -- 56 = note, 127 = velocity, 1 = channel
delay(250);
}
}
if(digitalRead(buttonTwo) == HIGH) { // check button state
delay(10); // software de-bounce
if(digitalRead(buttonTwo) == HIGH) { // check button state again
midiOut.sendControlChange(42,127,1); // send a MIDI CC -- 42 = note, 127 = velocity, 1 = channel
delay(250);
}
}
}
Note -- you will not be able to use Serial.println() with MIDI output.
If you wanted to send a PC message instead of a CC simply replace:
midiOut.sendControlChange(42,127,1);
With:
midiOut.sendProgramChange(value, channel);
In Action
Below is a demonstration as a controller for Ableton Live (Best DJ software for every budget). The top right shows the audio meters, and the top middle shows the incoming midi messages (via MIDI Monitor on OS X).
Have you Made a MIDI Controller?
There are a lot of practical uses for a custom MIDI controller. You could build a vast foot-controlled unit, or a sleek studio controller. And if you're interested in purchasing one, here are the best USB MIDI controllers you can buy.
Image Credit: Keith Gentry via Shutterstock.com