Building a MIDI Controller / MacroPad

I love tinkering with the Raspberry Pi Pico, since it is an easy starting point for anyone that wants to get into microcontrollers. In this detailed instructable I want to guide you how you can create your own MIDI controller or MacroPad. If you are a musician and in need of a cheap but awesome-looking MIDI pad then this project for you, or if you are just someone who is in need of a new MacroPad but you don’t want to buy the generic cherry mx keys kits but would prefer some classic arcade buttons then you are in luck, because we can set the Raspberry Pi Pico to do either of those two functions.

If you are not sure what a MIDI controller or MacroPad is, let me break it down for you:

MIDI controller:

A MIDI controller is a simple way to sequence music and play virtual instruments on your Mac or PC. It works by sending MIDI data (Musical Instrument Digital Interface) to a computer or synthesizer, which then interprets the signal and spits out a sound.

MacroPad:

A macro pad can give you plenty of buttons for your macros. And, with most pads coming with some form of software for recording macros, program-specific actions can also be performed, usually called “shortcuts.” Shortcuts can do a variety of power things like launch programs, turn on and off your microphone, and more.

Supplies

To make this instructable as detailed as possible, I will include links from AliExpress, these are not affiliated links but will guide you on where to start. You might already have some of the components. For the wood it might be that you do not have access to a laser cutter, but there is online services that provide laser cutting, or alternatively the enclosure can be 3D printed, or made out of cardboard, the enclosure is limited to your own creativity.

Parts:

16 * Arcade Buttons (24 or 28 mm) – Link

4 * M3 Hex Brass Spacing Screws Threaded Pillar 50mm – Link

4 * M3 Nut and Screw 6mm Length – Link

1 * Raspberry Pi Pico + Breadboard + Cables – Link

1 * 2m – 20 AWG single core wire – Link

Material:

3mm wood + Black spray-paint

Equipment:

Ortur 2 Laser Master

Optional:

3D printer, tools for cutting cardboard or wood if laser cutter is not used.

Note:

The above should be used as a guidance of what will be needed to complete this project or make something similar, if your budget does not allow you to create this you can replace the arcade and enclosure and just use tactile switches on a breadboard.

Step 1: Enclosure Design

Enclosure Design

The enclosure was designed in Fusion360. The reason for choosing this software is that it is free to use with a hobbyist license. I have included all the files that you will need to cut. If not possible to cut the enclosure out of wood, alternatively 3D print or create your own enclosure from different materials.

The enclosure is very simple design which consist of the top base to house the buttons and spacers to bottom base where you will have your Raspberry Pi Pico secured.

I have included all the steps in GIFS for you to follow along but in short.

  1. Make a square 220*220 mm
  2. Create a 30 diameter circle 42.5mm from the bottom and corner.
  3. Now duplicate this circle using the Rectangular Pattern function, setting the Distance Type -> Spacing. Set the quantity to 4 and distance 45 do to for the +x and +y direction.
  4. Using the fillet tool round the edges with a 30mm radius.
  5. Final step is to create holes for spacers from the outer corner circle add a 3mm hole a distance of 30 mm away from the circle center.

Now all that is left to do is either extrude and 3D print the enclosure or save the file to .dxf and use a laser cutter. The alternative will be to print this and stick on wood and cut out using power tools.

To give the enclosure a great looking finish I used some black spray-paint and give it 1-2 coats.

Step 2: Raspberry Pi Pico + Wiring

Raspberry Pi Pico + Wiring

Let’s first look at the Raspberry Pi Pico. The Pico has 26 are multipurpose GPIOs (general-purpose input/output) which for our application is perfect since we only need 16 inputs. The pinout of the Pico is given in Figure 1. We can use any GPIO pins to connect the arcade buttons, and I have included the connections I have made shown in the schematic diagram Figure 2. I decided to use the breadboard to make to process easier, and will on a later stage make a PCB to making things easier.

One side of the arcade buttons will be connected to the 3.3V power by the Pico’s 3V3 output on pin 36. Using the 20 AWG wire it was stripped clean of insulation and then connected to one pin of the buttons where after the connections was made proper by soldering the wire to the pin to make good connection. This is shown in the GIF.

The rest of the arcade buttons we will use Male-Female jumper wires and cut of the plug end and solder them to the remaining pin for each of the 16 buttons. We now can connect it to the Pico as indicated in the schematic.

We will test for a HIGH input from our arcade buttons. The buttons will be pulled down to ground by internal pull-down resistors and be LOW, and when a button is pressed the 3V3 will be detected by the pin and set to HIGH and trigger an event.

Step 3: Nuke Raspberry Pi Pico

There are some circumstances where you might want to make sure your Flash memory is empty. You can do this by dragging and dropping a special UF2 binary onto your Pico when it is in mass storage mode. We will do this to ensure that there is no data in memory that will cause errors.

I have included all the relevant files and code on my GitHub repository: here.

In this repository download the flash_nuke.uf2 file.

Start with your Pico unplugged from USB. Hold down the BOOTSEL button, and while continuing to hold it (don’t let go!), plug the Pico into USB. A short GIF above illustrates this step. Continue to hold the BOOTSEL button until the RPI-RP2 drive appears.

You will see a new disk drive appear called RPI-RP2.

Drag the flash_nuke.uf2 file to RPI-RP2.

Step 4: Installing CircuitPython

CircuitPython is a derivative of MicroPython designed to simplify experimentation and education on low-cost microcontrollers. We will use a library from the adafruit libraries for CircuitPython the HID library which will allow our Pico to be a Human Interface Device making it possible for us to receive input through a button push and send custom macro keycodes to the computer.

In this repository download the adafruit-circuitpython-raspberry_pi_pico file.

Start with your Pico unplugged from USB. Hold down the BOOTSEL button, and while continuing to hold it (don’t let go!), plug the Pico into USB. A short GIF above illustrates this step. Continue to hold the BOOTSEL button until the RPI-RP2 drive appears.

You will see a new disk drive appear called RPI-RP2.

Drag the adafruit-circuitpython-raspberry_pi_pico-en_US-7.2.3.uf2 file to RPI-RP2.

Once you have installed CircuitPython is will show up as a flash Drive named CircuitPy, inside the folder lib copy the adafruit_hid and adafruit_midi folder and paste it in this folder.

Step 5: MIDI Controller Code Explanation

The Keys are layout as follows:

'''
This is the layout for the MIDI PAD with the Raspberry Pi Pico:


         key[3]     Key[2]    Key[1]   Key[0]


         key[7]     Key[6]    Key[5]   Key[4]


         key[11]     Key[10]    Key[9]  Key[8]


         key[15]    Key[14]   Key[13]  Key[12]


'''

Take note of these as you will need it if you want to change the midi_notes.

We start of by uploading all the needed libraries

import time
import board
import terminalio
import busio
import digitalio
import usb_midi
import adafruit_midi
from adafruit_midi.note_on          import NoteOn
from adafruit_midi.note_off         import NoteOff

Next we will setup the midi to act as a USB MIDI output device. midi_out sends notes out from the device.

midi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=0)

The pins used for the arcade buttons are stored in the note_pins array.

They are setup to be digital inputs in the for statement and are then stored in the note_buttons array.

Each arcade button has a state setup for debouncing. These states are stored in the note_states array.

note_pins = [board.GP0, board.GP1,board.GP2,board.GP3,
             board.GP4,board.GP5,board.GP6, board.GP7,
             board.GP8,board.GP9,board.GP10,board.GP11,
             board.GP12,board.GP13,board.GP14,board.GP16]

note_buttons = []

for pin in note_pins:
    note_pin = digitalio.DigitalInOut(pin)
    note_pin.direction = digitalio.Direction.INPUT
    note_pin.pull = digitalio.Pull.DOWN
    note_buttons.append(note_pin)

#  note states
note0_pressed = False
note1_pressed = False
note2_pressed = False
note3_pressed = False
note4_pressed = False
note5_pressed = False
note6_pressed = False
note7_pressed = False
note8_pressed = False
note9_pressed = False
note10_pressed = False
note11_pressed = False
note12_pressed = False
note13_pressed = False
note14_pressed = False
note15_pressed = False


#  array of note states
note_states = [note0_pressed, note1_pressed, note2_pressed, note3_pressed,
               note4_pressed, note5_pressed, note6_pressed, note7_pressed,
               note8_pressed, note9_pressed, note10_pressed, note11_pressed,
               note12_pressed, note13_pressed, note14_pressed, note15_pressed]

The midi_notes array holds the default MIDI notes that are assigned to the arcade buttons. If you want to change the MIDI notes you need to edit the array to the midi note you would want to use.

midi_notes = [60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75]

The arcade buttons send their assigned MIDI note number out with a MIDI NoteOn message when they are pressed.

When the arcade button is released, a NoteOff message is sent.

Now we create an endless loop to loop through our 16 buttons to see if any button is pressed, if a button is pressed, it will assign MIDI note number out with a MIDI NoteOn message.

while True:

    #  MIDI input
    for i in range(16):
        buttons = note_buttons[i]
        #  if button is pressed...
        if buttons.value and note_states[i] is True:
            #  send the MIDI note
            midi.send(NoteOn(midi_notes[i], 120))
            note_states[i] = False
            print(midi_notes[i])
            
        #  if the button is released...
        if not buttons.value and note_states[i] is False:
            #  stop sending the MIDI note
            midi.send(NoteOff(midi_notes[i], 120))
            note_states[i] = True

Now run this code, and we can go to FL studio to set up our midi as a drum pad or anything else that you want, but we will focus on a drum pad for this instructable.

Source: Building a MIDI Controller / MacroPad


About The Author

Muhammad Bilal

I am highly skilled and motivated individual with a Master's degree in Computer Science. I have extensive experience in technical writing and a deep understanding of SEO practices.