Code up a simple, HID shortcut device to use during your Zoom calls.

The Neo Trinkey sticks out from your computer's USB port and gives you access to two capacitive touch pads. When you touch the top pad, you can mute or unmute your webcam and when you touch the bottom pad you can mute or unmute your microphone.

The four NeoPixels are a visual cue to let you know the state of your devices. They'll be red if an input is muted and green when they're unmuted.

For certain Zoom calls, the best part is leaving the call. You can leave quickly by pressing and holding down both touch pads for a little over a second to make a smooth exit. Additionally, the NeoPixels display the classic rainbow cycle animation to celebrate.

Parts

It's half USB Key, half Adafruit Trinket...it's Neo Trinkey, the circuit board with a Trinket M0 heart and  four RGB NeoPixels for customizable...
$6.95
In Stock

CircuitPython is a derivative of MicroPython designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the CIRCUITPY drive to iterate.

CircuitPython Quickstart

Follow this step-by-step to quickly get CircuitPython working on your board.

Click the link above and download the latest UF2 file.

Download and save it to your desktop (or wherever is handy).

Plug your Neo Trinkey directly into your computer's USB port, or via an adapter if needed.

Double-click the small reset button (indicated by the red arrow), and you will see the NeoPixel RGB LEDs turn green (indicated by the green box in the image). If they turn red, try another port, or if you're using an adapter or hub, try another adapter or hub.

If double-clicking doesn't work the first time, try again. Sometimes it can take a few tries to get the rhythm right!

You will see a new disk drive appear called TRINKEYBOOT.

 

 

 

Drag the adafruit_circuitpython_etc.uf2 file to TRINKEYBOOT.

The LEDs will flash red. Then, the TRINKEYBOOT drive will disappear and a new disk drive called CIRCUITPY will appear.

That's it, you're done! :)

As CircuitPython development continues and there are new releases, Adafruit will stop supporting older releases. Visit https://circuitpython.org/downloads to download the latest version of CircuitPython for your board. You must download the CircuitPython Library Bundle that matches your version of CircuitPython. Please update CircuitPython and then visit https://circuitpython.org/libraries to download the latest Library Bundle.

Each CircuitPython program you run needs to have a lot of information to work. The reason CircuitPython is so simple to use is that most of that information is stored in other files and works in the background. These files are called libraries. Some of them are built into CircuitPython. Others are stored on your CIRCUITPY drive in a folder called lib. Part of what makes CircuitPython so great is its ability to store code separately from the firmware itself. Storing code separately from the firmware makes it easier to update both the code you write and the libraries you depend.

Your board may ship with a lib folder already, it's in the base directory of the drive. If not, simply create the folder yourself. When you first install CircuitPython, an empty lib directory will be created for you.

CircuitPython libraries work in the same way as regular Python modules so the Python docs are an excellent reference for how it all should work. In Python terms, you can place our library files in the lib directory because it's part of the Python path by default.

One downside of this approach of separate libraries is that they are not built in. To use them, one needs to copy them to the CIRCUITPY drive before they can be used. Fortunately, there is a library bundle.

The bundle and the library releases on GitHub also feature optimized versions of the libraries with the .mpy file extension. These files take less space on the drive and have a smaller memory footprint as they are loaded.

Due to the regular updates and space constraints, Adafruit does not ship boards with the entire bundle. Therefore, you will need to load the libraries you need when you begin working with your board. You can find example code in the guides for your board that depends on external libraries.

Either way, as you start to explore CircuitPython, you'll want to know how to get libraries on board.

The Adafruit CircuitPython Library Bundle

Adafruit provides CircuitPython libraries for much of the hardware they provide, including sensors, breakouts and more. To eliminate the need for searching for each library individually, the libraries are available together in the Adafruit CircuitPython Library Bundle. The bundle contains all the files needed to use each library.

Downloading the Adafruit CircuitPython Library Bundle

You can download the latest Adafruit CircuitPython Library Bundle release by clicking the button below. The libraries are being constantly updated and improved, so you'll always want to download the latest bundle. 

Match up the bundle version with the version of CircuitPython you are running. For example, you would download the 6.x library bundle if you're running any version of CircuitPython 6, or the 7.x library bundle if you're running any version of CircuitPython 7, etc. If you mix libraries with major CircuitPython versions, you will get incompatible mpy errors due to changes in library interfaces possible during major version changes.

Download the bundle version that matches your CircuitPython firmware version. If you don't know the version, check the version info in boot_out.txt file on the CIRCUITPY drive, or the initial prompt in the CircuitPython REPL. For example, if you're running v7.0.0, download the 7.x library bundle.

There's also a py bundle which contains the uncompressed python files, you probably don't want that unless you are doing advanced work on libraries.

The CircuitPython Community Library Bundle

The CircuitPython Community Library Bundle is made up of libraries written and provided by members of the CircuitPython community. These libraries are often written when community members encountered hardware not supported in the Adafruit Bundle, or to support a personal project. The authors all chose to submit these libraries to the Community Bundle make them available to the community.

These libraries are maintained by their authors and are not supported by Adafruit. As you would with any library, if you run into problems, feel free to file an issue on the GitHub repo for the library. Bear in mind, though, that most of these libraries are supported by a single person and you should be patient about receiving a response. Remember, these folks are not paid by Adafruit, and are volunteering their personal time when possible to provide support.

Downloading the CircuitPython Community Library Bundle

You can download the latest CircuitPython Community Library Bundle release by clicking the button below. The libraries are being constantly updated and improved, so you'll always want to download the latest bundle.

The link takes you to the latest release of the CircuitPython Community Library Bundle on GitHub. There are multiple versions of the bundle available. Download the bundle version that matches your CircuitPython firmware version. If you don't know the version, check the version info in boot_out.txt file on the CIRCUITPY drive, or the initial prompt in the CircuitPython REPL. For example, if you're running v7.0.0, download the 7.x library bundle.

Understanding the Bundle

After downloading the zip, extract its contents. This is usually done by double clicking on the zip. On Mac OSX, it places the file in the same directory as the zip.

Open the bundle folder. Inside you'll find two information files, and two folders. One folder is the lib bundle, and the other folder is the examples bundle.

Now open the lib folder. When you open the folder, you'll see a large number of .mpy files, and folders.

Example Files

All example files from each library are now included in the bundles in an examples directory (as seen above), as well as an examples-only bundle. These are included for two main reasons:

  • Allow for quick testing of devices.
  • Provide an example base of code, that is easily built upon for individualized purposes.

Copying Libraries to Your Board

First open the lib folder on your CIRCUITPY drive. Then, open the lib folder you extracted from the downloaded zip. Inside you'll find a number of folders and .mpy files. Find the library you'd like to use, and copy it to the lib folder on CIRCUITPY.

If the library is a directory with multiple .mpy files in it, be sure to copy the entire folder to CIRCUITPY/lib.

This also applies to example files. Open the examples folder you extracted from the downloaded zip, and copy the applicable file to your CIRCUITPY drive. Then, rename it to code.py to run it.

If a library has multiple .mpy files contained in a folder, be sure to copy the entire folder to CIRCUITPY/lib.

Understanding Which Libraries to Install

You now know how to load libraries on to your CircuitPython-compatible microcontroller board. You may now be wondering, how do you know which libraries you need to install? Unfortunately, it's not always straightforward. Fortunately, there is an obvious place to start, and a relatively simple way to figure out the rest. First up: the best place to start.

When you look at most CircuitPython examples, you'll see they begin with one or more import statements. These typically look like the following:

  • import library_or_module

However, import statements can also sometimes look like the following:

  • from library_or_module import name
  • from library_or_module.subpackage import name
  • from library_or_module import name as local_name

They can also have more complicated formats, such as including a try / except block, etc.

The important thing to know is that an import statement will always include the name of the module or library that you're importing.

Therefore, the best place to start is by reading through the import statements.

Here is an example import list for you to work with in this section. There is no setup or other code shown here, as the purpose of this section involves only the import list.

import time
import board
import neopixel
import adafruit_lis3dh
import usb_hid
from adafruit_hid.consumer_control import ConsumerControl
from adafruit_hid.consumer_control_code import ConsumerControlCode

Keep in mind, not all imported items are libraries. Some of them are almost always built-in CircuitPython modules. How do you know the difference? Time to visit the REPL.

In the Interacting with the REPL section on The REPL page in this guide, the help("modules") command is discussed. This command provides a list of all of the built-in modules available in CircuitPython for your board. So, if you connect to the serial console on your board, and enter the REPL, you can run help("modules") to see what modules are available for your board. Then, as you read through the import statements, you can, for the purposes of figuring out which libraries to load, ignore the statement that import modules.

The following is the list of modules built into CircuitPython for the Feather RP2040. Your list may look similar or be anything down to a significant subset of this list for smaller boards.

Now that you know what you're looking for, it's time to read through the import statements. The first two, time and board, are on the modules list above, so they're built-in.

The next one, neopixel, is not on the module list. That means it's your first library! So, you would head over to the bundle zip you downloaded, and search for neopixel. There is a neopixel.mpy file in the bundle zip. Copy it over to the lib folder on your CIRCUITPY drive. The following one, adafruit_lis3dh, is also not on the module list. Follow the same process for adafruit_lis3dh, where you'll find adafruit_lis3dh.mpy, and copy that over.

The fifth one is usb_hid, and it is in the modules list, so it is built in. Often all of the built-in modules come first in the import list, but sometimes they don't! Don't assume that everything after the first library is also a library, and verify each import with the modules list to be sure. Otherwise, you'll search the bundle and come up empty!

The final two imports are not as clear. Remember, when import statements are formatted like this, the first thing after the from is the library name. In this case, the library name is adafruit_hid. A search of the bundle will find an adafruit_hid folder. When a library is a folder, you must copy the entire folder and its contents as it is in the bundle to the lib folder on your CIRCUITPY drive. In this case, you would copy the entire adafruit_hid folder to your CIRCUITPY/lib folder.

Notice that there are two imports that begin with adafruit_hid. Sometimes you will need to import more than one thing from the same library. Regardless of how many times you import the same library, you only need to load the library by copying over the adafruit_hid folder once.

That is how you can use your example code to figure out what libraries to load on your CircuitPython-compatible board!

There are cases, however, where libraries require other libraries internally. The internally required library is called a dependency. In the event of library dependencies, the easiest way to figure out what other libraries are required is to connect to the serial console and follow along with the ImportError printed there. The following is a very simple example of an ImportError, but the concept is the same for any missing library.

Example: ImportError Due to Missing Library

If you choose to load libraries as you need them, or you're starting fresh with an existing example, you may end up with code that tries to use a library you haven't yet loaded.  This section will demonstrate what happens when you try to utilise a library that you don't have loaded on your board, and cover the steps required to resolve the issue.

This demonstration will only return an error if you do not have the required library loaded into the lib folder on your CIRCUITPY drive.

Let's use a modified version of the Blink example.

import board
import time
import simpleio

led = simpleio.DigitalOut(board.LED)

while True:
    led.value = True
    time.sleep(0.5)
    led.value = False
    time.sleep(0.5)

Save this file. Nothing happens to your board. Let's check the serial console to see what's going on.

You have an ImportError. It says there is no module named 'simpleio'. That's the one you just included in your code!

Click the link above to download the correct bundle. Extract the lib folder from the downloaded bundle file. Scroll down to find simpleio.mpy. This is the library file you're looking for! Follow the steps above to load an individual library file.

The LED starts blinking again! Let's check the serial console.

No errors! Excellent. You've successfully resolved an ImportError!

If you run into this error in the future, follow along with the steps above and choose the library that matches the one you're missing.

Library Install on Non-Express Boards

If you have an M0 non-Express board such as Trinket M0, Gemma M0, QT Py M0, or one of the M0 Trinkeys, you'll want to follow the same steps in the example above to install libraries as you need them. Remember, you don't need to wait for an ImportError if you know what library you added to your code. Open the library bundle you downloaded, find the library you need, and drag it to the lib folder on your CIRCUITPY drive.

You can still end up running out of space on your M0 non-Express board even if you only load libraries as you need them. There are a number of steps you can use to try to resolve this issue. You'll find suggestions on the Troubleshooting page.

Updating CircuitPython Libraries and Examples

Libraries and examples are updated from time to time, and it's important to update the files you have on your CIRCUITPY drive.

To update a single library or example, follow the same steps above. When you drag the library file to your lib folder, it will ask if you want to replace it. Say yes. That's it!

A new library bundle is released every time there's an update to a library. Updates include things like bug fixes and new features. It's important to check in every so often to see if the libraries you're using have been updated.

Once you've finished setting up your Neo Trinkey with CircuitPython, you can access the code and necessary libraries by downloading the Project Bundle.

To do this, click on the Download Project Bundle button in the window below. It will download as a zipped folder.

import time
import board
import neopixel
import touchio
import usb_hid
from rainbowio import colorwheel
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.keycode import Keycode

#  setup for onboard neopixels
pixel_pin = board.NEOPIXEL
num_pixels = 4

pixels = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=0.05, auto_write=False)

#  setup for cap touch pads
top_touch = touchio.TouchIn(board.TOUCH1)
bot_touch = touchio.TouchIn(board.TOUCH2)

#  HID keyboard input setup
keyboard = Keyboard(usb_hid.devices)
keyboard_layout = KeyboardLayoutUS(keyboard)
#  variable for the ALT key
alt_key = Keycode.ALT


def rainbow_cycle(wait):
    for j in range(255):
        for i in range(num_pixels):
            rc_index = (i * 256 // num_pixels) + j
            pixels[i] = colorwheel(rc_index & 255)
        pixels.show()
        time.sleep(wait)

#  variables for colors
RED = (255, 0, 0)
GREEN = (0, 255, 0)

#  state machines
#  cap touch debouncing
bot_pressed = False
top_pressed = False
#  default mute states
mic_mute = True
vid_mute = True
#  time.monotonic() tracker
clock = time.monotonic()
#  tracking for initiating an exit from the meeting
escape = False
escape_1 = False
escape_2 = False

while True:
    #  cap touch debouncing
    if not top_touch.value and top_pressed:
        top_pressed = False
    if not bot_touch.value and bot_pressed:
        bot_pressed = False
    #  if your mic is muted...
    if mic_mute:
        #  neopixels are red
        pixels[0] = RED
        pixels[1] = RED
        pixels.show()
    #  if your camera is muted...
    if vid_mute:
        #  neopixels are red
        pixels[2] = RED
        pixels[3] = RED
        pixels.show()
    #  if your mic is NOT muted...
    if not mic_mute:
        #  neopixels are green
        pixels[0] = GREEN
        pixels[1] = GREEN
        pixels.show()
    #  if your camera is NOT muted...
    if not vid_mute:
        #  neopixels are green
        pixels[2] = GREEN
        pixels[3] = GREEN
        pixels.show()
    #  if you are leaving the meeting...
    if escape:
        #  neopixels are rainbow
        rainbow_cycle(0)
        #  resets exit states
        escape = False
        escape_1 = False
        escape_2 = False
        mic_mute = True
        vid_mute = True
    #  if you press the top touch cap touch pad...
    if (top_touch.value and not top_pressed):
        top_pressed = True
        #  start time count for exit
        clock = time.monotonic()
        #  slight delay so that you don't automatically mute/unmute
        #  if your intent is to exit
        time.sleep(0.12)
        #  if after the delay you're still pressing the cap touch pad...
        if top_touch.value and top_pressed:
            print("escape top")
            #  initial escape state is set to true
            escape_1 = True
        #  if you aren't still pressing the cap touch pad...
        else:
            #  if your camera was muted...
            if vid_mute:
                print("top")
                #  your camera is NOT muted
                vid_mute = False
                #  resets escape state just in case
                escape_1 = False
            #  if your camera was NOT muted...
            elif not vid_mute:
                print("top")
                #  your camera is muted
                vid_mute = True
                #  resets escape state just in case
                escape_1 = False
            #  sends camera mute/unmute shortcut
            keyboard.send(alt_key, Keycode.V)
    #  if you press the top touch cap touch pad...
    if (bot_touch.value and not bot_pressed):
        bot_pressed = True
        #  start time count for exit
        clock = time.monotonic()
        #  slight delay so that you don't automatically mute/unmute
        #  if your intent is to exit
        time.sleep(0.12)
        #  if after the delay you're still pressing the cap touch pad...
        if bot_touch.value and bot_pressed:
            print("escape bot")
            #  initial escape state is set to true
            escape_2 = True
        #  if you aren't still pressing the cap touch pad...
        else:
            #  if your mic was muted...
            if mic_mute:
                print("bot")
                #  your mic is NOT muted
                mic_mute = False
                #  resets escape state just in case
                escape_2 = False
            #  if your mic was NOT muted...
            elif not mic_mute:
                print("bot")
                #  your mic is muted
                mic_mute = True
                #  resets escape state just in case
                escape_2 = False
            #  sends mic mute/unmute shortcut
            keyboard.send(alt_key, Keycode.A)
    #  if you held down both cap touch pads and 2 seconds has passed...
    if ((clock + 2) < time.monotonic()) and (escape_1 and escape_2):
        print("escape")
        #  full escape state is set
        escape = True
        #  sends exit meeting shortcut
        keyboard.send(alt_key, Keycode.Q)
        #  brief delay for confirmation window to open
        time.sleep(0.1)
        #  sends enter to confirm meeting exit
        keyboard.send(Keycode.ENTER)

Upload the Code and Libraries to the Neo Trinkey

After downloading the Project Bundle, plug your board into the computer USB port. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called CIRCUITPY. Unzip the folder and copy the code.py and lib folder to the Neo Trinkey's CIRCUITPY drive.

Your Neo Trinkey CIRCUITPY drive should look like this after copying the contents of the Project Bundle.

Import the Libraries

First, the libraries are imported.

import time
import board
import neopixel
import touchio
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.keycode import Keycode

Setup the NeoPixels

Then, the NeoPixels are setup. The four onboard NeoPixels can be accessed with board.NEOPIXEL.

#  setup for onboard neopixels
pixel_pin = board.NEOPIXEL
num_pixels = 4

pixels = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=0.05, auto_write=False)

Setup the Capacitive Touch Inputs

The two capacitive touch pads are accessed with board.TOUCH1 and board.TOUCH2. They're setup as touch inputs with the touchio library.

#  setup for cap touch pads
top_touch = touchio.TouchIn(board.TOUCH1)
bot_touch = touchio.TouchIn(board.TOUCH2)

Setup for HID

keyboard is setup as the USB HID keyboard object. Zoom uses the ALT key for a lot of its built-in shortcuts, so alt_key is setup as a variable to hold Keycode.ALT.

#  HID keyboard input setup
keyboard = Keyboard(usb_hid.devices)
keyboard_layout = KeyboardLayoutUS(keyboard)
#  variable for the ALT key
alt_key = Keycode.ALT

NeoPixel Animation and Colors

The classic rainbow_cycle NeoPixel animation is included, followed by RED and GREEN RGB color values that will be used to show if your inputs are muted or unmuted.

#  rainbow cycle animation
def wheel(pos):
    # Input a value 0 to 255 to get a color value.
    # The colours are a transition r - g - b - back to r.
    if pos < 0 or pos > 255:
        return (0, 0, 0)
    if pos < 85:
        return (255 - pos * 3, pos * 3, 0)
    if pos < 170:
        pos -= 85
        return (0, 255 - pos * 3, pos * 3)
    pos -= 170
    return (pos * 3, 0, 255 - pos * 3)


def rainbow_cycle(wait):
    for j in range(255):
        for i in range(num_pixels):
            rc_index = (i * 256 // num_pixels) + j
            pixels[i] = wheel(rc_index & 255)
        pixels.show()
        time.sleep(wait)
        
#  variables for colors
RED = (255, 0, 0)
GREEN = (0, 255, 0)

State Machines

A few state machines are used in the loop and their functions are commented in the code.

#  state machines
#  cap touch debouncing
bot_pressed = False
top_pressed = False
#  default mute states
mic_mute = True
vid_mute = True
#  time.monotonic() tracker
clock = time.monotonic()
#  tracking for initiating an exit from the meeting
escape = False
escape_1 = False
escape_2 = False

The Loop and Touch Input Debouncing

The loop begins with some debouncing for the cap touch inputs.

while True:
    #  cap touch debouncing
    if not top_touch.value and top_pressed:
        top_pressed = False
    if not bot_touch.value and bot_pressed:
        bot_pressed = False

Setting the Mute, Unmute and Exit Colors

The mic_mute and vid_mute states affect whether the two corresponding NeoPixels are green or red.

#  if your mic is muted...
    if mic_mute:
        #  neopixels are red
        pixels[0] = RED
        pixels[1] = RED
        pixels.show()
    #  if your camera is muted...
    if vid_mute:
        #  neopixels are red
        pixels[2] = RED
        pixels[3] = RED
        pixels.show()
    #  if your mic is NOT muted...
    if not mic_mute:
        #  neopixels are green
        pixels[0] = GREEN
        pixels[1] = GREEN
        pixels.show()
    #  if your camera is NOT muted...
    if not vid_mute:
        #  neopixels are green
        pixels[2] = GREEN
        pixels[3] = GREEN
        pixels.show()

The escape state cues the start of the rainbow_cycle animation when you exit a meeting. It also resets the escape_1, escape_2, mic_mute and vid_mute states to their defaults.

#  if you are leaving the meeting...
    if escape:
        #  neopixels are rainbow
        rainbow_cycle(0)
        #  resets exit states
        escape = False
        escape_1 = False
        escape_2 = False
        mic_mute = True
        vid_mute = True

What Happens When You Press the Top Pad?

When you press the top touch pad, time.monotonic() is called to begin a time count. This is followed by a delay of 0.12 seconds. This tracks if you are holding the touch input for an exit. Otherwise, the code would go right to muting or unmuting your webcam.

#  if you press the top touch cap touch pad...
    if (top_touch.value and not top_pressed):
        top_pressed = True
        #  start time count for exit
        clock = time.monotonic()
        #  slight delay so that you don't automatically mute/unmute
        #  if your intent is to exit
        time.sleep(0.12)

If you are inputting a long press to trigger an exit from the meeting, the escape_1 state is set to True, which will begin initiating the exit. This also keeps your webcam's mute status unchanged so that you don't suddenly mute or unmute while you're leaving the meeting.

#  if after the delay you're still pressing the cap touch pad...
        if top_touch.value and top_pressed:
            print("escape top")
            #  initial escape state is set to true
            escape_1 = True

If you aren't inputting a long press, then the vid_mute status will change depending on the previous state and the shortcut for muting/unmuting the video (ALT+V) is sent. 

#  if you aren't still pressing the cap touch pad...
        else:
            #  if your camera was muted...
            if vid_mute:
                print("top")
                #  your camera is NOT muted
                vid_mute = False
                #  resets escape state just in case
                escape_1 = False
            #  if your camera was NOT muted...
            elif not vid_mute:
                print("top")
                #  your camera is muted
                vid_mute = True
                #  resets escape state just in case
                escape_1 = False
            #  sends camera mute/unmute shortcut
            keyboard.send(alt_key, Keycode.V)

The same logic is in place for the bottom capacitive touch input, which controls the microphone.

#  if you press the top touch cap touch pad...
    if (bot_touch.value and not bot_pressed):
        bot_pressed = True
        #  start time count for exit
        clock = time.monotonic()
        #  slight delay so that you don't automatically mute/unmute
        #  if your intent is to exit
        time.sleep(0.12)
        #  if after the delay you're still pressing the cap touch pad...
        if bot_touch.value and bot_pressed:
            print("escape bot")
            #  initial escape state is set to true
            escape_2 = True
        #  if you aren't still pressing the cap touch pad...
        else:
            #  if your mic was muted...
            if mic_mute:
                print("bot")
                #  your mic is NOT muted
                mic_mute = False
                #  resets escape state just in case
                escape_2 = False
            #  if your mic was NOT muted...
            elif not mic_mute:
                print("bot")
                #  your mic is muted
                mic_mute = True
                #  resets escape state just in case
                escape_2 = False
            #  sends mic mute/unmute shortcut
            keyboard.send(alt_key, Keycode.A)

The Great Escape

If you hold down both capacitive touch pads at the same time, escape is set to True after 2 seconds. This delay ensures no accidental exits from the meeting.

Then, the shortcut for exiting a meeting (ALT+Q) is sent, followed by a slight delay before sending the Enter key. Zoom puts up a confirmation window before you leave or end a meeting, so by sending Enter it fully automates your exit.

#  if you held down both cap touch pads and 2 seconds has passed...
    if ((clock + 2) < time.monotonic()) and (escape_1 and escape_2):
        print("escape")
        #  full escape state is set
        escape = True
        #  sends exit meeting shortcut
        keyboard.send(alt_key, Keycode.Q)
        #  brief delay for confirmation window to open
        time.sleep(0.1)
        #  sends enter to confirm meeting exit
        keyboard.send(Keycode.ENTER)

Before using the Neo Trinkey for Zoom shortcuts, its recommended to setup your default video and microphone mute settings in Zoom so that they match the code. 

When you're in a meeting, you can tap the top touch pad to mute or unmute your camera. The two NeoPixels directly next to the pad will light up either green or red depending on the state of the camera.

You can tap the bottom touch pad to mute or unmute your microphone. The bottom two NeoPixels will light up either green or red to show you if you're muted or unmuted.

No more having to hear that you're muted by your fellow Zoom meeting participants and no more delay in unmuting by fiddling with your mouse or keyboard.

To exit a meeting, press down on both touch pads at the same time and hold for about one second. Two seconds later, the keyboard shortcut to exit will be sent and you can have a mini celebration with the rainbow cycle animation on the NeoPixels.

If you use a different video conferencing software, you can change the keyboard shortcuts in the code to match. You can also change which touch pad is controlling the mic or webcam. Hopefully this project makes interacting with your virtual meetings easier and fun.

 

This guide was first published on Apr 27, 2021. It was last updated on 2021-04-27 20:06:04 -0400.