I wanted a project to have two keys that mute/unmute microphone and turn on and off camera on Zoom with a LED to show if it's both are on or not. I spotted this project:
I thought it suited my needs and would help me learn about microcontrollers so naively bought the the parts needed. However, I couldn't find a guide for this project anywhere. This was frustrating at first given my very limited knowledge of microcontrollers, CircuitPython (python in general), arduninos etc. I was lost and didn't know where to start. In retrospect however, this has been a great learning experience.
I'll use this project to document my findings so should anyone else have the same issues I faced you have some of the information needed all in one place.
Aims
To have a two keys that are separate from my main keyboard that: a. toggle mute b. toggle video
To have the mac tell the leds on the keyboard whether zoom is using the camera/microphone and be red if not and green if so. [I'm still working on this track my progress here https://github.com/athenasbane/NeoKey_two_key_ardunino/]
What you need:
- Breadboard
Jumper cables
USB-C Cable
NeoKey Socket Breakout x 2 - https://www.adafruit.com/product/4978
Cherry Mx Keyboard Switch of your choice x 2 - I used Cherry Mx Blues but I which I had used Cherry Mx Black. You can use anything with a Kailh socket
QtPy - There's lots of variants I used this one - https://www.adafruit.com/product/4600
Starting on the top:
** QT PY **
Top
GND Pin -> GND Rail
3v Pin -> POWER Rail
Bottom
A1 Pin -> NeoKey 2 - A Pin
A2 Pin -> NeoKey 1 - A Pin
A3 Pin -> NeoKey 1 - I Pin
** NeoKey 1 ** Make sure the orientation of the NeoKey is the correct: + - 0 A C on top, NeoPixel on the bottom
Positive Pin -> POWER Rail
Minus Pin -> GND Rail
O Pin -> NeoKey 2 - I Pin
C Pin -> GND Rail
** NeoKey 2 **
Positive Pin -> POWER Rail
Minus Pin -> GND Rail
C Pin -> GND Rail
Software Setup
Now you have set all that up plug it into your computer. I followed this guide Welcome to CircuitPython to setup CircuitPython. As I'm a webdev by day I'm alergic to development outside of vscode so instead of using the Mu IDE that the guide suggests I used the Circuit Python Extension in VSCode
Copying Libraries
I had an issue with dragging and dropping libraries in to the due to the limited storage space on the QT PY so I had to use cp -X or cp -RX in terminal on mac as the GUI Copy and Paste / move copies something called Extended Attributes (I don't know what they are either).
Guide
Add the libraries listed in code.py to the lib directory on the qtpy
Copy code.py to the root level of the qtpy make sure there is no other file like code copy.py or main.py ect.
Check the lights are correct (closest to the QTPY red furthest green)
Zoom Setup
Open zoom
Click zoom.us next to the apple logo then preferences or COMMAND + , on the keyboard
Click "Keyboard Shortcuts"
Find "Mute/Unmute My Audio"
Click the shortcut
Press the NeoKey you want to be assigned to microphone
Click the checkbox to Enable Global Shortcut
Find Start/Stop Video
Repeat steps 8,9,10 using the other neokey
(Note: I used COMMAND + CONTROL + SHIFT + A and COMMAND + CONTROL + SHIFT + S you can change these shortcuts in code.py look for "# Change these to what ever suits your use case" )
code.py
Code: Select all
"""
Two Key mute and video keyboard using NeoKey Sockets
Libraries:
adafruit_hid
adafruit_pypixelbuf.mpy
neopixel.mpy
"""
import sys
import time
import board
import neopixel
import usb_hid
import supervisor
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
from digitalio import DigitalInOut, Direction, Pull
# Keyboard setup
kbd = Keyboard(usb_hid.devices)
# board neopixel
qtpy_neopixel = neopixel.NeoPixel(board.NEOPIXEL, 1)
# The two neo key neopixels
video_mute_neopixel = neopixel.NeoPixel(board.A3, 2)
# Mute Switch Setup
mute_switch = DigitalInOut(board.A1)
mute_switch.direction = Direction.INPUT
mute_switch.pull = Pull.UP
mute_switch_status = False
mute_switch_awaiting_change = False
# Change these to what ever suits your use case
mute_keyboard_keys = [Keycode.CONTROL, Keycode.COMMAND, Keycode.SHIFT, Keycode.S]
# Video Switch Setup
video_switch = DigitalInOut(board.A2)
video_switch.direction = Direction.INPUT
video_switch.pull = Pull.UP
video_switch_status = True
video_switch_awaiting_change = False
# Change these to what ever suits your use case
video_keyboard_keys = [Keycode.CONTROL, Keycode.COMMAND, Keycode.SHIFT, Keycode.A]
# Global Variables
green = (0, 255, 0)
red = (255, 0, 0)
yellow = (255, 255, 0)
# Serial variables
microphone_on = "MICROPHONE_ON"
microphone_off = "MICROPHONE_OFF"
video_on = "VIDEO_ON"
video_off = "VIDEO_OFF"
# Green When connected
qtpy_neopixel.fill(green)
# node doesn't send the EOF character so have to loop through
def non_blocking_read():
i = ""
while supervisor.runtime.serial_bytes_available:
i += sys.stdin.read(1)
return i
while True:
if supervisor.runtime.serial_bytes_available:
value = non_blocking_read()
print(value)
if value == microphone_on:
mute_switch_status = False
elif value == microphone_off:
mute_switch_status = True
elif value == video_on:
video_switch_status = False
elif value == video_off:
video_switch_status = True
if not mute_switch.value and not mute_switch_awaiting_change:
mute_switch_awaiting_change = True
elif mute_switch.value and mute_switch_awaiting_change:
mute_switch_status = not mute_switch_status
mute_switch_awaiting_change = False
kbd.press(*mute_keyboard_keys)
time.sleep(.09)
kbd.release(*mute_keyboard_keys)
if not video_switch.value and not video_switch_awaiting_change:
video_switch_awaiting_change = True
elif video_switch.value and video_switch_awaiting_change:
video_switch_status = not video_switch_status
video_switch_awaiting_change = False
kbd.press(*video_keyboard_keys)
time.sleep(.09)
kbd.release(*video_keyboard_keys)
if not video_switch.value and video_switch_awaiting_change:
video_mute_neopixel[0] = yellow
elif video_switch_status:
video_mute_neopixel[0] = red
else:
video_mute_neopixel[0] = green
if not mute_switch.value and mute_switch_awaiting_change:
video_mute_neopixel[1] = yellow
elif mute_switch_status:
video_mute_neopixel[1] = red
else:
video_mute_neopixel[1] = green
video_mute_neopixel.show()
time.sleep(.01)