WIKI: How to make a DIY MIDI controller easily

This WIKI is a tutorial series i am writing to assist those that want to make a midi controller.

Feel free to ask questions or ask for examples in this thread, i will be making new replies on topics in the future and will link them to this first post. This way we can have a discussion about this topic, can ask and answer questions, but we can also keep organized with the tutorial posts. Feel free to make requests for topics to be covered, but i already have a rough outline for what will be covered, since we want to make sure certain things are well understood first.

I am targeting this WIKI to people that may not be very comfortable coding. I aim to keep this an example focused tutorial series.

Don’t fuss too much about whether it is “elegant”. If it feels like a hack but it works, remember that a CPU is literally a rock we trick into thinking.


Installing and configuring coding environment
Quick example of single CC button


Installing and configuring coding environment.

You will need an arduino nano/uno, or a teensy chip. Other chips are supported by the library, but these are the ones I am going to cover.

Here are some links to buy them. The clone nanos Tayda sells work just fine:

Teensy 3.2:

Arduino NANO:

If you have (Arduino Nano or UNO), Download Arduino IDE:

  1. Go here:
  2. Download the version for your operating system.
  3. Contribute if you want, or just click “Just Download”.
  4. Install using the downloaded package.

If you have (Teensy 3.2+), Download Teensyduino:

  1. Go here:
  2. Follow install instructions on the page:

No matter what, you are going to end up with a very similar looking program with which to code in.

When you first open up the arduino IDE, this is what will greet you:

Next, you want to tell the program what board you are going to be programming for.

For uno and nano this is how you get to the option:

For teensy, this is the path to select your board. if you have a different version than 3.2, select the appropriate one.

Next we need to download and add the Control Surface library to our coding environment, since it is not a standard library that comes with the software we just downloaded.

  1. Go here:
  2. If you arn’t familiar with using github of git, dont worry you dont need to do much. Just click the green dropdown and click download zip.
  3. Next just add the library using the path in the image below. In the box that opens up, just navigate to where you downloaded the zip file, select it and hit the open button. What this does for you is extract this zip and place it into arduino’s libraries path. (This is found in C:\Users%UserProfile%\Documents\Arduino\libraries on windows for example.) You dont need to go through that unless you want to remove libraries later.

The coding environment should be good to go for now!


Any experience with Teensy 4.0? How does it compare with 3.2?


No personal experience, but that teensy is compatible with this library’s MIDIUSB lib unsurprisingly. The pinouts are identical, its all just a few extra features and clock speeds I reckon. Sparkfun has a writeup. The thing is, most basic controller functions are fine with just a nano. Although witht he nano, you cant use the MIDI over USB functionality. i dont know what benefit you’d get from going 4.0 you dont already get just from picking a teensy already. Unless you plan on doing a lot of floating point arithmetic. Which i argue you can just work around for midi controller work.,4.0%20consumes%20approximately%20100mA%20current.&text=If%20a%20coin%20cell%20is,while%20the%20power%20is%20off.


Maybe no advantage for a MIDI controller, more for synthesis…


I have not experimented with using the teensy as an audio interface (implemented in 5 lines of code with a DAC like the PCM5102), but this library can help in that regard as well. Again, it comes down to what you need. From my research i think even for synth work a 3.2 is going to be overkill and thats great cause it means you can be a little less efficient and get something working.

It all really depends on what you need it for, but even the 3.2 is fast enough to process audio speed.

Here is a link to an article talking about I2S DACs:


Quick example of a single CC button

This example comes directly from the github wiki for the library itself:

MIDI itself is a protocol one can use to control instruments like hardware synths and even software. It allows for up to 16 “channels” at a time. Right now, just think of those channels as different separators that give us some organization.

MIDI CC (Control Change) messages have a number which indicate what the conventions of MIDI say the message should be used to control, and a value to be sent. It is important to know that these Control Number parameters are just convention, not rules. That said, the convention exists with the reason that you may encounter some undesirable behavior if you hook up a controller you made to some other software that expects a specific thing.

Here is a handy list:

Here is the simple hookups for each board:

Code Example:

#include <Control_Surface.h> // Include the Control Surface library
// Instantiate a MIDI over USB interface.
USBMIDI_Interface midi;
// Instantiate a CCButton object
CCButton button = {
  // Push button on pin 5:
  // General Purpose Controller #1 on MIDI channel 1:
  {MIDI_CC::General_Purpose_Controller_1, CHANNEL_1},
void setup() {
  Control_Surface.begin(); // Initialize Control Surface
void loop() {
  Control_Surface.loop(); // Update the Control Surface

Code Breakdown:


#include <Control_Surface.h>

This line will take all that code in the library, and make it available for you to use in your project.

Midi Interface Initialization

USBMIDI_Interface midi;

This line will create a new midi “interface” using USB. All you need to know here is that this is what will enable communication from your controller to what’s being controlled. There are a few different types of interface to choose from, and depending on the board you selected, you may be limited in which you can select.

If using a nano or uno board, you have to select a serial based midi interface for use with a 5-pin MIDI din cable like this:

The definition for this interface is instead like this, so if that applies, just use this instead:

// Select the serial port to use.
auto &serial = Serial;
// Instantiate a Serial MIDI interface at the default MIDI baud rate.
SerialMIDI_Interface<decltype(serial)> midi = {serial, MIDI_BAUD};

The full list can be found here:


CCButton button = {
  {MIDI_CC::General_Purpose_Controller_1, CHANNEL_1},

This is just defining all of the details about your button (or other components later down the road).

CCButton is the type of MIDI output element you want to define. You give it the name “button”, but to define more, you will want to name them differently.

After the equals sign, there is a comma separated list of parameters you specify inside of curly braces, including an entry which itself a comma separated list of items in curly braces.

So if i wanted to define more buttons and assign them different names, digital pins on the microcontroller, MIDI CC Numbers and MIDI channels, can do it like this:

CCButton name_of_button_01 = {
  {MIDI_CC::General_Purpose_Controller_1, CHANNEL_1},

CCButton name_of_button_02 = {
  {MIDI_CC::General_Purpose_Controller_2, CHANNEL_1},

CCButton name_of_button_03 = {
  {MIDI_CC::General_Purpose_Controller_1, CHANNEL_2},

CCButton name_of_button_04 = {
  {MIDI_CC::General_Purpose_Controller_2, CHANNEL_2},

Notice that i have the same CC numbers “General_Purpose_Controller_1” in two channels. This is a fine way to get more CC numbers without using unconventional numbers. Also, you are allowed to either specify the actual number or this human readable constants already defined for us in the library.


The list of constants available are found here:

Setup Function

void setup() {
  Control_Surface.begin(); // Initialize Control Surface

Arduino code has two functions that are standard. The setup() function is run once and will usually be used to start things up in an initial state. This is exactly what this code here does for the control surface. Dont worry about what it does, just know you need to plop that Control_Surface.begin() function in the setup function.

Loop Function

void loop() {
  Control_Surface.loop(); // Update the Control Surface

The other standard function you find is the loop() function. This is where things well… loop. The Control Surface Library handles all of the update routines to send and receive midi messages here using its Control_Surface.loop() function.

Its really that simple.


Thanks for taking the time to write all of this!


Thanks I might give it a go. :smile:


This just came up, I know nothing about it:


That’s an impressive amount of code. I’ve got a couple of Mega2560s somewhere. Might be fun to play with it…one day.

Yeah I saw that when I first started.

The GUI configuration is pretty interesting, and i was wondering myself how best to enable users to configure hardware i would make. However, I thought the price could be reduced.

That said, the features are great, and the board has a lot of connectivity.


The software is open source so you’re not tied to their choice of hardware. You can apparently run it on an Arduino Mega2560, which is available locally sourced in the UK for less than £10 postage free.


I’m glad I spotted this in the email round-up, because this is something I’m working on right now! (Still got protective film on the buttons…)

Internally it’s an Arduino Mega clone with mocoLUFA on the 16u2 chip so that I can swap between Arduino and USB Midi. I used Pimp my mocoLUFA to build the firmware so that I could give it a descriptive name in the list of USB devices. You need to be happy programming the chip with the 6 pin ICSP headers to do it, which is likely to be the only stumbling block most people will find.

I’ve been planning on using either the Control Surface library or a combination of hardware handling libraries because I want to be able to do some coding directly on the board, like remapping the 16 buttons to do different tasks. That’s why there’s a suggestion of a piano keyboard layout of little buttons at the top, under the OLED and rotary encoder - as an untalented, uncoordinated meat bag, being able to assign chords/arpeggios to the buttons will let me circumvent my failings :laughing:

Regarding OpenDeck, I’ve used it quite a bit, although never completed a project using it. The developer Igor is really, really helpful and has taken the project a long way. The official board was expensive because he had to support the development somehow; the official board isn’t available any more as he’s been moving on to more powerful architectures (case in point - STM32 support has landed in v5) and I gather from his Instagram that he’s making money building bespoke controllers with OpenDeck inside right now. It’s a really good platform to work on though, if a bit complicated to get the initial installation done.


Lovin seeing these examples!


I added a link to the official PRJC web page for the Teensy, in the Wiki. (It is meant to be edited, right?)

I was going to do the same for the Arduino Nano, but while trying to get a clean link to share, this happened:

The owner of this website ( has banned you temporarily from accessing this website.

So, I guess they just gone and f***ed themselves.


This is rad! thank you, I’m inspired. Maybe those couple years of trying to code crappy video games will come in use :sweat_smile:


Should be fine, it would be nice if i wasn’t at this alone.

1 Like