Text Editor
Adafruit recommends using the Mu editor for using your CircuitPython code with the Pico. You can get more info in this guide.
Alternatively, you can use any text editor that saves files.
Download the Project Bundle
Your project will use a specific set of CircuitPython libraries and the code.py file. In order to get the libraries you need, click on the Download Project Bundle link below, and uncompress the .zip file.
Drag the contents of the uncompressed bundle directory onto your board's CIRCUITPY drive, replacing any existing files or directories with the same names, and adding any new ones that are necessary.
# SPDX-FileCopyrightText: 2021 John Park for Adafruit Industries # SPDX-License-Identifier: MIT # Pico RP2040 Mechanical MIDI Modal Keyboard # 7x3 mech keyboard # Each key sends MIDI NoteOn / NoteOff message over USB # Can be any scale/mode # Key combo sends MIDI panic (see bottom section of code) import time import board from digitalio import DigitalInOut, Direction, Pull import usb_midi import adafruit_midi from adafruit_midi.note_on import NoteOn from adafruit_midi.note_off import NoteOff from adafruit_debouncer import Debouncer print("---Pico MIDI Modal Mech Keyboard---") MIDI_CHANNEL = 1 # pick your MIDI channel here midi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=MIDI_CHANNEL-1) def send_midi_panic(): print("All MIDI notes off") for x in range(128): midi.send(NoteOff(x, 0)) led = DigitalInOut(board.LED) led.direction = Direction.OUTPUT led.value = True num_keys = 21 # list of pins to use (skipping GP15 on Pico because it's funky) pins = ( board.GP0, board.GP1, 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.GP16, board.GP17, board.GP18, board.GP19, board.GP20, board.GP21, ) keys = [] for pin in pins: tmp_pin = DigitalInOut(pin) tmp_pin.pull = Pull.UP keys.append(Debouncer(tmp_pin)) root_notes = (48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59) # used during config note_numbers = (48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83) note_names = ("C2", "C#2", "D2", "D#2", "E2", "F2", "F#2", "G2", "G#2", "A2", "A#2", "B2", "C3", "C#3", "D3", "D#3", "E3", "F3", "F#3", "G3", "G#3", "A3", "A#3", "B3", "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4",) scale_root = root_notes[0] # default if nothing is picked root_picked = False # state of root selection mode_picked = False # state of mode selection mode_choice = 0 # ----- User selection of the root note ----- # print("Pick the root using top twelve keys, then press bottom right key to enter:") print(". . . . . . .") print(". . . . . o o") print("o o o o o o .") while not root_picked: for i in range(12): keys[i].update() if keys[i].fell: scale_root = root_notes[i] midi.send(NoteOn(root_notes[i], 120)) print("Root is", note_names[i]) if keys[i].rose: midi.send(NoteOff(root_notes[i], 0)) keys[20].update() if keys[20].rose: root_picked = True print("Root picked.\n") # lists of mode intervals relative to root major = ( 0, 2, 4, 5, 7, 9, 11 ) minor = ( 0, 2, 3, 5, 7, 8, 10 ) dorian = ( 0, 2, 3, 5, 7, 9, 10 ) phrygian = ( 0, 1, 3, 5, 7, 8, 10 ) lydian = (0 , 2, 4, 6, 7, 9, 11 ) mixolydian = ( 0, 2, 4, 5, 7, 9, 10) locrian = ( 0, 1, 3, 5, 6, 8, 10) modes = [] modes.append(major) modes.append(minor) modes.append(dorian) modes.append(phrygian) modes.append(lydian) modes.append(mixolydian) modes.append(locrian) mode_names = ("Major/Ionian", "Minor/Aeolian", "Dorian", "Phrygian", "Lydian", "Mixolydian", "Locrian") intervals = list(mixolydian) # intervals for Mixolydian by default print("Pick the mode with top seven keys, then press bottom right key to enter:") print(". . . . . . .") print("o o o o o o o") print("o o o o o o .") while not mode_picked: for i in range(7): keys[i].update() if keys[i].fell: mode_choice = i print(mode_names[mode_choice], "mode") for j in range(7): intervals[j] = modes[i][j] # play the scale for k in range(7): midi.send(NoteOn(scale_root+intervals[k], 120)) note_index = note_numbers.index(scale_root+intervals[k]) print(note_names[note_index]) time.sleep(0.15) midi.send(NoteOff(scale_root+intervals[k], 0)) time.sleep(0.15) midi.send(NoteOn(scale_root+12, 120)) note_index = note_numbers.index(scale_root+12) print(note_names[note_index], "\n") time.sleep(0.15) midi.send(NoteOff(scale_root+12, 0)) time.sleep(0.15) keys[20].update() if keys[20].rose: print(mode_names[mode_choice], "mode picked.\n") mode_picked = True scale = [] # create the base scale for i in range(7): scale.append(scale_root + intervals[i]) midi_notes = [] # build the list with three octaves for k in range(7): midi_notes.append(scale[k]+24) for l in range(7): midi_notes.append(scale[l]+12) for m in range(7): midi_notes.append(scale[m]) led.value = False print("Ready, set, play!") while True: for i in range(num_keys): keys[i].update() if keys[i].fell: try: midi.send(NoteOn(midi_notes[i], 120)) note_index = note_numbers.index(midi_notes[i]) print("MIDI NoteOn:", note_names[note_index]) except ValueError: # deals w six key limit pass if keys[i].rose: try: midi.send(NoteOff(midi_notes[i], 0)) note_index = note_numbers.index(midi_notes[i]) print("MIDI NoteOff:", note_names[note_index]) except ValueError: pass # Key combo for MIDI panic # . o o o o o . # o o o . o o o # . o o o o o . if (not keys[0].value and not keys[6].value and not keys[10].value and not keys[14].value and not keys[20].value): send_midi_panic() time.sleep(1)
Use the Modal MIDI Keyboard
To test the keyboard, plug it into your computer and launch this handy Chrome browser MIDI Monitor web app to check that it is working.
Make Some Sound
MIDI note messages are fun to look at, but even better when the make some sound! Use a software synthesizer that accepts MIDI messages (pretty much all of them do!).
Here are some examples of free, open source synths for Linux, Windows, and mac os:
Launch your software synth and select the Pico CircuitPython keyboard as your MIDI source.
This video shows how root and mode selection work on startup. You can start over again at any time by pressing the reset button.
Root Note Selection
On startup, you can press each of the first 12 keys starting from the upper left corner of the keyboard to preview/select your scale root note.
Press the bottom right key to enter/commit the most recently previewed note.
Mode Selection
Once your root note is picked, the Modal MIDI keyboard goes into mode selection configuration. Press each of the keys on the top row of the keyboard to preview each mode:
- Major/Ionian
- Minor/Aeolian
- Dorian
- Phrygian
- Lydian
- Mixolydian
- Locrian
You'll hear each note of the selected mode play. Once you like your choice, press the lower right key to enter.
Now, you can start playing all three octaves!
Page last edited January 21, 2025
Text editor powered by tinymce.