Maker.io main logo

MIDI for Makers

2024-02-14 | By Adafruit Industries

License: See Original Project Displays Programmers

Courtesy of Adafruit

Guide by Liz Clark

Overview

Whether you're a classically trained pianist or a hobbyist who thinks ‎synths sound really cool, the allure of DIY MIDI projects are incredibly ‎tempting. It can be hard to know how to get started though. What ‎board should you use? How does the code work? What even is MIDI?‎

This guide will tell you everything you need to know to get started ‎with your own MIDI projects so that you can focus on building rather ‎than searching everywhere on the internet.‎

midi_1

What is MIDI?‎

Music Instrument Digital Interface (MIDI) is a 7-bit communication ‎protocol that dates back to the 1980's. It may sound antiquated, but ‎it is the standard digital communication method for all things music. ‎If you've ever had a piano keyboard control a piece of software or ‎digital audio workstation (DAW) on a computer, then you were using ‎MIDI.‎

MIDI is dependable, predictable, reliable and at this point is usually ‎plug and play without too much fuss. This also means that it's ‎approachable for folks to build their own MIDI devices.‎

Why build a MIDI controller?‎

There are a lot of aftermarket MIDI devices available, so why would ‎you want to build your own? When you build your own MIDI ‎controller, you can design it specifically for what you need. If you ‎aren't quite sure what you want to do with MIDI, you can experiment ‎and test things before committing to a full build. Additionally, you ‎can share your MIDI project's details with the community for other ‎musicians to benefit from.‎

Parts

How MIDI is Transmitted

transmit_2

MIDI In vs. MIDI Out

The MIDI communication protocol has two main options: MIDI in and ‎MIDI out. MIDI in refers to receiving MIDI messages as an input. ‎MIDI out refers to sending MIDI messages as an output.‎

Each type requires a different circuit to send or receive the messages ‎properly. MIDI controllers will have the ports labeled as such. For ‎homemade devices, you will define your device as either being an ‎input or an output.‎

MIDI Channels

MIDI messages are sent via channels. There are 16 available channels, ‎numbered 1-16. The channels allow for devices to know what and ‎who to listen to, similar to setting up a remote to control a specific ‎television.‎

For example, a MIDI controller could send a C3 note and CC ‎messages via channel 1 to a synthesizer setup to receive messages ‎on channel 1. At the same time, that MIDI controller could send the ‎same or different messages on channel 8 to a drum machine setup ‎to receive messages on channel 8.‎

In code, these channels are numbered 0 to 15 because you are ‎counting with zero-based numbering. For example, real-world ‎channel 1 is coded as channel 0, real-world channel 2 is coded as ‎channel 1, etc.‎

In CircuitPython, your MIDI device's channel that it is listening to or ‎sending messages on is defined at the beginning of your code. It ‎looks like this:‎

midi = adafruit_midi.MIDI(

   ‎ midi_in=usb_midi.ports[0], in_channel=0,

midi_out=usb_midi.ports[1], out_channel=0)‎

where in_channel and out_channel are holding the channel values.‎

In Arduino, it's a little different. In the setup(), the begin() function is ‎called with MIDI_CHANNEL_OMNI. MIDI_CHANNEL_OMNI means that the code is able ‎to listen for any of the 16 MIDI channels. it looks like this:‎

MIDI_CREATE_DEFAULT_INSTANCE();‎

void setup()

{ ‎

    MIDI.begin(MIDI_CHANNEL_OMNI);

‎}‎

More discrete MIDI channel definitions can be defined when sending ‎individual MIDI messages. Examples for this syntax are available on ‎the MIDI Messages page of this guide.‎

Connections

connections_3

DIN-5‎

The original MIDI connector is a MIDI jack, or DIN-5 connector. These ‎are chunky, hearty connectors. Though they are becoming less ‎common on newer devices, they are reliable and provide a ‎straightforward connection to transmit MIDI data.‎

din_4

TRS-A

A newer MIDI connection standard that has been implemented is ‎TRS-A, which utilizes a TRS audio jack to transmit MIDI data over ‎UART. When this first began appearing, the connection was not ‎standardized so older devices may require specialized cables for full ‎compatibility. ‎

The standardized pinout for TRS-A is as follows:‎

  • MIDI Sink to TRS Tip
  • MIDI Source to TRS Ring
  • MIDI Shield to TRS Sleeve

This new standard allows for the reliability of a DIN-5 connector, but ‎in a smaller and more common form factor.‎

trs_5

Beware of TRS-B! TRS-B is a different TRS wiring that was used ‎before TRS-A was standardized and can be found on various MIDI ‎controllers.

For more information on TRS MIDI connections, check out this ‎resource from audionerd

MIDI over UART

When you see a connection with a DIN-5 or TRS-A connector, the ‎device is using MIDI over UART. MIDI over UART transmits MIDI ‎messages over the TX and RX serial connections at a baud rate of ‎‎31250.‎

You can use MIDI over UART to create MIDI devices using older ‎boards with microcontrollers that do not have support for direct USB ‎communication, such as the Atmega328. Additionally, you can have ‎boards communicate with each other over MIDI directly using UART.‎

USB MIDI

USB MIDI has become more common for MIDI controllers. These ‎devices have a configuration descriptor allowing them to be plug ‎and play. These do not have direct compatibility with MIDI over UART ‎devices, and, as a result, would require a converter to communicate ‎directly. ‎

For MIDI over USB devices, you can use them with your computer to ‎interface with music tech software. You can also use them with a ‎USB MIDI Host, which allows for USB devices to interface with each ‎other. There are aftermarket USB MIDI hosts available, as well as DIY ‎options (you can even use a Raspberry Pi).‎

Many current microcontrollers have the ability to be used for DIY ‎USB MIDI projects, including boards that are based on the ATSAMD21 ‎‎(M0 Express), ATSAMD51 (M4 Express) and RP2040. All of these ‎boards have support for both CircuitPython and Arduino.‎

BLE MIDI

One of the more unique ways to transmit MIDI is over Bluetooth with ‎BLE MIDI. BLE MIDI allows for wireless MIDI communication over ‎Bluetooth. It can be the perfect choice for some projects that benefit ‎from not being tethered by cables.‎

If you want to build a BLE MIDI project, you'll want to use an ‎nRF52840 Express-based board, which has support for BLE.‎

MIDI Messages

messages_6

You've read how MIDI is transmitted, but what exactly are MIDI ‎messages? This section will go through the most commonly used ‎messages and show how to send them with either CircuitPython or ‎Arduino.‎

For more information on the Arduino MIDI library, check out this ‎documentation on GitHub.IO

For more information on the CircuitPython MIDI library, check out ‎this documentation on readthedocs

NoteOn

This message begins playing a note. A MIDI NoteOn message sends the ‎MIDI note number and the velocity of the note.‎

MIDI note numbers range from 0 to 127, where 21 is A0. Inspired ‎Acoustics has a great table here showing the MIDI note numbers ‎with their corresponding English note name, piano key number and ‎pitch frequency.‎

CircuitPython

Download File

Copy Code
midi.send(NoteOn(note, velocity))

Arduino

Download File

Copy Code
MIDI.sendNoteOn(note, velocity, midi channel);

NoteOff

This stops a note from playing. If a NoteOff message is not sent after ‎a NoteOn message, the note will continue playing forever. A ‎MIDI NoteOff message sends the note number and the velocity of the ‎note.‎

CircuitPython

Download File

Copy Code
midi.send(NoteOff(note, velocity))

Arduino

Download File

Copy Code
MIDI.sendNoteOff(note, velocity, midi channel);

ControlChange Messages

ControlChange (CC) messages are utility messages that can have a range ‎of functionality. CC messages are usually what is sent when you turn ‎a knob or move a slider on a MIDI controller. Some examples of ‎common messages are modulation and sustain. Each CC message ‎has an assigned number. The MIDI Association has a resource listing ‎CC messages here.‎

A MIDI CC message contains the CC number and the value that you ‎want to send to that control change. The value range will differ ‎depending on the CC message, but usually have a range of 0 - 127.‎

CircuitPython

Download File

Copy Code
midi.send(ControlChange(CC, value))

Arduino

Download File

Copy Code
MIDI.sendControlChange(CC, value, midi channel);

PitchBend

PitchBend is a special MIDI message, with a range of 0 to 16383. Since ‎pitch can be bent up or down, the midpoint (no pitch bend) is 8192.‎

CircuitPython

Download File

Copy Code
x = PitchBend(value)
midi.send(x)

Arduino

Download File

Copy Code
MIDI.sendPitchBend(value, midi channel);

Reading MIDI In

You can receive MIDI data and have your microcontroller read the ‎incoming MIDI message.‎

CircuitPython

Download File

Copy Code
midi.receive()

Arduino

Download File

Copy Code
MIDI.read();

Let's Build!‎

build_7

In the next section, you'll be able to look at six example projects that ‎apply the MIDI knowledge in this guide. You can reference these ‎projects for your own creations.‎

The first three projects are examples for sending MIDI out:‎

  • Basic MIDI Keyboard
    • Send MIDI notes with button presses
  • MIDI CC Control with Pots
    • Affect control change messages with potentiometers
  • BLE MIDI Sequencer
    • Send arpeggios to a synth over BLE MIDI. Wireless ‎melodies!‎

The last three projects are examples for receiving MIDI in:‎

  • Receive and Display MIDI Messages
    • Using an LCD screen, view the MIDI messages being sent ‎in real-time from a MIDI controller
  • Receive MIDI Over UART and Send Over USB
    • Convert a UART MIDI device to a USB MIDI device
  • Control Motors with MIDI
    • You can control a variety of peripherals with MIDI ‎messages. Motors are common since you can create MIDI-‎controlled robotic instruments

Prerequisite Guides

If this is your first-time using electronics or CircuitPython, you will ‎want to browse these guides first to have a better background when ‎trying out the MIDI example projects.‎

Welcome to CircuitPython! Learn Guide

CircuitPython Essentials Learn Guide

Which CircuitPython Board is Right for You? Learn Guide

Basic MIDI Keyboard

basic_8

Being able to send note messages is one of the most popular MIDI ‎projects. In this example, you'll see how to use button inputs to ‎send NoteOn and NoteOff messages to a synth or DAW.‎

Below, a simple circuit is built using a Feather RP2040 ‎microcontroller board wired to four buttons. The components and ‎wires are seated in a breadboard which facilitates connections via ‎wires.‎

The Feather will need to have header pins soldered on. The ‎Breadboard can help hold them while you do this.‎

Circuit Diagram

diagram_9

  • Button 1
    • Pin 1 to Feather pin D5‎
    • Pin 4 to Feather GND
  • Button 2
    • Pin 1 to Feather pin D6‎
    • Pin 4 to Feather GND
  • Button 3
    • Pin 1 to Feather pin D9
    • Pin 4 to Feather GND
  • Button 4
    • Pin 1 to Feather pin D10‎
    • Pin 4 to Feather GND

Setup the Feather RP2040‎

For this example, you'll be using the Feather RP2040. To make sure ‎it is setup properly with CircuitPython, please follow the steps in the ‎guide below.‎

Feather RP2040 CircuitPython Setup

CircuitPython Code

Once you've finished setting up your Feather 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.‎

Download Project Bundle

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

import board
import digitalio
import usb_midi
import adafruit_midi
from adafruit_midi.note_on import NoteOn
from adafruit_midi.note_off import NoteOff

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

# midi note numbers
midi_notes = [60, 64, 67, 72]

# digital pins for the buttons
key_pins = [board.D5, board.D6, board.D9, board.D10]

# array for buttons
keys = []

# setup buttons as inputs
for key in key_pins:
key_pin = digitalio.DigitalInOut(key)
key_pin.direction = digitalio.Direction.INPUT
key_pin.pull = digitalio.Pull.UP
keys.append(key_pin)

# states for buttons
key0_pressed = False
key1_pressed = False
key2_pressed = False
key3_pressed = False

# array for button states
key_states = [key0_pressed, key1_pressed, key2_pressed, key3_pressed]

while True:

# iterate through 4 buttons
for i in range(4):
inputs = keys[i]
# if button is pressed...
if not inputs.value and key_states[i] is False:
# update button state
key_states[i] = True
# send NoteOn for corresponding MIDI note
midi.send(NoteOn(midi_notes[i], 120))

# if the button is released...
if inputs.value and key_states[i] is True:
# send NoteOff for corresponding MIDI note
midi.send(NoteOff(midi_notes[i], 120))
key_states[i] = False

‎View on GitHub

Upload the Code and Libraries to the Feather ‎RP2040‎

After downloading the Project Bundle, plug your Feather 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 Feather RP2040's CIRCUITPY drive. ‎

  • lib folder
  • code.py

Your Feather RP2040 CIRCUITPY drive should look like this after ‎copying the lib folder and the code.py file.‎

circuitpy_10

How the CircuitPython Code Works

The MIDI note numbers assigned to each button are setup in ‎the midi_notes array. When a key press is detected, a NoteOn message ‎with the corresponding MIDI note number is sent. When a key is ‎released, then a NoteOff message is sent.‎

If you wanted to change the notes or buttons, you would edit ‎the midi_notes and key_pins array.‎

Usage

You can connect your Feather RP2040 via USB to either your ‎computer or a USB MIDI host. Then, press the buttons to play the ‎notes of a C major triad. ‎

A DIY MIDI keyboard is a very popular project. You can add more ‎buttons, change the notes, or create a fun enclosure to customize ‎this project further.‎

 

MIDI CC Control with Pots

controls_10a

Potentiometers (often times shortened to the word pots, which are ‎resistors varied by turning a knob) are a common peripheral on MIDI ‎controllers. They also provide a greater sense of control than clicking ‎and dragging when using a mouse with a DAW. In this example, ‎you'll see how to update control change (CC) messages with pots.‎

Circuit Diagram

board_10b

  • Pot 1‎
    • Pin 1 to QT Py RP2040 GND
    • Pin 2 to QT Py RP2040 A0‎
    • Pin 3 to QT Py RP2040 3V
  • Pot 2
    • Pin 1 to QT Py RP2040 GND
    • Pin 2 to QT Py RP2040 A1
    • Pin 3 to QT Py RP2040 3V‎
  • Pot 3
    • Pin 1 to QT Py RP2040 GND
    • Pin 2 to QT Py RP2040 A2
    • Pin 3 to QT Py RP2040 3V‎
  • Pot 4
    • Pin 1 to QT Py RP2040 GND
    • Pin 2 to QT Py RP2040 A3
    • Pin 3 to QT Py RP2040 3V‎

Setup the QT Py RP2040‎

For this example, you'll be using the QT Py RP2040. To make sure it ‎is setup properly with CircuitPython, please follow the steps in the ‎guide below.‎

QT Py RP2040 CircuitPython Setup

CircuitPython Code

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.‎

Download Project Bundle

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

import board
import usb_midi
import adafruit_midi
import simpleio
from analogio import AnalogIn
from adafruit_midi.control_change import ControlChange
from adafruit_midi.pitch_bend import PitchBend

# 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)
pitchDown_pot = AnalogIn(board.A1)
pitchUp_pot = AnalogIn(board.A2)
sus_pot = AnalogIn(board.A3)

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

# variables for last read value
# defaults to 0
# no pitchbend is 8192
mod_val2 = 0
pitchDown_val2 = 8192
pitchUp_val2 = 8192
sus_val2 = 0

while True:

# map range of analog input to midi values
# pitchbend range is 0 to 16383 with 8192 centered or no pitchbend
mod_val1 = round(simpleio.map_range(val(mod_pot), 0, 65535, 0, 127))
pitchDown_val1 = round(simpleio.map_range(val(pitchDown_pot), 0, 65535, 0, 8192))
pitchUp_val1 = round(simpleio.map_range(val(pitchUp_pot), 0, 65535, 8192, 16383))
sus_val1 = round(simpleio.map_range(val(sus_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
midi.send(modWheel)

# pitchbend down value is updated...
if abs(pitchDown_val1 - pitchDown_val2) > 75:
# update pitchDown_val2
pitchDown_val2 = pitchDown_val1
# create PitchBend message
pitchDown = PitchBend(int(pitchDown_val2))
# send PitchBend message
midi.send(pitchDown)

# pitchbend up value is updated...
if abs(pitchUp_val1 - pitchUp_val2) > 75:
# updated pitchUp_val2
pitchUp_val2 = pitchUp_val1
# create PitchBend message
pitchUp = PitchBend(int(pitchUp_val2))
# send PitchBend message
midi.send(pitchUp)

# sustain value is updated...
if abs(sus_val1 - sus_val2) > 2:
# update sus_val2
sus_val2 = sus_val1
# create integer
sustain = int(sus_val2)
# create CC message
sustainPedal = ControlChange(64, sustain)
# send CC message
midi.send(sustainPedal)

View on GitHub

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
  • code.py

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

circuitpy_13

How the CircuitPython Code Works

The map_range() function is used to map the analog values from the ‎potentiometers to MIDI values. The _val1 and _val2 variables are ‎compared to each other for each pot to see if the pot has been ‎turned. If it has, then an updated MIDI value is sent. ‎

Usage

You can connect your QT Py RP2040 via USB to either your ‎computer or a USB MIDI host. Then, turn the knobs to affect ‎modulation, sustain and pitch bend. This project works best with a ‎software or hardware synth since the effects generally affect musical ‎input happening in real time.‎

Going further, you could combine this project with the components ‎and code from the MIDI keyboard example to create a larger MIDI ‎interface

 

BLE MIDI Sequencer

mdi_15

BLE MIDI lets you send MIDI data wirelessly over Bluetooth. In this ‎example, you'll use a CLUE to send arpeggios to a DAW.‎

Setup the CLUE

This example uses an Adafruit CLUE microcontroller board. To make ‎sure it is setup properly with CircuitPython, please follow the steps in ‎the guide below.‎

CLUE CircuitPython Setup

CircuitPython Code

Once you've finished setting up your CLUE 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.‎‎

Download Project Bundle

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

import time
import adafruit_ble
import touchio
import board
import adafruit_midi
import adafruit_ble_midi
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_midi.note_on import NoteOn
from adafruit_midi.note_off import NoteOff

# CLUE cap touch setup
c_touch = touchio.TouchIn(board.D0)
f_touch = touchio.TouchIn(board.D1)
g_touch = touchio.TouchIn(board.D2)

# array of touch pads
pads = [c_touch, f_touch, g_touch]

# BLE MIDI setup
midi_service = adafruit_ble_midi.MIDIService()
advertisement = ProvideServicesAdvertisement(midi_service)

ble = adafruit_ble.BLERadio()
if ble.connected:
for c in ble.connections:
c.disconnect()

# midi setup
midi = adafruit_midi.MIDI(midi_out=midi_service, out_channel=0)

print("advertising")
ble.start_advertising(advertisement)

# MIDI note numbers for C, F and G major triads
c_triad = (60, 64, 67)
f_triad = (65, 69, 72)
g_triad = (67, 71, 74)

# array of triads
triads = [c_triad, f_triad, g_triad]

# touch debounce states
c_pressed = False
f_pressed = False
g_pressed = False

# array of debounce states
triad_states = [c_pressed, f_pressed, g_pressed]

# beginning triad
active_triad = c_triad
# variable for triad index
z = 0

while True:
# BLE connection
print("Waiting for connection")
while not ble.connected:
pass
print("Connected")
time.sleep(1.0)

# while BLE is connected...
while ble.connected:
# iterate through the touch inputs
for i in range(3):
inputs = pads[i]
# if a touch input is detected...
if inputs.value and triad_states[i] is False:
# debounce state activated
triad_states[i] = True
# update triad
active_triad = triads[i]
print(active_triad)
# after touch input...
if not inputs.value and triad_states[i] is True:
# reset debounce state
triad_states[i] = False
# send triad arpeggios out with half second delay
midi.send(NoteOn(active_triad[z]))
time.sleep(0.5)
midi.send(NoteOff(active_triad[z]))
time.sleep(0.5)
# increase index by 1
z += 1
# reset index at end of triad
if z > 2:
z = 0

# BLE connection
print("Disconnected")
print()
ble.start_advertising(advertisement)

View on GitHub

Upload the Code and Libraries to the CLUE

After downloading the Project Bundle, plug your CLUE 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 CLUE's CIRCUITPY drive.‎

  • lib folder
  • code.py

Your CLUE CIRCUITPY drive should look like this after copying ‎the lib folder and the code.py file.‎

mpy_17

How the CircuitPython Code Works

There are three sequences of MIDI note numbers ‎setup: c_triad, f_triad, and g_triad. These correspond with the CLUE's ‎three capacitive touch inputs. ‎

In the loop, NoteOn and NoteOff messages are sent every 0.5 seconds to ‎play through the triad. If a touch input is detected, then the triad ‎updates.‎

The code is using BLE MIDI to utilize the CLUE's nRF52840. The loop ‎is dependent on a BLE connection being active, otherwise the CLUE ‎will wait for a connection.‎

Usage

You can connect your CLUE via BLE to your computer or mobile ‎device. After a BLE connection is established, open your preferred ‎music software to send the arpeggios to a software synth. Tap the ‎three capacitive touch inputs to change the chord.‎

Going further, you could change the notes being sent and also play ‎with the timing for sending the notes. You could use an additional ‎input to code up a tap tempo to set the beats per minute (BPM) for ‎sending notes.‎

 

Receive and Display MIDI Messages

noteoff_18

Reading incoming MIDI messages can come in handy for ‎troubleshooting, hacking or general curiosity. In this example, you'll ‎see how to receive MIDI in messages and display them on an LCD ‎screen.‎

Circuit Diagram

You can easily connect the LCD to the QT Py RP2040 with a STEMMA ‎QT cable.‎

  • LCD 3V to QT Py RP2040 3V
  • LCD GND to QT Py RP2040 GND
  • LCD SDA to QT Py RP2040 SDA
  • LCD SCL to QT Py RP2040 SCL

diagram_19

Setup the QT Py RP2040‎

This example uses an QT Py RP2040. To make sure it is setup ‎properly with CircuitPython, please follow the steps in the guide ‎below.‎

QT Py RP2040 CircuitPython Setup

CircuitPython Code

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.‎

Download Project Bundle

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

import board
import busio
import usb_midi
import adafruit_midi
import displayio
import terminalio
from adafruit_display_text import label
import adafruit_displayio_ssd1306
from adafruit_midi.control_change import ControlChange
from adafruit_midi.note_off import NoteOff
from adafruit_midi.note_on import NoteOn
from adafruit_midi.pitch_bend import PitchBend

displayio.release_displays()

oled_reset = board.D1

# I2C setup for display

# STEMMA I2C setup pre-CP 7.2
i2c = busio.I2C(board.SCL1, board.SDA1)

# STEMMA I2C setup for CP 7.2+
# i2c = board.STEMMA_I2C()

display_bus = displayio.I2CDisplay(i2c, device_address=0x3D, reset=oled_reset)

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

msg = midi.receive()

# display width and height setup
WIDTH = 128
HEIGHT = 64
BORDER = 5

# display setup
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=WIDTH, height=HEIGHT)

splash = displayio.Group()
display.root_group = splash

# text area setup
text = "MIDI Messages"
text_area = label.Label(
terminalio.FONT, text=text, color=0xFFFFFF, x=30, y=HEIGHT // 2+1)
splash.append(text_area)

while True:
# receive midi messages
msg = midi.receive()

if msg is not None:
# if a NoteOn message...
if isinstance(msg, NoteOn):
string_msg = 'NoteOn'
# get note number
string_val = str(msg.note)
# if a NoteOff message...
if isinstance(msg, NoteOff):
string_msg = 'NoteOff'
# get note number
string_val = str(msg.note)
# if a PitchBend message...
if isinstance(msg, PitchBend):
string_msg = 'PitchBend'
# get value of pitchbend
string_val = str(msg.pitch_bend)
# if a CC message...
if isinstance(msg, ControlChange):
string_msg = 'ControlChange'
# get CC message number
string_val = str(msg.control)
# update text area with message type and value of message as strings
text_area.text = (string_msg + " " + string_val)

View on GitHub

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
  • code.py

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

circuitpy_20

How the CircuitPython Code Works

msg is acting as a variable to hold the incoming MIDI messages ‎received with midi.receive(). Every time a new message comes in, the ‎text on the screen is updated using text_area.text. The message values ‎are converted to strings using str() so that they can be displayed.‎

Usage

You can connect your QT Py RP2040 via USB to either your ‎computer or a USB MIDI host. Setup your software to send MIDI ‎messages out and you will see the messages update in real time on ‎the screen. ‎

You can use this project to figure out what MIDI messages are being ‎sent out, especially with ControlChange messages.

 

Receive MIDI Over UART and Send Over ‎USB

receive_11

In this example, you'll receive MIDI in over UART with the Adafruit ‎MIDI FeatherWing, using a DIN-5 connector, to send the received ‎MIDI messages out over USB with the Feather M4 Express. Basically, ‎converting a UART MIDI device to use USB MIDI.‎

Plug the Feather RP2040 and the Adafruit MIDI FeatherWing into a ‎FeatherWing Doubler.‎

plug_12

For more information on the Adafruit MIDI FeatherWing, check out ‎this guide

Setup the Feather RP2040‎

For this example, you'll be using the Feather RP2040. To make sure ‎it is setup properly with CircuitPython, please follow the steps in the ‎guide below.‎

Feather RP2040 CircuitPython Setup

CircuitPython Code

Once you've finished setting up your Feather 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.‎

Download Project Bundle

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

import board
import busio
import adafruit_midi
import usb_midi
from adafruit_midi.control_change import ControlChange
from adafruit_midi.pitch_bend import PitchBend
from adafruit_midi.note_off import NoteOff
from adafruit_midi.note_on import NoteOn

# uart setup
uart = busio.UART(board.TX, board.RX, baudrate=31250, timeout=0.001)
# midi channel setup
midi_in_channel = 1
midi_out_channel = 1
# midi setup
# UART is setup as the input
# USB is setup as the output
midi = adafruit_midi.MIDI(
midi_in=uart,
midi_out=usb_midi.ports[1],
in_channel=(midi_in_channel - 1),
out_channel=(midi_out_channel - 1),
debug=False,
)

print("MIDI UART In/USB Out")
print("Default output channel:", midi.out_channel + 1)

# array of message types
messages = (NoteOn, NoteOff, PitchBend, ControlChange)

while True:
# receive MIDI input from UART
msg = midi.receive()

# if the input is a recognized message...
if msg is not None:
for i in range(0, 3):
# iterate through message types
# makes it so that you aren't sending any unnecessary messages
if isinstance(msg, messages[i]):
# send the input out via USB
midi.send(msg)
print(msg)

‎View on GitHub

Upload the Code and Libraries to the Feather ‎RP2040‎

After downloading the Project Bundle, plug your Feather 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 Feather RP2040's CIRCUITPY drive.‎‎

  • lib folder
  • code.py

Your Feather RP2040 CIRCUITPY drive should look like this after ‎copying the lib folder and the code.py file.‎

drive_13

How the CircuitPython Code Works

The MIDI FeatherWing's DIN-5 (or TRS) connections use MIDI over ‎UART to transmit MIDI messages. Here, the code is setting ‎up midi_in to use UART and midi_out to use the Feather's USB ‎connection.‎

In the loop, when a recognized MIDI message is received, it is sent ‎out via USB. As a result, you have a small form factor DIN-5 MIDI to ‎USB MIDI converter.‎

Usage

You can connect your Feather via USB to either your computer or a ‎USB MIDI host. Then, plug your DIN-5 hardware controller's output to ‎the MIDI FeatherWing's input jack. When you send a MIDI message ‎from your hardware controller, it should transmit via USB.‎

This essentially creates a simple DIN-5 to USB converter box. You ‎could also do the reverse (taking MIDI in over USB and sending MIDI ‎out over UART) if you wanted to send software MIDI to a hardware ‎synth that did not have a USB connection.‎

 

Control Motors with MIDI

control_14

Musical robots controlled by incoming MIDI messages are a popular ‎project. This example will show how you can use MIDI in messages ‎to affect a servo motor.‎

Circuit Diagram

diagram_15

  • Servo GND to Metro M4 Express GND
  • Servo power to Metro M4 Express 5V
  • Servo signal to Metro M4 Express pin D2

Setup the Metro M4 Express

This example uses a Metro M4 Express microcontroller board. To ‎make sure it is setup properly with CircuitPython, please follow the ‎steps in the guide below.‎

Metro M4 Express CircuitPython Setup

CircuitPython Code

Once you've finished setting up your Metro M4 Express 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.‎

Download Project Bundle

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

import board
import pwmio
import usb_midi
import adafruit_midi
from adafruit_midi.note_off import NoteOff
from adafruit_midi.note_on import NoteOn
from adafruit_motor import servo

# pwm setup for servo
pwm = pwmio.PWMOut(board.D2, duty_cycle=2 ** 15, frequency=50)

# servo setup
motor = servo.Servo(pwm)

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

while True:
# receive midi input
msg = midi.receive()

if msg is not None:
# if a NoteOn message is received...
if isinstance(msg, NoteOn):
# servo set to 180 degrees
motor.angle = 180
# if a NoteOff message is received...
if isinstance(msg, NoteOff):
# servo set to 0 degrees
motor.angle = 0

View on GitHub

Upload the Code and Libraries to the Metro ‎M4 Express

After downloading the Project Bundle, plug your Metro M4 Express ‎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 Metro M4 Express's CIRCUITPY drive.‎‎ ‎

  • lib folder
  • code.py

Your Metro M4 Express CIRCUITPY drive should look like this after ‎copying the lib folder and the code.py file.‎

drive_16

How the CircuitPython Code Works

This code is an example of having another peripheral react to a MIDI ‎input. Every time a NoteOn message is received, the servo turns ‎to 180 degrees. When a NoteOff message is received, the servo turns ‎back to 0 degrees.‎

Usage

You can connect your Metro M4 via USB to either your computer or a ‎USB MIDI host. Send MIDI NoteOn and NoteOff messages to the Metro ‎and you should see the servo move back and forth accordingly.‎

Musical robots are a popular music technology project that often ‎incorporate MIDI. Using techniques like this can also be useful in ‎interactive installations where folks can see a physical reaction to a ‎digital input.‎

 

MIDI Projects on the Learn System

There are many fantastic MIDI projects on the Adafruit Learning ‎System for you to get further inspiration from. These projects are full ‎builds compared to the examples in this guide, so you can get some ‎ideas for enclosures, hardware and more advanced code.‎

Raspberry Pi Pico and LED Arcade Button ‎MIDI Controller

This project uses 16 light-up arcade buttons to send MIDI note ‎messages out. Additionally, it has a 5-way joystick and a screen to ‎change the MIDI note numbers assigned to each button on the go.‎

Trellis M4 Expressive MIDI Controller

This project uses the Trellis M4 to have 32 button inputs for sending ‎MIDI out. Additionally, it uses the built-in accelerometer to control ‎MIDI parameters by panning and tilting the board.‎

MIDI Solenoid Drummer

This project sends MIDI out to control solenoid motors. The solenoids ‎are setup to hit percussive objects to create a robotic percussion ‎instrument.‎

MIDI Melody Maker

This project sends MIDI note sequences out. There are parameters ‎for changing the note pattern, key and note value. Additionally, there ‎is a slider that affects the BPM, or the speed, at which notes are sent ‎out. ‎

Power Glove Wireless MIDI Controller

This project uses BLE MIDI to modify a classic Nintendo Power Glove ‎to be a wireless MIDI controller. Flex sensors in the fingers are used ‎to affect the values of the MIDI messages.

制造商零件编号 4740
ADAFRUIT MIDI FEATHERWING KIT
Adafruit Industries LLC
¥56.57
Details
制造商零件编号 4884
ADAFRUIT FEATHER RP2040
Adafruit Industries LLC
¥97.27
Details
制造商零件编号 4900
QT PY RP2040
Adafruit Industries LLC
¥80.99
Details
制造商零件编号 4500
CLUE NRF52840 EXPRESS
Adafruit Industries LLC
¥365.88
Details
制造商零件编号 3382
METRO ATSAMD51J19 EVAL BRD
Adafruit Industries LLC
¥223.84
Details
制造商零件编号 938
GRAPHIC DISPLAY OLED WHITE 1.3"
Adafruit Industries LLC
¥168.08
Details
制造商零件编号 2890
FEATHERWING DOUBLER - PROTOTYPIN
Adafruit Industries LLC
¥61.05
Details
制造商零件编号 239
BREADBRD TERM STRIP 2.20"-7.00"
Adafruit Industries LLC
¥50.13
Details
制造商零件编号 153
JUMPER WIRE M TO M VARIOUS
Adafruit Industries LLC
¥40.29
Details
制造商零件编号 1119
TACTILE SWITCH BUTTONS (12MM SQU
Adafruit Industries LLC
¥20.35
Details
制造商零件编号 169
SERVOMOTOR RC 5V TOWERPRO
Adafruit Industries LLC
¥53.52
Details
制造商零件编号 4210
JST SH 4-PIN CABLE - QWIIC COMPA
Adafruit Industries LLC
¥7.73
Details
制造商零件编号 5153
CABLE A PLUG TO C PLUG 3.28'
Adafruit Industries LLC
¥24.01
Details
制造商零件编号 3727
ITSYBITSY M0 EXPRESS ATSAMD21
Adafruit Industries LLC
¥97.27
Details
制造商零件编号 3857
FEATHER M4 EXPRESS ATSAMD51J19
Adafruit Industries LLC
¥186.81
Details
制造商零件编号 4062
ADAFRUIT FEATHER NRF52840 EXPRES
Adafruit Industries LLC
¥203.09
Details
Add all DigiKey Parts to Cart
TechForum

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.

Visit TechForum