When Elgato announced its new Stream Deck pedal, the maker world said, “Hey, I can make one for less money.” And with this battle cry we reached our faithful Raspberry Pi Pico (in our case Adafruit KB2040) and looted the switch box to create our own alternative.
The Elgato Stream Deck Pedal is a three-pedal controller used to trigger actions during your streams. We’ll emulate this functionality using three heavy-duty switches, intended for guitar effects pedals, and a Raspberry Pi Pico running Adafruit’s CircuitPython. With CircuitPython, we can create a USB keyboard and assign keystrokes, or even entire sequences of presses, to a single button.
Once we’ve built and coded our Pico pedal, we’ll learn how to use it with OBS, ready for our next stream.
For this project you will need
Building the Pico Pedal
The circuit for this project is extremely simple. It uses three GPIO pins (2, 3, and 4) and a common ground (GND) connection. We connect the buttons to pins 2, 3, and 4, then connect each button to a different GND pin on the Pico. All GND pins connect to a single GND, creating a common reference point.
Cabling and preparing the cabinet
Before going into details, here is a word on security. Welding is a great skill to learn, but make sure you wear proper eye protection and weld in a well-ventilated area. Drilling into plastic or metal will produce “chips”, wires and chunks of drilled material. Wear protective eyewear and double check your positions before beginning the exercise. Make sure the casing is held firmly in place and always drill a pilot hole before drilling larger holes.
We chose to use momentary foot switches, commonly used in guitar pedals. They are designed to be stamped and therefore have the best chance of surviving the longest. The wiring is simple. They have no polarity, rather they make a connection when pressed. In this project they connect one side of the switch to a GPIO pin, pulled high using the Pico’s internal resistors to the other side which is connected to ground (GND). By pressing the button, we establish the connection between the two sides, pulling the GPIO pin down and triggering a key press.
We have chosen an alternative to welding. Crimp terminals are a solderless method that uses friction to hold the wires in place. The crimps hold the wire inside a plastic sleeve. In the sleeve is a metal terminal. We press the crimp with a tool and this locks the wire in place. The crimp terminal then slides over the switch contacts. Crimps are faster and easier than soldering, but they are an additional cost.
These switches must be secured in a suitable enclosure. Typically, we’d use a plastic case, but unless we pay a lot of money, we’ll end up with flimsy plastic. Rugged ABS plastic enclosures can be found for around $10, but we’ve opted for an aluminum enclosure, to match the pedal’s rugged, industrial aesthetic.
1. Measure three points on the enclosure cover and drill a pilot hole for each. We chose the dead center of the case as a starting point, then measured 5cm in both directions from the center. In hindsight, we would have moved further, say 7cm, and created a triangle of dots instead of a line.
2. Move to the side of the enclosure and drill another pilot hole for the USB cable.
3. Switch to a larger drill and enlarge all the holes. Do it a few times before continuing.
4. Switch to a stepped drill then enlarge the three holes in the cover to match the diameter of the pedal buttons. In our case we had 12mm switches, so we used a piece of tape to identify the pitch on the bit.
5. Use a file to remove the sharp edges from the holes.
6. Use the step drill to enlarge the remaining pilot hole to match the size of the smaller end of your USB cable.
seven. File the hole to remove any sharp edges. This is important for the USB cable as it rubs against the hole and can be damaged.
8. Thread a suitable USB cable through the hole, connect it to your Pico and add a large knot to relieve the strain. The knot will reduce the risk of accidental removal of the USB cable.
9. Secure Button Wires to the screw terminal according to the circuit diagram.
You should now have all three buttons connected to the Raspberry Pi Pico via the screw terminal. Do not close the lid until the code has been tested.
Install CircuitPython and Libraries
Installing CircuitPython is extremely easy, and Adafruit has a fantastic guide on how to do it. To follow Instructions from Adafruit to install the latest version.
Our Raspberry Pi Pico now appears as a drive in the file manager. The drive is called CIRCUITPY and there we find a series of files and folders. Right now we need to focus on lib. We need to download a ZIP file full of libraries and install the correct files for our projects
1. Download the latest set of libraries from the CircuitPython website.
2. Extract files on your desktop.
3. Copy the adafruit_hid folder from extracted files in the lib folder on your CIRCUITPY player.
Pico Pedal Coding
1. Open the code.py file found in the root of the CIRCUITPY drive in your favorite editor.
2. Delete any code in the file.
3. Import two libraries to enable GPIO access. The first board is a basic way to connect to the GPIO pins, the second provides a way to check the state of the pins.
import board from digitalio import DigitalInOut, Direction, Pull
4. Import four libraries to emulate US layout USB keyboard and keystrokes.The USB HID (Human Interface Device) library allows our Pico to become a keyboard. The remaining libraries configure the keyboard to use a US layout and to use the keycode to send keystrokes.
import usb_hid from adafruit_hid.keyboard import Keyboard from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS from adafruit_hid.keycode import Keycode
5. Finally, import the time library. This library is used to add a break to code. This reduces the risk of accidental double pressing (anti-bounce).
6. Create a keyboard object that will act as a USB HID.
keyboard = Keyboard(usb_hid.devices)
seven. Set the layout to US.
keyboard_layout = KeyboardLayoutUS(keyboard)
8. Create a new object, a, to represent the first button of the pedal. This button is connected to pin 2 of the Pico.
one = DigitalInOut(board.D2)
9. Using an object, set the pin as an input, and pull the internal resistor up, so the pin is high (on) at 3.3V.
one.direction = Direction.INPUT one.pull = Pull.UP
ten. Repeat the process for a second entry. This time the variable should be connected to pin 3.
two = DigitalInOut(board.D3) two.direction = Direction.INPUT two.pull = Pull.UP
11. Repeat for a third input connected to pin 4.
three = DigitalInOut(board.D4) three.direction = Direction.INPUT three.pull = Pull.UP
12. Create variable, delay, to hold the pause duration of our code. Using a variable makes it easy to adjust the duration according to our needs.
delay = 0.5
13. Create a loop to continuously run our code.
14. Create a conditional test that will check the state of button one. If pressed, the button will connect the GPIO pin, 2 (which is True/HIGH), to ground (GND) and change the state to False/LOW.
if one.value == False:
15. Add a print statement to indicate that the button press has been registered. This is a debugging step that can be skipped, but we find it useful to prove that the code registers a press because it eliminates one thing to debug.
16. Send a series of key presses. In our case we used CTRL+ALT+A, but you can change that to any key you want and Adafruit has the list of all the keys you need.
keyboard.send(Keycode.CONTROL, Keycode.ALT, Keycode.A)
17. Release the keys. By doing this without delay, we limit the keybinding to a single keypress.
18. Add a delay to reduce the risk of pressing keys multiple times.
19. Repeat the process for the second button.
elif two.value == False: print("Button 2") keyboard.send(Keycode.CONTROL, Keycode.ALT, Keycode.B) keyboard.release_all() time.sleep(delay)
20. Repeat the process for the third button.
elif three.value == False: print("Button 3") keyboard.send(Keycode.CONTROL, Keycode.ALT, Keycode.C) keyboard.release_all() time.sleep(delay)
21. Add another condition to print “Waiting for button press” with a short delay before the loop repeats. The else condition is activated when no button is pressed.
else: print("Waiting for button press") time.sleep(0.1)
Save the code to your Raspberry Pi Pico and CircuitPython will reload and run the code. Don’t close the case just yet!
Using the Pico Pedal with OBS
Open Broadcaster Software (OBS) is a remarkable free streaming tool. With OBS we can live stream to YouTube, Twitch and many other platforms. We can also record videos for later editing. OBS uses Scenes to create different camera angles, screencasts, and other video inputs.
We use a number of scenes for The Pi Cast, our weekly Raspberry Pi show. We have a scene for our front camera, a scene for the aerial cameras to show a project, and a scene to show a stream of images or plugs techniques. To swap scenes, we have a keyboard programmed to move between them using hotkeys. The Pico pedal can be used in the same way. You will need to have already created the Scenes in order to create hotkeys for them.
1. OBS open and go to settingsfound in the lower right corner.
2. Click Keyboard Shortcuts.
3. Scroll to the scene for which you want to add a hotkey. In our example, we go to Face.
4. Click on the black box to switch to the scene.
5. On your pedal, press the button you want to assign to the scene. OBS will update to show this button press.
6. Click Apply and OK to save the keyboard shortcut.
seven. Repeat the process for the two additional buttons.
You now have your pedal buttons mapped to OBS, ready for your next stream. Now you can move smoothly from scene to scene without touching the keyboard.
Complete list of codes
import board from digitalio import DigitalInOut, Direction, Pull import usb_hid from adafruit_hid.keyboard import Keyboard from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS from adafruit_hid.keycode import Keycode import time keyboard = Keyboard(usb_hid.devices) keyboard_layout = KeyboardLayoutUS(keyboard) one = DigitalInOut(board.D2) one.direction = Direction.INPUT one.pull = Pull.UP two = DigitalInOut(board.D3) two.direction = Direction.INPUT two.pull = Pull.UP three = DigitalInOut(board.D4) three.direction = Direction.INPUT three.pull = Pull.UP delay = 0.5 while True: if one.value == False: print("Button 1") keyboard.send(Keycode.CONTROL, Keycode.ALT, Keycode.A) keyboard.release_all() time.sleep(delay) elif two.value == False: print("Button 2") keyboard.send(Keycode.CONTROL, Keycode.ALT, Keycode.B) keyboard.release_all() time.sleep(delay) elif three.value == False: print("Button 3") keyboard.send(Keycode.CONTROL, Keycode.ALT, Keycode.C) keyboard.release_all() time.sleep(delay) else: print("Waiting for button press") time.sleep(0.1)