This guide will show how to take a freshly installed Raspberry Pi and have it operate as an OS-discoverable MIDI I/O device. It will also provide some examples of using various Python libraries to get MIDI data into and out of the programming environment.

UPDATE – Sep 11, 2021.: This guide has been updated to fix some issues with the latest Raspberry Pi OS version.

What we need


Use the nylon screws and standoffs to assembly the Raspberry Pi together with the MIDI Board, as shown on the image below:

First time setup

We tested all the examples in this document on a Pi 4B using Rasperry Pi OS, version May 2020). The first time, it is necessary to use a screen and keyboard to set the Pi up. Thereafter, use your method of choice to access the Pi’s OS. All steps are mandatory unless otherwise stated. 



Perform the update and upgrade as described here:

Network Configuration (Optional)

If you are SSH’ing from another machine into the Pi, it is worthwhile giving the Pi a fixed IP address:

It is also a good idea to add the network security settings to the Pi so that it will automatically connect to the network:

Set the Pi Up as a USB OTG Gadget

Open a terminal on the Pi and follow this procedure:

    • Set the USB driver to dwc2
      echo "dtoverlay=dwc2" | sudo tee -a /boot/config.txt
    • Enable the dwc2 driver
      echo "dwc2" | sudo tee -a /etc/modules
    • Enable the libcomposite driver
      echo "libcomposite" | sudo tee -a /etc/modules
    • Enable the MIDI gadget
      echo "g_midi" | sudo tee -a /etc/modules

Create the configuration script:

    • Create the file
      sudo touch /usr/bin/midi_over_usb
    • Make it executable
      sudo chmod +x /usr/bin/midi_over_usb
    • Edit it with Nano 
      sudo nano /usr/bin/midi_over_usb

Paste the following into the file, making edits to the product and manufacturer strings as required.

cd /sys/kernel/config/usb_gadget/ 
mkdir -p midi_over_usb 
cd midi_over_usb 
echo 0x1d6b > idVendor # Linux Foundation 
echo 0x0104 > idProduct # Multifunction Composite Gadget 
echo 0x0100 > bcdDevice # v1.0.0 
echo 0x0200 > bcdUSB # USB2 
mkdir -p strings/0x409 
echo "fedcba9876543210" > strings/0x409/serialnumber 
echo "OSA Electronics" > strings/0x409/manufacturer 
echo "MIDI USB Device" > strings/0x409/product 
ls /sys/class/udc > UDC

Exit Nano and save the file (Ctrl+X, Y, return).

Add a call to the script to rc.local, so that it executes on every startup.

sudo nano /etc/rc.local

Add the following line before “exit0”


Exit Nano and save the file and reboot the Pi.

sudo reboot

List the available MIDI ports.

amidi -l

If the MIDI is configured correctly, the last command should output something similar to:

Dir Device Name IO hw:0,0 f_midi
IO hw:0,0 f_midi


Install Python Libraries

This section will explain how to install our preferred libraries for Python 2.x. If you are using Python 3.x, install mido and rtmidi using the pip3 installl command. You can also install the packages for both Python versions. We have not experienced any conflicts.


Mido is an easy-to-use library for handling MIDI data. It relies on the rt-midi backend, the asound library, and Jack. Input the following commands in sequence:

pip install mido
sudo apt-get install libasound2-dev
sudo apt-get install libjack0
sudo apt-get install libjack-dev
pip install python-rtmidi

Do a quick Python command line check:

import mido

The output should show one ‘Midi Through’ port and one additional port. If this is the case, all is well.

*Note: in Mido, the port name is the entire string enclosed in single quotes, but it is possible to truncate the name to the string before the colon. On this machine, the string is:

'f_midi:f_midi 16:0'.

For example, these two commands are equivalent:

port = mido.open_output('f_midi:f_midi 16:0')
port = mido.open_output('f_midi')


We use the pigpio library to interface with the GPIO pins. We have found this library to be more stable and flexible than the standard method of interfacing with the Pi’s hardware (RPi.GPIO). If you want to use another library, edit the code accordingly.

To install the pigpio library, follow the instructions here:

Prior running all of the examples below, you should start the pigpio service if not done:

sudo pigpiod


Python Examples

The examples also use the numpy library’s interp function as an easy way to map between two ranges. We used Reaper to send and receive data. The Pi is configured as a Hardware MIDI output in Reaper’s preferences menu.

Control GPIO with Note Data (

This example shows how to:

  • Listen for 3 specific note-on and note-off events using a simple condition
  • Catch the exceptions that arise when non-note data is sent to the Pi (e.g. transport data from a sequencer)
  • Map the note velocity to the PWM of the output pin

Import the relevant libraries, create the pi object from the pigpio library, and open the output port:

import mido
import pigpio
from numpy import interp
pi1 = pigpio.pi()
port = mido.open_input('f_midi') # open USB port

The try/catch block is to catch the errors that arise from other types of MIDI data being sent (e.g. transport
controls etc.).

while True:
try: #This filters out all non-note data
for msg in port.iter_pending(): # if there is a message pending
if(msg.type == 'note_on'): # if it is Note On message
out = interp(msg.velocity, [0,127],[0,255]) #
scale velocity from 0-127 to 0-255
#filter the data by note number
if(msg.note == 53):
pi1.set_PWM_dutycycle(2, out)
elif(msg.note == 55):
pi1.set_PWM_dutycycle(3, out)
elif(msg.note == 57):
pi1.set_PWM_dutycycle(4, out)
else: # if the message is not Note On (e.g. Note Off)
if(msg.note == 53):
pi1.set_PWM_dutycycle(2, 0)
elif(msg.note == 55):
pi1.set_PWM_dutycycle(3, 0)
elif(msg.note == 57):
pi1.set_PWM_dutycycle(4, 0)
except AttributeError as error:
print("Error excepted")


Control GPIO with Mod and Pitch Wheels (

This example shows how to:

  • Listen for Pitch and Mod Data and filter them by type
  • Map the data to the PWM of the output pin

This example is similar to the above, with these message types:

  • The Pitch wheel is type pitchwheel with a value of msg.pitch
  • The Mod Wheel is a Continuous Controller type control_change with a control parameter of msg.control = 1 (the CC number) and a value of msg.value
import mido
import pigpio
from numpy import interp
pi1 = pigpio.pi()
port = mido.open_input('f_midi') # open USB port
while True:
try: #This filters out all non-note data
for msg in port.iter_pending(): #if there is a message pending
if msg.type == 'pitchwheel': #of type pitchwheel
print("PITCH: ", msg.pitch)
out = interp(msg.pitch, [-8192,8192],[0,255])
pi1.set_PWM_dutycycle(2, out)
pi1.set_PWM_dutycycle(3, out)
pi1.set_PWM_dutycycle(4, out)
if msg.type == 'control_change' and msg.control == 1:
print("MOD: ", msg.value)
except AttributeError as error:
print("Error excepted")


Output MIDI Data from a GPIO Event (

This example shows how to:

  • Use an interrupt to detect a button press
  • Send MIDI data from the Pi to another device

Open the output port, create two messages and setup the GPIO pin as an input. This example assumes there is a button tied to pin 21, so that pin goes HIGH when the button is pressed:

import mido
import pigpio
pi1 = pigpio.pi()
outport = mido.open_output('f_midi') # open USB port
onmess = mido.Message('note_on', note = 34, velocity = 127)
offmess = mido.Message('note_off', note = 34, velocity = 127)
buttPin = 21
pi1.set_mode(buttPin, pigpio.INPUT)

The following are the callback functions called when the button is pressed or released. The output ports send() function simply sends the message out of the port:

def buttonDown(gpio, level, tick):
def buttonUp(gpio, level, tick):

The callback listeners run in the background and do not need any more attention:

cb = pi1.callback(buttPin,pigpio.RISING_EDGE, buttonDown)
cb2 = pi1.callback(buttPin,pigpio.FALLING_EDGE, buttonUp)
#Just loop and do nothing
while True:


Playback a MIDI File

This example shows how to:

  • Load a MIDI file in the programming environment
  • Playback the file

This examples assumes you have a MIDI file called midi_file.mid in the same directory as your python script:

import mido
from mido import MidiFile
from mido import MetaMessage
port = mido.open_output('f_midi')
mid = MidiFile('midi_file.mid')
while True:
for msg in MidiFile('midi_file.mid').play():


15 thoughts on “Setting-up Raspberry Pi for MIDI

    • Oriol Sanchez says:

      Hello Mike,

      Just changed the way the examples work to make it easier for everybody, can you please a look now?


      • Mike says:

        Hi Oriol

        thx. I got it working in the meantime as well. I am using ttymidi so I can use mido to support midi over serial as well as midi over USB.


        • Oriol Sanchez says:

          Hello Mike,

          That’s great! If you can send/share your examples, I will post them here on the guide to help other users!


  1. Joseph Olano says:

    Hi Oriol Sanchez,
    I trust you are well and keeping safe, I am having the same problem as Julian Gough. Bought this from Pi Hut. Updated the RPI as per guide. Tx LED light constantly lit.

    The MIDI data from the USB MIDI pad controller is read by the RPI OK I want to pass it to the MIDI out it of this board but it does not work. Can I contact you too please?

    • Oriol Sanchez says:

      Hello Joseph,

      There is a problem with the Pi GPIO library, that is not working properly with the newer kernels. We are trying to fin another way to make this work again or make some other simple example to at least verify the board is working and do some small comunication tests. So sorry to all of you are experiencing this issue!

      Keep all of you updated and I will update the guide soon.


      • Joseph says:

        Thanks Oriol,
        I thought I was going crazy by retrying the guide numerous times.
        Can OSA publish a guide at least that uses the last working version/kernels ( I am a noob I don’t even know if this is even possible ). My rig will be a dedicated/standalone solution to translate MIDI signals so I don’t mind using the old kernels if that’s possible, just also not to this purchase to waste. Thanks for your help on this and please keep us updated.

        Kind Regards, Joseph

        • Oriol Sanchez says:

          Hello Joseph,

          We are working on this as fast as we can! Maybe some working image with old kernel will work for you? That could be a solution to some other people too.


  2. Marko says:

    I also get nothing on Rx and Tx LEDs. However, this guide installs midi_over_usb, but your MIDI extension board does not connect to USB, but on GPIO header, where it uses UART pins. How is it supposed to work?

    • Oriol Sanchez says:


      As I recently mentioned, there is a problem with the Pi GPIO library, that is not working properly with the newer kernels. We are trying to fin another way to make this work again or make some other simple example to at least verify the board is working and do some small comunication tests. So sorry to all of you are experiencing this issue!


  3. David says:

    Hello, I am having what seems to be a similar issue to Julian. However in my case there is no activity whatsoever on the Tx and Rx LEDs. I am simply trying to play a midi file and nothing appears to be happening.

  4. MJB says:

    I figured out that the problem was with >>> which is the >>> that apperas automatically. Now I’m getting this error:
    <bound method Backend.get_output_names of >
    any solutions?

    • MJB says:

      This form is messing and truncating what I type! The first lot of > above should be &gt
      This is the error I’m getting now:
      <bound method Backend.get_output_names of >

  5. Julian Gough says:

    I have followed very carefully these instructions, then went over them again, then again. My python code is not giving any errors when run, but the simplest of code to send MIDI data does not generate any data on the MIDI out. The Tx LED on the board is constantly lit. I tried sending data into the MIDI in socket but the Rx LED doesn’t light or flicker. Though it is hard to see with the Tx LED being so bright.

Leave a Reply

Your email address will not be published.