Having had a first play with my new Raspberry Pi Pico, I thought the next step was to make a simple MIDI controller with some IO. So I thought I’d get a simple music keyboard up and running.
- In part 2 I build this up on stripboard.
- In part 3 I expand it to four octaves.
- In part 4 I update the circuit to cope with “ghost” keypresses.
Warning! I strongly recommend using old or second hand equipment for your experiments. I am not responsible for any damage to expensive instruments!
These are the key tutorials for the main concepts used in this project:
- Getting Started with the Raspberry Pi Pico: Inputs and Outputs
- How to make a keyboard matrix
- Keyboard MIDI Matrix Decode
- MIDI, MicroPython and the Raspberry Pi Pico
If you are new to microcontrollers, see the Getting Started pages.
- Raspberry Pi Pico
- One of the 3.3V compatible Ready-Made MIDI Modules; or
- 5-pin 180 DIN socket, 10Ω and 33Ω resistors
- 12x breadboard friendly switches
- Breadboard and jumper wires
- MIDI module or sound generator (I used my Arduino MIDI VS1003 or VS1053 Synth)
This circuit is essentially hooking up 12 buttons formed to represent 12 notes of a piano keyboard to 12 input pins for the Pi Pico, in this case using GP2 to GP13.
The simplest way to do this would be to tie the other side of each button to either GND (if the inputs are configured for PULL_UP mode) or to +3.3V (if configure for PULL_DOWN mode).
Allowing for the use of TX on GP1, there are enough IO pins to allow 24 buttons to be connected directly – that’s enough for two full octaves of notes.
But I want to eventually have more notes than that. One common way would be to start including additional expander peripherals, but I’m trying to keep things relatively simple. So instead I am treating all 12 buttons in one octave as a single column in a keyboard “matrix” and hooking up the common pin from all the switches to another one of the Pico’s IO pins, in this case GP28.
To add another octave, the switch board must be duplicated and the individual switch connections hooked up to GP2 to GP13, duplicating the links to the first board. Then the common pin needs to be hooked up to an unused IO pin, for example GP27.
Further octaves can be added on a “one extra IO pin per octave” basis until all GPIO pins have been used. In theory, it should be possible to hook up 12 of these “octave” boards to the Pi Pico!
For now though, I’m sticking with a single octave for this first experiment.
This is using MicroPython once more so follow the relevant tutorials to get it running on your Pico. I’m using the Thonny editor to edit and load code. The code is saved as main.py onto the Pico so that it will run automatically on power up without being connected to a computer.
Caveat: I am still learning Python myself, so don’t expect code of any significant quality!
There are two key functions for this code:
- Setup and repeatedly scan the keyboard “matrix” to work out which keys have been pressed.
- Use this information to send the appropriate MIDI messages over the serial link.
I won’t go into detail of how to scan a keyboard matrix – there are details and links in one of my previous projects. For this first demonstration, the matrix is really just a single “column” of keys anyway.
The pins to be used for rows and columns (even though there is only one column) are set up in a list at the start and then those lists are used to initialise the Pins into another list called “rows” or “cols” as appropriate.
The columns are the outputs and the rows are the inputs. Ideally, the outputs would be configured in OPEN DRAIN mode which, whilst not directly supported on the RP2 used on the Pico, can be simulated within Micropython. This mode means that outputs have two states: LOW or “not connected”. This provides better handling of multiple button presses whilst scanning the matrix (see this for all the gory details) but at the time of writing the OPEN DRAIN simulation for the Pi Pico in Micropython was literally just a few days old and hadn’t made it into the release image yet, so I simply used the standard Pin.OUT mode for the time being. This will only be an issue when I have many columns (i.e. more octaves).
In terms of working out when to play MIDI notes, the simplest thing would be to send a MIDI noteOn when a switch is detected followed by a noteOff some time later. But the more sophisticated method is to recognise the OFF to ON and ON to OFF transitions for each individual switch and send the appropriate message as required. This is the approach I’m taking so there are two lists – playnote and lastnote – that record the new and previous states of each switch. This has the advantage that the keyboard is fully polyphonic.
As it stands the code has a starting MIDI note and will calculate which note to play depending on which switch has been pressed. This means it will only send the standard chromatic scale. It would be possible to maintain a list of specific notes if alternative scales were required.
The actual MIDI handling is as before – using a ustruct.pack of three bytes to construct the actual MIDI messages, which are sent on a hard-coded MIDI channel 1 for now.
The video above shows this driving my Arduino MIDI VS1003 or VS1053 Synth as a stand alone MIDI sound module using its default piano sound on channel 1.
Update: It is now possibly to use the MIDI serial port in CircuitPython. There are details of how to do that here.
Using these simple mechanical buttons on solderless breadboard can be frustrating as they tend to pop out with no warning, so although this is designed to allow the use of several octaves of buttons, I don’t recommend using solderless breadboard to make them!
This is now ready for transferring to simple proto or strip-board I think to test the multi-octave performance of the code.
It is really not stressing the Pi Pico at all yet though, and arguably there isn’t anything here that any other microcontroller couldn’t do. But that is ok. This is still general playing around at this stage.