
This may be the easiest electronic project I’ve ever made. I’ve been helping a magician friend who has been doing virtual magic shows over zoom during the Covid-19 pandemic. I use OBS which is open source software used by live streamers. I have it hooked up through zoom so I can choose to display my webcam as I introduce Lawrence the magician, and I can also switch to show a poster image, a black screen, or a video. OBS also allows me to mix in music and sound effects for the show and does some audio processing on my mic. There’s a lot to do at times (ie. when I have to change the spotlighted video in zoom at the same time as I start a music track on my iPad and change my OBS scene to a black screen so I’m not distracting from the show and mute my mic). OBS has good support for user definable hotkeys which can be set to perform all kinds of functions, but I had a couple of problems with them which I’ll get into later so I decided it’d be easiest for me to make a hardware box that could take care of some OBS functions instead.


I decided to use a project box that I had on hand rather than 3d printing one. It’s bigger than it needs to be but that’s fine for this use case. I drilled some holes with a step bit and popped in some arcade buttons. All the buttons are connected to pins on an arduino Micro which can act as a HID (human interface device… that means that it can act as keyboard or mouse via USB unlike the arduino nano which is my usual go-to microcontroller for simple projects). The other conductors of each button are all ganged together and connected to a ground pin of the arduino. I declared each pin as an INPUT_PULLUP to make use of the built-in pullup resistors in the arduino so I didn’t need to add external resistors. I plugged a USB cable into the arduino Micro and tied a little knot inside for strain relief. That’s it for the hardware! Because the device is powered by USB I don’t need to deal with batteries, and there are no other sensors or actuators. It almost feels like cheating to put such little hardware into a big box and not even have to worry about space. What a treat.
The rest is all just code. The first problem I had when using hotkeys to control OBS was that if I chose easy keys (single letters or numbers) then those keystrokes would be activated by accident if I was typing in a different program (like in the zoom chat window). Using key combinations with modifier keys would solve that, but it would also compromise the ease of use of just hitting a single key to switch scenes, for example. With this hardware solution, I can send any keystrokes that I want when I press each button, so it’s simple to send a very complex combination of keys with a single button. I chose to send Ctrl>Alt>Cmd>F1 for the first button, and the same combination with different function keys for each of the other buttons. Now I don’t have to worry about accidentally triggering OBS functions while typing in the zoom chat.
Here’s the arduino code I wrote to poll the buttons (there aren’t enough interrupt pins to use interrupts for each button so I have to poll them but it’s plenty fast enough for this case):
//Remember that I'm a self taught hack so there is almost
//certainly a more elegant, efficient way to do this.
//If you have suggestions for improvement I'd love to hear them.
//But... it works just fine the way it is written so I'm ok with it.
//library used to send keystrokes
#include "Keyboard.h"
//these are the hardware pins stored in an array
const int bp[10] = {8, 9, 10, 11, 12, 7, 6, 5, 4, 3};
//create an array to store the previous state of each button
bool previousButtonState[10];
//create an array to store the current state of each button
bool buttonState[10];
void setup() {
//loop through the array to initialize all previousButtonStates as HIGH
for (int i = 0; i < 10; i++) {
previousButtonState[i] = HIGH;
}
//loop through the array to set each pin as INPUT_PULLUP
for (int i = 0; i < 10; i++) {
pinMode(bp[i], INPUT_PULLUP);
}
// initialize control over the keyboard:
Keyboard.begin();
}
void loop() {
//loop over and over reading all the button states
for (int i = 0; i < 10; i++) {
buttonState[i] = digitalRead(bp[i]);
//if the button state has changed...
if (buttonState[i] != previousButtonState[i]) {
//and it's now pressed
if (buttonState[i] == LOW) {
//run this function passing in the array index
pressButton(i);
previousButtonState[i] = LOW;
} else {
previousButtonState[i] = HIGH;
}
}
}
}
void pressButton(int buttonNum) {
// for all buttons, start by pressing three modifier keys
Keyboard.press(KEY_LEFT_ALT);
Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press(KEY_LEFT_GUI);
//then add one more key depending on which button is pressed
switch (buttonNum) {
case 0:
Keyboard.press(KEY_F1);
break;
case 1:
Keyboard.press(KEY_F2);
break;
case 2:
Keyboard.press(KEY_F3);
break;
case 3:
Keyboard.press(KEY_F4);
break;
case 4:
Keyboard.press(KEY_F5);
break;
case 5:
Keyboard.press(KEY_F6);
break;
case 6:
Keyboard.press(KEY_F7);
break;
case 7:
Keyboard.press(KEY_F8);
break;
case 8:
Keyboard.press(KEY_F9);
break;
case 9:
Keyboard.press(KEY_F10);
break;
}
//release all keys
Keyboard.releaseAll();
//and a debouncing delay to get rid of double presses
delay(200);
}
The second problem with OBS hotkeys is still a mystery to me. They just stopped working when OBS didn’t have focus. I don’t know if I updated something that caused a conflict (OBS itself or MacOS Catalina?). I spent some time trying to figure out what happed but couldn’t manage to restore the functionality. That solved my first problem of accidentally triggering OBS functions, but if I have to switch focus from Zoom to OBS to use the hotkeys then they aren’t as useful. I was almost ready to give up when it occurred to me that another program which I use called Keyboard Maestro might come to the rescue. It did. Keyboard Maestro is a MacOS utility which can do a whole lot of automation tasks but I just use it to run user defined macros. It was just what I needed. I created macros for each hardware button. With each button press the macro starts by making OBS the app with focus (so that the hotkeys will work). Then the macros send a series of key combinations with a short delay between each. Those key combinations are all things that I assigned to various functions in OBS (switching scenes and making sure that my mic is or isn’t muted and that the iPad which is connected as an audio source is or isn’t muted depending on the scene, etc). The last thing that the macros do is return focus to the previous app. This way, while I am in the Zoom app, I can tap a button on my little box and a series of different actions take place and return focus right back to Zoom. I’m pleased with how well it works. Here’s an example of what one of my macros looks like in Keyboard Maestro

If I didn’t have the problem of OBS hotkeys not responding when OBS doesn’t have focus I would not need to use keyboard maestro at all and could just add the delays and the other keystrokes for the multiple functions all within the arduino program itself. Because the device is already plugged into the computer, it’s just as easy to fire up the arduino IDE and reprogram the box as it is to fire up keyboard maestro and change the functionality there.