DIY MIDI Controller

3D Print and build your own MIDI foot pedal to create expression to your musical performances! Use CircuitPython and QT Py RP2040 to make a MIDI controller that you can assign to any MIDI CC!

MIDI Control Change

Use the MIDI library for CircuitPython to create a custom MIDI controller that can trigger and control MIDI CC's (control change). Use MIDI music software to assign your MIDI controller to any number of synths and effects to adjust parameters.

Prerequisite Guides

Take a moment to walk through the following guides.


Video of hand holding a QT Py PCB in their hand. An LED glows rainbow colors.
What a cutie pie! Or is it... a QT Py? This diminutive dev board comes with one of our new favorite chip, the RP2040. It's been made famous in the new
In Stock
Angled shot of a pink/purple woven USB cable plugged into a laptop port and a small dev board.
This cable is not only super-fashionable, with a woven pink and purple Blinka-like pattern, it's also made for USB C for our modernized breakout boards, Feathers and more. 
In Stock
Opened box showing many nylon screws
Totaling 420 pieces, this M3 Screw Set is a must-have for your workstation. You'll have enough screws, nuts, and hex standoffs to fuel...
Out of Stock
1 x Rubber Feet
Rubber Bumper Feet - Pack of 4
1 x 10-Wire Ribbon Cable
Silicone Cover Stranded-Core - 10 Wire 1 Meter Long - 28AWG Black
2 x M3 x 16mm Screws
M3 x 16mm Screws
2 x M3 Lock Nuts
M3 x 0.5mm Nylon Insert Lock Nut

The diagram below provides a visual reference for wiring of the components. This diagram was created using the software package Fritzing.

Adafruit Library for Fritzing

Use Adafruit's Fritzing parts library to create circuit diagrams for your projects. Download the library or just grab individual parts. Get the library and parts from GitHub - Adafruit Fritzing Parts.

Wired Connections

Potentiometer to QT Py RP2040

  • Pin 1 to Pin A0 on QT Py RP2040
  • Pin 2 to GND on QT Py RP2040
  • Pin 3 to 3V on QT Py RP2040

CircuitPython is a derivative of MicroPython designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the CIRCUITPY drive to iterate.

CircuitPython Quickstart

Follow this step-by-step to quickly get CircuitPython running on your board.

Click the link above to download the latest CircuitPython UF2 file.

Save it wherever is convenient for you.

To enter the bootloader, hold down the BOOT/BOOTSEL button (highlighted in red above), and while continuing to hold it (don't let go!), press and release the reset button (highlighted in blue above). Continue to hold the BOOT/BOOTSEL button until the RPI-RP2 drive appears!

If the drive does not appear, release all the buttons, and then repeat the process above.

You can also start with your board unplugged from USB, press and hold the BOOTSEL button (highlighted in red above), continue to hold it while plugging it into USB, and wait for the drive to appear before releasing the button.

A lot of people end up using charge-only USB cables and it is very frustrating! Make sure you have a USB cable you know is good for data sync.

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


Drag the adafruit_circuitpython_etc.uf2 file to RPI-RP2.

The RPI-RP2 drive will disappear and a new disk drive called CIRCUITPY will appear.

That's it, you're done! :)

Safe Mode

You want to edit your or modify the files on your CIRCUITPY drive, but find that you can't. Perhaps your board has gotten into a state where CIRCUITPY is read-only. You may have turned off the CIRCUITPY drive altogether. Whatever the reason, safe mode can help.

Safe mode in CircuitPython does not run any user code on startup, and disables auto-reload. This means a few things. First, safe mode bypasses any code in (where you can set CIRCUITPY read-only or turn it off completely). Second, it does not run the code in And finally, it does not automatically soft-reload when data is written to the CIRCUITPY drive.

Therefore, whatever you may have done to put your board in a non-interactive state, safe mode gives you the opportunity to correct it without losing all of the data on the CIRCUITPY drive.

Entering Safe Mode

To enter safe mode when using CircuitPython, plug in your board or hit reset (highlighted in red above). Immediately after the board starts up or resets, it waits 1000ms. On some boards, the onboard status LED (highlighted in green above) will blink yellow during that time. If you press reset during that 1000ms, the board will start up in safe mode. It can be difficult to react to the yellow LED, so you may want to think of it simply as a slow double click of the reset button. (Remember, a fast double click of reset enters the bootloader.)

In Safe Mode

If you successfully enter safe mode on CircuitPython, the LED will intermittently blink yellow three times.

If you connect to the serial console, you'll find the following message.

Auto-reload is off.
Running in safe mode! Not running saved code.

CircuitPython is in safe mode because you pressed the reset button during boot. Press again to exit safe mode.

Press any key to enter the REPL. Use CTRL-D to reload.

You can now edit the contents of the CIRCUITPY drive. Remember, your code will not run until you press the reset button, or unplug and plug in your board, to get out of safe mode.

Flash Resetting UF2

If your board ever gets into a really weird state and CIRCUITPY doesn't show up as a disk drive after installing CircuitPython, try loading this 'nuke' UF2 to RPI-RP2. which will do a 'deep clean' on your Flash Memory. You will lose all the files on the board, but at least you'll be able to revive it! After loading this UF2, follow the steps above to re-install CircuitPython.

Once you've finished setting up your QT Py RP2040 with CircuitPython, you can access the code and necessary libraries by downloading the Project Bundle.

To do this, click on the Download Project Bundle button in the window below. It will download as a zipped folder.

# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries
# SPDX-License-Identifier: MIT

import time
import board
import usb_midi
import adafruit_midi
import simpleio
from analogio import AnalogIn
from adafruit_midi.control_change import ControlChange

#  midi setup
midi = adafruit_midi.MIDI(
    midi_in=usb_midi.ports[0], in_channel=0, midi_out=usb_midi.ports[1], out_channel=0

#  potentiometer setup
mod_pot = AnalogIn(board.A0)

#  function to read analog input
def val(pin):
    return pin.value

#  variables for last read value
#  defaults to 0
mod_val2 = 0

while True:

    #  Print out the min/max values from potentiometer to the serial monitor and plotter

    #  map range of potentiometer input to midi values - update the min/max values below
    mod_val1 = round(simpleio.map_range(val(mod_pot), 0, 65535, 0, 127))

    #  if modulation value is updated...
    if abs(mod_val1 - mod_val2) > 2:
        #  update mod_val2
        mod_val2 = mod_val1
        #  create integer
        modulation = int(mod_val2)
        #  create CC message
        modWheel = ControlChange(1, modulation)
        #  send CC message

Upload the Code and Libraries to the QT Py RP2040

After downloading the Project Bundle, plug your QT Py RP2040 into the computer's USB port. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called CIRCUITPY. Unzip the folder and copy the following items to the QT Py RP2040's CIRCUITPY drive. 

  • lib folder

Your QT Py RP2040 CIRCUITPY drive should look like this after copying the lib folder and the file.

Adjust Min/Max Values

In the code, values from the potentiometer are mapped to a range of MIDI CC values.

Use the serial monitor and plotter in Mu Editor to see the values from the potentiometer and set the minimum and maximum values.

Change the first and second number values in the map_range function to map the potentiometer to the range of control change (values 0-127).

CAD Parts List

STL files for 3D printing are oriented to print "as-is" on FDM style machines. Parts are designed to 3D print without any support material. Original design source may be downloaded using the links below:

  • Top-Pedal.stl
  • Base-Frame.stl
  • Bottom-Plate.stl
  • QTPy-Holder.stl
  • Pot-Holder.stl
  • Pot-Linkage.stl
  • Hinge-Plate.stl
  • Pedal-Arm.stl
  • washer-cap.stl
  • washer-middle.stl
  • washer-sleeve.stl

Hinge Assembly

Sleeve washers are installed to the top pedal. Middle washers are fitted over the sleeve washers. The hinge plate is installed in between the sleeve washers. Long M3 screws are inserted through the sleeve washers. Washer caps are then installed. M3 nylon insert lock nuts are then added to secure parts.

Foot Pedal Assembly

The hinge plate is secured to the base frame with M3 screws and hex nuts. The QT Py snap fits into its holder. The potentiometer is secured its holder. The holders are secured to the bottom plate. The bottom plate is secured to the base frame with M3 screws and hex nuts.

Build Volume

The parts require a 3D printer with a minimum build volume.

  • 210mm (X) x 210mm (Y) x 100mm (Z)

Design Source Files

The project assembly was designed in Fusion 360. This can be downloaded in different formats like STEP, STL and more. Electronic components like Adafruit's board, displays, connectors and more can be downloaded from the Adafruit CAD parts GitHub Repo.

Wire for Potentiometer

Use a 3-wire ribbon cable to connect the potentiometer to the QT Py RP2040. Measure and cut the cable to 9cm(3.5in) in length. 

Solder Cable to QT Py RP2040

Connect the 3-wire ribbon cable to 3V, GND and A0 pins on the QT Py RP2040.

Solder Cable to Potentiometer

Connect the wire from A0 pin on QT Py RP2040 to the middle pin on the potentiometer. The 3V and GND wires are connected to the far left and right pins on the potentiometer.

Soldered QT Py RP2040 and Potentiometer

Double check the wires are properly soldered to the potentiometer and QT Py 2040.

Install Potentiometer to Holder

Use a pair of pliers to remove the registration nub from the body of the potentiometer. Unfasten the hex nut and washer from the threaded shaft. 

Insert the shaft through the potentiometer holder. Reference the image for the correct placement and orientation.

Secure Potentiometer to Holder

Insert the washer and hex nut on to the threaded shaft.

Use a pair of pliers to tighten the hex nut onto the threaded shaft.

Double check the potentiometer is in the correct orientation.

Install Potentiometer Linkage

Insert the linkage to the shaft of the potentiometer.

Firmly press the linkage into the shaft.

If the fitting is too tight, use a filing tool to loosen up the tolerance.

Linkage Range of Motion

Rotate the linkage to test the range of motion.

The linkage should freely rotate and only stop when reaching the base of the holder.

Remove linkage from the shaft and adjust the placement if needed.

Install QT Py RP2040

The QTPy RP2040 is snap fitted into the QT Py Holder.

Install QT Py to Holder

Insert the QT Py into the holder at and angle with the back side of the PCB fitting under the clips.

Slightly flex the holder to fit the front side of the PCB under the clips.

Assembly Check Point

Take a moment to inspect the QT Py RP2040 and the potentiometer are properly installed.

Install Rubber Feet

Stick the 4x rubber bumper feet into the corners of the bottom plate.

Install Potentiometer Holder

Insert 2x M3 x 10mm screws through the holes in the bottom plate.

Place the potentiometer holder onto the bottom plate so the screws are fitted through the mounting holes.

Secure Potentiometer Holder

Use 2x M3 hex nuts to secure the potentiometer holder to the bottom plate.

Install QT Py RP2040 Holder

Insert 2x M3x10mm through the holes in the bottom plate.

Place the QT Py holder onto the bottom plate so the screws are fitted through the mounting tabs.

Secure QT Py Holder

Use 2x M3 hex nuts to secure the QT Py holder to the bottom plate.

Assembled Bottom Plate

Take a moment to tighten the screws for the QT Py and potentiometer holders.

Install Arm to Top Pedal

The arm is secured to the top pedal using M3 screws and hex nuts.

Insert 2x M3 x 10mm flat head screws to the top pedal.

Place the arm under the top pedal so the screws are fitted through the mounting holes.

Secure Arm to Top Pedal

Use 2x M3 nylon insert lock nuts to secure the arm to the top pedal.

Use plier to grab hold of the lock nuts to reliably fasten parts together.

Hardware for Hinge Plate

Use the following hardware to secure the hinge to the base frame.

  • 4x M3 x 10mm screws
  • 4x M3 hex nuts

Hinge Placement

Take a moment to orient the hinge with the base frame.

Use arrow to indicate the correct orientation of the hinge with the base frame.

Install Washer Sleeves

Insert the washer sleeves to the holes on the left and right sides of the top pedal.

With the washer sleeves installed, insert the middle washer to the sleeves.

Installed Middle Washer

Double check the middle washers are installed to the left and right sides.

Install Hinge to Top Pedal

Insert the hinge base plate onto the top pedal.

Adjust the washer sleeves and middle washers to allow the hinge base plate.

Install Hinge Screws

Insert 2x M3 x 16mm long screws to the left and right sides of the top pedal.

Install Washer Caps to Hinge

Insert the remaining washer caps to the long M3 screws.

Secure Lock Nuts to Hinge

Insert and fasten M3 nylon insert lock nuts to the M3 screws.

Use pliers to grab onto the lock nuts to reliably secure parts.

Assembled Top Pedal

Take a moment to check all of the hardware has been tightened.

Test Hinge Plate

Rotate the hinge plate and test the fitting. The rotational movement should ideally have a snug fit.

Install Hinge Plate Screws

Rotate the hinge plate and check the orientation is correct.

Insert 4x M3x10mm screws through the mounting holes.

Install Hinge to Base 

Orient the top pedal with the base framing.

Begin to install the hinge to the base frame by fitting the screws through the mounting holes.


Secure hinge plate to base frame

Use M3 hex nuts to secure the hinge plate to the base frame.

Test Pedal Hinge

Move the top pedal to test the hinge. The motion should behave like a see-saw.

Install Linkage to Arm

Begin to install the bottom plate to the base frame and orient the parts together.

Place the linkage from the potentiometer over the peg in the arm of the top pedal.

Install Bottom Plate

With the linkage and arm installed, begin to line up the mounting tabs from the bottom plate with the mounting holes on the base frame.

Secure Bottom Plate

Use M3x10mm and hex nuts to secure the bottom plate to the base frame.

Secure Bottom Plate (continued)

Install and fasten remaining hardware to the mounting tabs on the back side of the foot pedal.

Final Build

Test the motion of the foot pedal by moving the top pedal up and down.

Congratulations on your build!

Plug the pedal to your computer with a known good USB Power & Data Cable.

USB MIDI Audio MIDI Setup (Mac OS)

The QT Py RP2040 will be listed in the MIDI Studio window of the Audio MIDI Setup application for Mac OS.

The USB MIDI device is automatically configured and ready to go with DAW such as Garage band, Logic Pro, and Ableton.

MIDI CC Mapping & Assigning

Each music software has its own interface for mapping MIDI controllers. This topic goes outside the scope of this guide. We recommend referencing your music software documentation or search for video tutorials.

This guide was first published on Apr 06, 2022. It was last updated on Jul 13, 2024.