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
制造商零件编号 4884
ADAFRUIT FEATHER RP2040
Adafruit Industries LLC
制造商零件编号 4900
QT PY RP2040
Adafruit Industries LLC
制造商零件编号 4500
CLUE NRF52840 EXPRESS
Adafruit Industries LLC
制造商零件编号 3382
METRO ATSAMD51J19 EVAL BRD
Adafruit Industries LLC
制造商零件编号 938
GRAPHIC DISPLAY OLED WHITE 1.3"
Adafruit Industries LLC
制造商零件编号 2890
FEATHERWING DOUBLER - PROTOTYPIN
Adafruit Industries LLC
制造商零件编号 239
BREADBRD TERM STRIP 2.20"-7.00"
Adafruit Industries LLC
制造商零件编号 153
JUMPER WIRE M TO M VARIOUS
Adafruit Industries LLC
制造商零件编号 1119
TACTILE SWITCH BUTTONS (12MM SQU
Adafruit Industries LLC
制造商零件编号 169
SERVOMOTOR RC 5V TOWERPRO
Adafruit Industries LLC
制造商零件编号 4210
JST SH 4-PIN CABLE - QWIIC COMPA
Adafruit Industries LLC
制造商零件编号 5153
CABLE A PLUG TO C PLUG 3.28'
Adafruit Industries LLC
制造商零件编号 3727
ITSYBITSY M0 EXPRESS ATSAMD21
Adafruit Industries LLC
制造商零件编号 3857
FEATHER M4 EXPRESS ATSAMD51J19
Adafruit Industries LLC
制造商零件编号 4062
ADAFRUIT FEATHER NRF52840 EXPRES
Adafruit Industries LLC
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