Code the Stomp Box
Once you've finished setting up your Pico 2 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 to your computer as a zipped folder.
# SPDX-FileCopyrightText: 2026 Liz Clark for Adafruit Industries
# SPDX-License-Identifier: MIT
"""Bass Synth MIDI Stomp Box"""
import board
import busio
import adafruit_midi
import keypad
from digitalio import DigitalInOut, Direction
# pylint: disable=unused-import
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
from adafruit_midi.program_change import ProgramChange
from adafruit_ticks import ticks_ms, ticks_add, ticks_diff
# status LED
led = DigitalInOut(board.GP18)
led.direction = Direction.OUTPUT
led.value = True
# UART MIDI
uart = busio.UART(board.GP0, board.GP1, baudrate=31250)
# midi channel setup
midi_out_channel = 1
# midi setup - UART out on GP0
midi = adafruit_midi.MIDI(
midi_out=uart,
out_channel=(midi_out_channel - 1),
)
# foot switches as keypad object
KEY_PINS = (
board.GP2,
board.GP3,
board.GP4,
board.GP5,
board.GP6,
board.GP7,
board.GP8,
board.GP9,
board.GP10,
board.GP11,
board.GP12,
board.GP13,
board.GP14,
board.GP15,
)
notes = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59]
keys = keypad.Keys(KEY_PINS, value_when_pressed=False)
# variables & states
MIN_NOTE = 24 # lowest starting octave
MAX_NOTE = 84 # highest starting octave
channel_num = midi.out_channel # track channel
pressed_channel = False # did we try to change the MIDI channel
blink_count = 0 # number of times the LED has blinked
clock = ticks_ms() # time keeping
blink_timer = 500 # blink interval (0.5 seconds)
while True:
event = keys.events.get()
if event:
if event.pressed:
if event.key_number == 12:
# change MIDI channel
channel_num = (channel_num + 1) % 16
midi.out_channel = channel_num
print(channel_num + 1)
pressed_channel = True
led.value = False
blink_count = 0
clock = ticks_ms()
elif event.key_number == 13:
# checks if transposing the first note (C) by 1 octave would exceed the MAX_NOTE
# if it does, wraps the array down to start at MIN_NOTE
# otherwise, transposes all notes up by one octave
notes = [MIN_NOTE + (n - notes[0]) if notes[0] + 12 > MAX_NOTE
else n + 12 for n in notes]
else:
# otherwise send noteOn message
midi.send(NoteOn(notes[event.key_number], 120))
if event.released:
if event.key_number < 12:
midi.send(NoteOff(notes[event.key_number], 120))
if pressed_channel:
# blink LED to show what MIDI channel we're on
if ticks_diff(ticks_ms(), clock) > blink_timer:
if not led.value:
led.value = True
blink_count += 1
else:
led.value = False
clock = ticks_add(clock, blink_timer)
# reset after blinking
if blink_count == (channel_num + 1):
pressed_channel = False
blink_count = 0
led.value = True
Upload the Code and Libraries to the Pico 2
After downloading the Project Bundle, plug your Pico 2 into your 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 Pico 2's CIRCUITPY drive.
- lib folder
- code.py
Your Pico 2 CIRCUITPY drive should look like this after copying the lib folder and code.py file:
How the CircuitPython Code Works
The code begins by initializing the status LED and UART for MIDI output.
# status LED
led = DigitalInOut(board.GP18)
led.direction = Direction.OUTPUT
led.value = True
# UART MIDI
uart = busio.UART(board.GP0, board.GP1, baudrate=31250)
# midi channel setup
midi_out_channel = 1
# midi setup - UART out on GP0
midi = adafruit_midi.MIDI(
midi_out=uart,
out_channel=(midi_out_channel - 1),
)
Foot Switches
All of the foot switches are passed to a Keypad object. The notes array has all of the MIDI note numbers that are assigned by default to the first 12 switches.
# foot switches as keypad object
KEY_PINS = (
board.GP2,
board.GP3,
board.GP4,
board.GP5,
board.GP6,
board.GP7,
board.GP8,
board.GP9,
board.GP10,
board.GP11,
board.GP12,
board.GP13,
board.GP14,
board.GP15,
)
notes = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59]
keys = keypad.Keys(KEY_PINS, value_when_pressed=False)
Variables and States
A few variables and states are used in the loop.
-
MIN_NOTEandMAX_NOTEdenote the lowest and highest starting octaves for the foot switches -
channel_numis used to track the current MIDI channel number -
pressed_channelchecks if the foot switch for changing the MIDI channel has been pressed -
blink_counttracks how many times the status LED has blinked -
clockandblink_timerare used for time keeping when blinking the LED
# variables & states MIN_NOTE = 24 # lowest starting octave MAX_NOTE = 84 # highest starting octave channel_num = midi.out_channel # track channel pressed_channel = False # did we try to change the MIDI channel blink_count = 0 # number of times the LED has blinked clock = ticks_ms() # time keeping blink_timer = 500 # blink interval (0.5 seconds)
The Loop
In the loop, keypad events are monitored for the foot switch inputs. If any of the first 12 foot switches are pressed, then a MIDI note on message is sent and a note off message is sent when the switch is released. Foot switch 13 changes the MIDI channel number and foot switch 14 changes the octave of the notes that are being sent by the foot switches.
while True:
event = keys.events.get()
if event:
if event.pressed:
if event.key_number == 12:
# change MIDI channel
channel_num = (channel_num + 1) % 16
midi.out_channel = channel_num
print(channel_num + 1)
pressed_channel = True
led.value = False
blink_count = 0
clock = ticks_ms()
elif event.key_number == 13:
# checks if transposing the first note (C) by 1 octave would exceed the MAX_NOTE
# if it does, wraps the array down to start at MIN_NOTE
# otherwise, transposes all notes up by one octave
notes = [MIN_NOTE + (n - notes[0]) if notes[0] + 12 > MAX_NOTE
else n + 12 for n in notes]
else:
# otherwise send noteOn message
midi.send(NoteOn(notes[event.key_number], 120))
if event.released:
if event.key_number < 12:
midi.send(NoteOff(notes[event.key_number], 120))
If the MIDI channel changes, then the status LED blinks the number of the channel selected. For example, if you are on MIDI channel 5, then the LED will blink 5 times. This blinking is non-blocking thanks to the ticks() library. You can press any of the foot switches while the blinking is happening.
if pressed_channel:
# blink LED to show what MIDI channel we're on
if ticks_diff(ticks_ms(), clock) > blink_timer:
if not led.value:
led.value = True
blink_count += 1
else:
led.value = False
clock = ticks_add(clock, blink_timer)
# reset after blinking
if blink_count == (channel_num + 1):
pressed_channel = False
blink_count = 0
led.value = True
Page last edited January 28, 2026
Text editor powered by tinymce.