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()
Text editor powered by tinymce.