Once your ItsyBitsy is set up with CircuitPython 5.3.0 or greater, you'll also need to add some libraries. Follow this page for information on how to download and add libraries to your ItsyBitsy.
From the library bundle you downloaded in that guide page, transfer the following libraries onto the ItsyBity /lib directory on the CIRCUITPY drive:
- adafruit_bus_device
- adafruit_dotstar
- adafruit_hid
- adafruit_led_animation
- neopixel
- simpleio
Text Editor
Adafruit recommends using the Mu editor for using your CircuitPython code with the ItsyBitsy boards. You can get more info in this guide.
Alternatively, you can use any text editor that saves files.
# SPDX-FileCopyrightText: 2020 John Park for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# ItsyBitsy Keypad
# Uses ItsyBitsy M4/M0 plus Pimoroni Keybow
# To build a customizable USB keypad
import time
import board
from digitalio import DigitalInOut, Direction, Pull
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
from adafruit_hid.consumer_control import ConsumerControl
from adafruit_hid.consumer_control_code import ConsumerControlCode
import adafruit_dotstar as dotstar
dots = dotstar.DotStar(board.SCK, board.MOSI, 12, brightness=0.4)
RED = 0xFF0000
AMBER = 0xAA9900
BLUE = 0x0066FF
MAGENTA = 0xFF00FF
PURPLE = 0x3B0F85
BLACK = 0x000000
kbd = Keyboard(usb_hid.devices)
cc = ConsumerControl(usb_hid.devices)
orientation = 1 # 0 = portrait/vertical, 1 = landscape/horizontal
if orientation == 0:
key_dots = [0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11]
# 0 #4 #8
# 1 #5 #9
# 2 #6 #10
# 3 #7 #11
if orientation == 1:
key_dots = [3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8]
# 3 #2 #1 #0
# 7 #6 #5 #4
# 11 #10 #9 #8
def dot_on(dot, color):
dots[dot] = color
def dot_off(dot):
dots[dot] = BLACK
# Pin definitions
if orientation == 0: # 0 = portrait/vertical
pins = [
board.D11,
board.D12,
board.D2,
board.D10,
board.D9,
board.D7,
board.A5,
board.A4,
board.A3,
board.A2,
board.A1,
board.A0,
]
if orientation == 1: # 1 = landscape/horizontal
pins = [
board.A2,
board.A5,
board.D10,
board.D11,
board.A1,
board.A4,
board.D9,
board.D12,
board.A0,
board.A3,
board.D7,
board.D2,
]
# the two command types -- MEDIA for ConsumerControlCodes, KEY for Keycodes
# this allows button press to send the correct HID command for the type specified
MEDIA = 1
KEY = 2
keymap = {
(0): (AMBER, MEDIA, ConsumerControlCode.PLAY_PAUSE),
(1): (AMBER, MEDIA, ConsumerControlCode.MUTE),
(2): (AMBER, MEDIA, ConsumerControlCode.VOLUME_DECREMENT),
(3): (AMBER, MEDIA, ConsumerControlCode.VOLUME_INCREMENT),
(4): (BLUE, KEY, (Keycode.GUI, Keycode.C)),
(5): (BLUE, KEY, (Keycode.GUI, Keycode.V)),
(6): (MAGENTA, KEY, [Keycode.UP_ARROW]),
(7): (PURPLE, KEY, [Keycode.BACKSPACE]),
(8): (BLUE, KEY, [Keycode.SPACE]),
(9): (MAGENTA, KEY, [Keycode.LEFT_ARROW]),
(10): (MAGENTA, KEY, [Keycode.DOWN_ARROW]),
(11): (MAGENTA, KEY, [Keycode.RIGHT_ARROW]),
}
switches = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
for i in range(12):
switches[i] = DigitalInOut(pins[i])
switches[i].direction = Direction.INPUT
switches[i].pull = Pull.UP
switch_state = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
print("ItsyBitsy Keybow")
# Starup lights
for k in range(12):
dot_on(key_dots[k], RED)
time.sleep(0.05)
dot_on(key_dots[k], keymap[k][0]) # use individual key colors from set
time.sleep(0.05)
while True:
for button in range(12):
if switch_state[button] == 0:
if not switches[button].value:
try:
if keymap[button][1] == KEY:
kbd.press(*keymap[button][2])
else:
cc.send(keymap[button][2])
dot_on(key_dots[button], RED)
except ValueError: # deals w six key limit
pass
print("pressed key{}".format(button))
switch_state[button] = 1
if switch_state[button] == 1:
if switches[button].value:
try:
if keymap[button][1] == KEY:
kbd.release(*keymap[button][2])
dot_on(key_dots[button], keymap[button][0])
except ValueError:
pass
print("released key{}".format(button))
switch_state[button] = 0
time.sleep(0.01) # debounce
With the code.py loaded onto your ItsyBitsy M4/M0, you'll see the Keybow DotStar LEDs light up with a startup animation, and then they'll settle into their color coded default stated.
Try pressing the four amber top row keys -- they control media play/pause, mute, and volume up/down.
To test the dark blue copy/paste keys, first select some text and hit the left copy key. Then, place your cursor in a text field and press the right paste key.
The magenta keys are up/down/left/right arrows for navigation.
Be careful with the purple key, it's the delete key!
And the cyan key is the spacebar.
There are nearly limitless customization you can do to make your ItsyBitsy Keybow work the way you want! Here are a few to try:
- color
- key assignments
- orientation
- animation
We can use the Keybow in a vertical or horizontal orientation by re-assigning the relationship in code between the physical pins and their logical representations. This means that we can rotate the board by only changing a single variable and the key colors and assignments will update automatically.
So, for example, if we use this assignment in vertical orientation:
a b c
d e f
g h 1
2 3 4
It will become this when set to horizontal orientation:
a b c d
e f g h
1 2 3 4
This is accomplished by setting the orientation variable to 0 or 1, which then chooses which pins list to use. These set the logical order from 0-11 to use the physical pins on the ItsyBitsy that are wired via the ProtoBonnet to the Keybow.
orientation = 0 # 0 = portrait/vertical, 1 = landscape/horizontal
# Pin definitions
if orientation == 0: # 0 = portrait/vertical
pins = [
board.D11,
board.D12,
board.D2,
board.D10,
board.D9,
board.D7,
board.A5,
board.A4,
board.A3,
board.A2,
board.A1,
board.A0,
]
if orientation == 1: # 1 = landscape/horizontal
pins = [
board.A2,
board.A5,
board.D10,
board.D11,
board.A1,
board.A4,
board.D9,
board.D12,
board.A0,
board.A3,
board.D7,
board.D2,
]
Here's an example of the vertical orientation and some alternate keycaps.
The orientation variable also is used to remap the DotStar physical order to the appropriate logical order.
The physical order of the DotStar LEDs can be seen in the diagram to the left.
So, to use these with our keymap ordering, the code below is used, with the if orientation == 1: list:
if orientation == 0:
key_dots = [0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11]
# 0 #4 #8
# 1 #5 #9
# 2 #6 #10
# 3 #7 #11
if orientation == 1:
key_dots = [3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8]
# 3 #2 #1 #0
# 7 #6 #5 #4
# 11 #10 #9 #8
HID Keyboard Basics
This guide page has a great intro to CircuitPython HID Keyboard.
For even more details, check out the documentation at https://circuitpython.readthedocs.io/projects/hid/en/latest/ which includes all of the keycodes and media codes you can use.
By importing the adafruit_hid library into our program we can make calls to send keyboard keys and media keys.
Keyboard Press/Release
Using the HID library in CircuitPtyhon, we can send this command to "type" the letter 'a':
kbd.press(Keycode.A)
kbd.release(Keycode.A)
This would send a lowercase 'a' to the computer just as if you had typed it yourself. To send a capital 'A', we'd add the shift key to the command like this:
kbd.press(Keycode.SHIFT, Keycode.A)
kbd.release(Keycode.SHIFT, Keycode.A)
This is pretty cool, since it means we can layer on lots of keys all at the same time, just like you do on your physical keyboard when using keyboard shortcuts!
So, if there's some keyboard shortcut you want to use (or create for yourself in something like Quicksilver or AutoKeys) that is command+option+ctrl+a the CircuitPython code would look like this:
kbd.press(Keycode.GUI, Keycode.ALT, Keycode.CONTROL, Keycode.A)
kbd.release(Keycode.GUI, Keycode.ALT, Keycode.CONTROL, Keycode.A)
There is a second command we'll use when we want to adjust volume, play/pause, skip tracks, and so on with media such as songs and videos. These are often represented on a physical keyboard as icons silkscreened onto the rightmost function keys.
In USB HID speak, these are known as "Consumer Control codes". To play or pause a track we'll use this command:
cc.send(ConsumerControlCode.PLAY_PAUSE)
Key Assignements
With that in mind, you can now fully customize the function of each key by editing this section of the code:
keymap = {
(0): (AMBER, MEDIA, ConsumerControlCode.PLAY_PAUSE),
(1): (AMBER, MEDIA, ConsumerControlCode.MUTE),
(2): (AMBER, MEDIA, ConsumerControlCode.VOLUME_DECREMENT),
(3): (AMBER, MEDIA, ConsumerControlCode.VOLUME_INCREMENT),
(4): (BLUE, KEY, (Keycode.GUI, Keycode.C)),
(5): (BLUE, KEY, (Keycode.GUI, Keycode.V)),
(6): (MAGENTA, KEY, [Keycode.UP_ARROW]),
(7): (PURPLE, KEY, [Keycode.BACKSPACE]),
(8): (BLUE, KEY, [Keycode.SPACE]),
(9): (MAGENTA, KEY, [Keycode.LEFT_ARROW]),
(10): (MAGENTA, KEY, [Keycode.DOWN_ARROW]),
(11): (MAGENTA, KEY, [Keycode.RIGHT_ARROW]),
}
Note, in order to use a single stroke keycode, you'll surround it in [brackets], while a multi-stroke keycode will have its own (parentheses) as shown here:
(4): (BLUE, KEY, [Keycode.C]), (5): (BLUE, KEY, (Keycode.SHIFT, Keycode.C)),
Since we can use the orientation variable as shown above, the keymap values pertain to the keys starting at the origin key in the upper left corner of the board and moving left to right, top to bottom toward the last key in the lower right corner in either case.
Colors
On easy way to customize colors is to simply change the keymap color assignments, or, you can create your own colors to augment this list:
RED = 0xFF0000 AMBER = 0xAA9900 BLUE = 0x0066FF MAGENTA = 0xFF00FF PURPLE = 0x3B0F85 BLACK = 0x000000
Animation
In this code we're doing a simple animation at startup by calling the dot_on() function with different color settings.
However, if you want to get HIGHLY FANCY, -- and who doesn't?! -- you can use the adafruit_led_animation library to create beautiful effects. Here's a great guide for getting started with CircuitPython LED Animation
Here's an example of some adafruit_led_animation library sequences running on the ItstyBitsy Keybow's DotStars. (Note, this is an animation example only and doesn't have key functions.)
import board
from adafruit_led_animation.sequence import AnimationSequence
from adafruit_led_animation.animation.comet import Comet
from adafruit_led_animation.animation.chase import Chase
from adafruit_led_animation.animation.blink import Blink
from adafruit_led_animation.helper import PixelMap
from adafruit_led_animation.color import RED, BLUE, GREEN
import adafruit_dotstar as dotstar
dots = dotstar.DotStar(board.SCK, board.MOSI, 12, brightness=0.85, auto_write=False)
pixel_grid = PixelMap(dots, [
[3], [2], [1], [0],
[7], [6], [5], [4],
[11], [10], [9], [8],
], individual_pixels=True)
blink = Blink(pixel_grid, speed=0.5, color=BLUE)
comet = Comet(pixel_grid, speed=0.03, color=RED, tail_length=12)
chase = Chase(pixel_grid, speed=0.1, color=GREEN, size=1, spacing=3)
animations = AnimationSequence(chase, comet, blink, advance_interval=2, auto_clear=True)
while True:
animations.animate()
Page last edited January 21, 2025
Text editor powered by tinymce.