Let’s continue working on Pico MIDI – a Raspberry Pi Pico based MIDI pedal. In my previous posts I covered how to send MIDI commands, how to support footswitch and LED and how to support external expression pedal.
Today, let’s add support for external switches. I’ll demonstrate that with RC-500 Boss looper, but I also controlled Digitech Whammy so make sure you check that out too.
In this post, I used Digitech FX3S, a 3 footswitch unit. The approach is similar for other external footswitches like Boss FS-5, FS-6 and FS-7. I don’t own one of those so some tweaking might be necessary for them. Boss pedals support different options so it’s a matter of trying them out really.
As usual – you can skip directly to the video and come back later for more details if you wish so.
Here’s the ToC:
External Footswitches
I dismantled my pedal to see what’s in it. Please don’t do it … that will void your warranty … on top of that, shematics for most of the pedals can be found online. Anyway, here’s what we’re dealing with:

There isn’t much to it, two diodes, 3 switches and a stereo audio jack. The switches are normally open, but if you have a different pedal, that may not be given. Some Boss pedals are normally closed, and on some you might have a switch to control this. But whatever it is, our software should be able to handle it.
How Does it Work?
Expanding the schematic a bit to include connection to Pico:

If we connect sleeve to ground, and then we connect ring and tip to a GPIO of our microcontroller each, we get the above schematic. Since one end of the switch is connected to ground we activate internal pull-up resistors.
When the switch is not pressed, there is no connection to ground and the microcontroller reads high level on GPIO. When the switch is pressed (stomped on), the GPIO input is driven down. Simple 🙂.
The diodes isolate tip and ring, and only if the 3rd switch (UP) is pressed, both are active and both tip and ring are driven low. If we did not connect it this way, if we connected sleeve to positive supply and we configured our GPIOs with pull-down resistors instead, our 3rd button would not work.
Connecting to Pico
Previous time, we finished with this schematic:

So sleeve is already grounded, and we have tip and ring connected to two GPIOs. Well, that looks like we’re done! No need to change the schematic, only software 🥳.
We did switches before, so if I just update the code slightly we should be good to go:
from machine import UART, Pin, ADC
import uasyncio as asyncio
from primitives import Pushbutton, AADC
pc_1 = b'\xc0\x00'
pc_2 = b'\xc0\x01'
pc_22 = b'\xc0\x15'
pc_23 = b'\xc0\x16'
pc_33 = b'\xc0\x20'
pc_34 = b'\xc0\x21'
pc_49 = b'\xc0\x30'
pc_50 = b'\xc0\x31'
class midi_command:
def __init__(self, cmd_on, cmd_off, print_on, print_off):
self.is_on = False
self.on = cmd_on
self.off = cmd_off
self.print_on = print_on
self.print_off = print_off
def toggle(self):
self.is_on = not self.is_on
def toggle(led, midi, cmd):
cmd.toggle()
if led != None:
led.value(1 if cmd.is_on else 0)
if cmd.is_on:
midi.write(bytearray(cmd.on))
print(cmd.print_on)
else:
midi.write(bytearray(cmd.off))
print(cmd.print_off)
async def my_app():
btn_pin = Pin(14, Pin.IN, Pin.PULL_UP)
red = Pin(15, Pin.OUT)
midi = UART(0, baudrate=31250, bits=8, parity=None, stop=1, tx=Pin(16), rx=Pin(17), invert=0)
ext_btn_pin1 = Pin(26, Pin.IN, Pin.PULL_UP) #tip
ext_btn_pin2 = Pin(27, Pin.IN, Pin.PULL_UP) #ring
pb = Pushbutton(btn_pin)
pb.press_func(toggle, (red, midi, midi_command(pc_1, pc_2, 'pc_1', 'pc_2'),))
pb_mode = Pushbutton(ext_btn_pin1)
pb_mode.press_func(toggle, (None, midi, midi_command(pc_22, pc_23, 'pc_22', 'pc_23'),))
pb_down = Pushbutton(ext_btn_pin2)
pb_down.press_func(toggle, (None, midi, midi_command(pc_33, pc_34, 'pc_33', 'pc_34'),))
while True:
await asyncio.sleep(60)
asyncio.run(my_app()) # Run main application code
Most of the code is just copy-pasted from previous posts. There’s more to it because I added more code so the demo is more exciting 🙂. It’s not a great demo if I’m not playing anything and just pressing switches randomly.
Watch the video for more details on how the code works, but hopefully the code is not too hard to follow. This code does not support the third button just yet, but I’ll work on that in future.
Configuration
OK, buttons are there, how does all of this fit together, because I kind of have everything to complete the pedal? Let me combine today’s code with last video’s code, but this time, I’ll add extra code to read configuration I stored to a file to indicate whether I’m using an external switches or expression pedal.
MicroPython supports reading from and writing to a file so the expanded code looks like this:
from machine import UART, Pin, ADC
import uasyncio as asyncio
from primitives import Pushbutton, AADC
pc_1 = b'\xc0\x00'
pc_2 = b'\xc0\x01'
pc_22 = b'\xc0\x15'
pc_23 = b'\xc0\x16'
pc_33 = b'\xc0\x20'
pc_34 = b'\xc0\x21'
pc_49 = b'\xc0\x30'
pc_50 = b'\xc0\x31'
# CC#11 - toe up/down
cc_toe_up = b'\xb0\x0b\x00'
cc_toe_down = b'\xb0\x0b\x7f'
cc_partial = b'\xb0\x0b'
class midi_command:
def __init__(self, cmd_on, cmd_off, print_on, print_off):
self.is_on = False
self.on = cmd_on
self.off = cmd_off
self.print_on = print_on
self.print_off = print_off
def toggle(self):
self.is_on = not self.is_on
def toggle(led, midi, cmd):
cmd.toggle()
if led != None:
led.value(1 if cmd.is_on else 0)
if cmd.is_on:
midi.write(bytearray(cmd.on))
print(cmd.print_on)
else:
midi.write(bytearray(cmd.off))
print(cmd.print_off)
#linear
def translate_pot_value(val):
(x2, y2) = (65_535.0, 65_535.0)
taper = [(0.0,0.0)]
for (x1, y1) in taper:
if val >= x1: #detect piecewise linear function
piecewise_portion = (val-x1)/(x2-x1) #portion of that piecewise function
piecewise_value = (y2-y1)*piecewise_portion + y1
return piecewise_value
(x2, y2) = (x1, y1)
return 0.0
def express(aadc, midi):
while True:
value = await aadc(512)
cmd = bytearray(cc_partial)
adc_value = int(translate_pot_value(value)/512)
cmd.append(adc_value)
print(adc_value)
out_len = midi.write(cmd) #setting out_len so count is not printed out
async def my_app():
btn_pin = Pin(14, Pin.IN, Pin.PULL_UP)
red = Pin(15, Pin.OUT)
midi = UART(0, baudrate=31250, bits=8, parity=None, stop=1, tx=Pin(16), rx=Pin(17), invert=0)
pb = Pushbutton(btn_pin)
pb.press_func(toggle, (red, midi, midi_command(pc_1, pc_2, 'pc_1', 'pc_2'),))
f = open('config.txt')
line = f.readline()
print(line)
f.close()
if line != '':
if line == 'BTN':
ext_btn_pin1 = Pin(26, Pin.IN, Pin.PULL_UP) #tip
ext_btn_pin2 = Pin(27, Pin.IN, Pin.PULL_UP) #ring
pb_mode = Pushbutton(ext_btn_pin1)
pb_mode.press_func(toggle, (None, midi, midi_command(pc_22, pc_23, 'pc_22', 'pc_23'),))
pb_down = Pushbutton(ext_btn_pin2)
pb_down.press_func(toggle, (None, midi, midi_command(pc_33, pc_34, 'pc_33', 'pc_34'),))
elif line == 'EXP':
aadc = AADC(ADC(Pin(27)))
driver = Pin(26, Pin.OUT)
driver.on()
asyncio.create_task(express(aadc, midi))
while True:
await asyncio.sleep(60)
asyncio.run(my_app()) # Run main application code
See the video for longer explanation of code, but in essence, the code uses config.txt to read configuration. In this very simple example this configuration is either BTN or EXP. If BTN is read, our Pico is configured to use the external jack for FS3X pedal. If EXP is read, Pico is configured to use ADC and expect expression pedal to be connected.
Now, this is super simplistic for the demo, I would imagine a full product would have some kind of a configuration tool with snazzy UI for this. Also, the code looks a bit complex, but all I really did was copy pasted from posts we covered before.
Warnings
I would like to point out one thing at this stage. It is a question of connecting external devices directly to your microcontroller like this. It’s a bit dangerous for your microcontroller because you don’t really know what is going to be connected to it. One of the sure ways to burn your Pico is to connect active inputs and drive your inputs with say 5V levels. That will damage your GPIOs.
Passive devices will be just fine, but there’s also a thing when you connect two GPIOs directly – which may not be immediatelly apparent but as you saw, 3rd button is kind of doing this. For this post, we’re using GPIOs as inputs so not such a big deal. But for CMOS devices,
if you configure GPIOs as outputs and drive one HIGH and the other LOW, there is a great chance of damaging your device due to excessive current.
Pico does list out maximum output current, but this is misleading, that is
maximum output current that will still preserve correct output voltage levels. So in this kind of situation, current may well go over that value. Just something to be aware of, at the moment I’ll finish with that.
Action
Here’s a (not so) quick demo of what I covered here:
This one was relatively small (well I still manage to make the video too long) advancement towards our finished pedal. Next time I’ll cover some more flashy stuff (Neopixels and RGB LEDs). Subscribe so you don’t miss out 😉