In this project I’ve added some basic IO to the Pi Pico PIO Poly Tone Step Sequencer.
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:
- MIDI, MicroPython and the Raspberry Pi Pico
- Pimoroni RGB Keypad Base Tutorial
- Getting started with the Pico – Using Digital Inputs and Outputs
- Pi Pico PIO Poly Tone MIDI “Pack”
If you are new to microcontrollers, see the Getting Started pages.
Parts list
- Raspberry Pi Pico
- 1x10k potentiometer
- 1x button switch
- Solderless breadboard and jumper wires
- Pimoroni RGB Keypad Base
- Pimoroni Pico Omnibus Dual Expander (optional)
- Pi Pico PIO Poly Tone MIDI “Pack” or solderless breadboard equivalent
- Amplification as required
The Circuit
Once again I’m using my RGB Keypad and my Tone Pack. here you can see a potentiometer connected to 3V, GND and ADC2, and a button switch across GP6 and GND. If you are not using my “pack” but hooking up to your Pi Pico itself, you can connect these direct.
The Code
This is an addition to the code in the Pi Pico PIO Poly Tone Step Sequencer project. I’ve added a new routine “updateIO” to scan the additional GPIO and update various aspects of the sequencer as required. In this case I’m using the potentiometer to set the tempo and the button to reset the grid back to playing no notes.
Before anything else can happen the IO pins must be initialised. The following sets up two buttons and three potentiometers on the pins as used by my “tone pack”.
button1 = machine.Pin(6, machine.Pin.IN, machine.Pin.PULL_UP) button2 = machine.Pin(7, machine.Pin.IN, machine.Pin.PULL_UP) pot1 = machine.ADC (machine.Pin(26)) pot2 = machine.ADC (machine.Pin(27)) pot3 = machine.ADC (machine.Pin(28)) btn1val = True btn2val = True
btn1val and btn2val can be used to detect a HIGH to LOW transition on the buttons – note that they are in PULL_UP mode, so normally will be reading True until pressed, when they will switch to False.
The updateIO routing reads the two IO values of interest and then actions them.
def ioUpdate(): global TEMPO, btn1val TEMPO = (pot3.read_u16()>>6)+30; if TEMPO > 800: TEMPO = 800 if TEMPO < 60: TEMPO = 60 btn1 = button1.value() if (btn1val == True) and (btn1 == False): for key in range (NUM_PADS): chordOff(noteGrid[key]) noteGrid[key] = 0 btn1val = btn1
The potentiometer reading is scaled from a 0 to 65535 (u16) value into a range between 30 and a few hundred and stored in the TEMPO global variable which is used in the main scanning routine later on to set the timings of the sequencer.
On a button press the note grid is initialised back to 0s, after having send “off” messages for an already sounding notes.
All that is left is to make sure the updateIO() function is called as part of the main “loop” routing in the code.
Closing Thoughts
Handling IO in MicroPython is relatively straight forward so now that the basics are in place, we can start to experiment with other controllable parameters to change other aspects of the sound generation.
Kevin