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.
# SPDX-FileCopyrightText: 2023 Liz Clark for Adafruit Industries
# SPDX-License-Identifier: MIT
import time
import board
from adafruit_motor import servo
from adafruit_pca9685 import PCA9685
import usb_midi
import adafruit_midi
from adafruit_midi.note_on import NoteOn
# MIDI input setup
midi = adafruit_midi.MIDI(midi_in=usb_midi.ports[0], in_channel=0)
# i2c PCA9685 setup
i2c = board.STEMMA_I2C()
pca = PCA9685(i2c)
pca.frequency = 50
# create the servo objects and add them to the servos array
servos = [servo.Servo(pca.channels[i]) for i in range(16)]
# array of midi notes, high to low
midi_notes = [83, 81, 79, 77, 76, 74, 72, 71, 69, 67, 65, 64, 62, 60, 59, 57]
angle0 = 20
angle1 = 70
# set servos to the same angle on boot
# easier to adjust angles of the horns if needed
print("setting servos")
for i in range(16):
s = servos[i]
s.angle = angle1
time.sleep(0.05)
print("servos set")
while True:
# msg holds MIDI messages
msg = midi.receive()
for i in range(16):
# iterate through servos array & midi notes array
servo = servos[i]
note_played = midi_notes[i]
# if a noteon msg comes in that matches a note in the midi notes array..
if isinstance(msg, NoteOn) and msg.note == note_played:
# print(servo)
# print(note_played)
# servo moves
# angle alternates between angle0 and angle1
if servo.angle <= angle0:
servo.angle = angle1
else:
servo.angle = angle0
# print(servo.angle)
Upload the Code and Libraries to the Feather RP2040
After downloading the Project Bundle, plug your Feather RP2040 into the computer's USB port with a known good USB data+power cable. 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:
How the CircuitPython Code Works
The code begins by creating a midi object over USB as a MIDI input. This means that the Feather will take in, or receive, MIDI messages from another source. The source could be your computer or an external MIDI device.
# MIDI input setup midi = adafruit_midi.MIDI(midi_in=usb_midi.ports[0], in_channel=0)
Servos Over I2C
The STEMMA QT port on the Feather is setup as the default i2c object. The PCA9685 servo driver is instantiated over I2C. Then, the servo objects are created and added to the servos array with a Python list comprehension.
# i2c PCA9685 setup i2c = board.STEMMA_I2C() pca = PCA9685(i2c) pca.frequency = 50 servos = [servo.Servo(pca.channels[i]) for i in range(16)]
Arrays of Servos and Notes
An array is created for the MIDI note numbers. This array will correspond with the servos array. Both will be iterated through in the loop. If you want to change which MIDI notes are assigned to each servo, you can edit the midi_notes array. By default, it lists a C major scale.
# array of midi notes, high to low midi_notes = [83, 81, 79, 77, 76, 74, 72, 71, 69, 67, 65, 64, 62, 60, 59, 57]
Angles
The servos will move back and forth between two set angles. These angles are defined with angle0 and angle1. Before the loop, all of the servos are set to angle1. This makes it easier to adjust the servo horns if needed.
angle0 = 20
angle1 = 70
# set servos to the same angle on boot
# easier to adjust angles of the horns if needed
print("setting servos")
for i in range(16):
s = servos[i]
s.angle = angle1
time.sleep(0.05)
print("servos set")
The Loop
In the loop, the Feather listens for incoming MIDI messages with midi.receive(). If a NoteOn message comes in containing a note number that is defined in the midi_notes array, then the corresponding servo will move to either angle0 or angle1 depending on the current angle of the servo.
while True:
# msg holds MIDI messages
msg = midi.receive()
for i in range(16):
# iterate through servos array & midi notes array
servo = servos[i]
note_played = midi_notes[i]
# if a noteon msg comes in that matches a note in the midi notes array..
if isinstance(msg, NoteOn) and msg.note == note_played:
# print(servo)
# print(note_played)
# servo moves
# angle alternates between angle0 and angle1
if servo.angle <= angle0:
servo.angle = angle1
else:
servo.angle = angle0
# print(servo.angle)
Page last edited January 21, 2025
Text editor powered by tinymce.