Text Editor
Adafruit recommends using the Mu editor for using your CircuitPython code with the Pico. You can get more info in this guide.
Alternatively, you can use any text editor that saves files.
# SPDX-FileCopyrightText: 2021 John Park for Adafruit Industries # SPDX-License-Identifier: MIT # RaspberryPi Pico RP2040 Mechanical Keyboard 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 print("---Pico Pad Keyboard---") led = DigitalInOut(board.LED) led.direction = Direction.OUTPUT led.value = True kbd = Keyboard(usb_hid.devices) cc = ConsumerControl(usb_hid.devices) # list of pins to use (skipping GP15 on Pico because it's funky) pins = ( board.GP0, board.GP1, board.GP2, board.GP3, board.GP4, board.GP5, board.GP6, board.GP7, board.GP8, board.GP9, board.GP10, board.GP11, board.GP12, board.GP13, board.GP14, board.GP16, board.GP17, board.GP18, board.GP19, board.GP20, board.GP21, ) MEDIA = 1 KEY = 2 keymap = { (0): (KEY, (Keycode.GUI, Keycode.C)), (1): (KEY, (Keycode.GUI, Keycode.V)), (2): (KEY, [Keycode.THREE]), (3): (KEY, [Keycode.FOUR]), (4): (KEY, [Keycode.FIVE]), (5): (MEDIA, ConsumerControlCode.VOLUME_DECREMENT), (6): (MEDIA, ConsumerControlCode.VOLUME_INCREMENT), (7): (KEY, [Keycode.R]), (8): (KEY, [Keycode.G]), (9): (KEY, [Keycode.B]), (10): (KEY, [Keycode.UP_ARROW]), (11): (KEY, [Keycode.X]), # plus key (12): (KEY, [Keycode.Y]), (13): (KEY, [Keycode.Z]), (14): (KEY, [Keycode.I]), (15): (KEY, [Keycode.O]), (16): (KEY, [Keycode.LEFT_ARROW]), (17): (KEY, [Keycode.DOWN_ARROW]), (18): (KEY, [Keycode.RIGHT_ARROW]), (19): (KEY, [Keycode.ALT]), (20): (KEY, [Keycode.U]), } switches = [] for i in range(len(pins)): switch = DigitalInOut(pins[i]) switch.direction = Direction.INPUT switch.pull = Pull.UP switches.append(switch) switch_state = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] while True: for button in range(21): if switch_state[button] == 0: if not switches[button].value: try: if keymap[button][0] == KEY: kbd.press(*keymap[button][1]) else: cc.send(keymap[button][1]) except ValueError: # deals w six key limit pass switch_state[button] = 1 if switch_state[button] == 1: if switches[button].value: try: if keymap[button][0] == KEY: kbd.release(*keymap[button][1]) except ValueError: pass switch_state[button] = 0 time.sleep(0.01) # debounce
Even before we've attached the mech keyswitches and PCB, you can test the code by shorting each of the GPIO pins used to ground. This is the same as pressing a key. Just be careful not to short power to ground!
Open the serial monitor for your board in Mu, or use a text editor to watch the keys get typed each time you short one of the GPIO pins.
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 the program, you can make calls to send keyboard keys and media keys.
USB Keyboards versus CircuitPython HID Keyboards
Certain keys you might want to emulate from your standard existing keyboard, such as multi-media (volume, play/pause, etc.) keys, are not actually regular keyboard keys. They are consumer control keys. All keys on a standard USB keyboard are not necessarily sending regular keycode values, but instead may be sending consumer control values on a separate consumer control device. In CircuitPython this is the difference between sending Keycode
values via a Keyboard
, and sending ConsumerControlCode
values via a ConsumerControl
. Therefore, if, in CircuitPython, you intend to send, for example, a "mute" command in your HID example, you should use ConsumerControl
instead of an equivalent keyboard key based on your standard existing keyboard.
Consumer control keys, such as multi-media keys, do not require "focus" to function. For example, sending a VOLUME_DECREMENT
consumer control command will decrease the volume regardless of which window currently has keyboard focus.
Keyboard Press/Release
Using the HID library in CircuitPtyhon, you 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)
Media Control
There is a second command to use 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, use this command:
cc.send(ConsumerControlCode.PLAY_PAUSE)
keymap = { (0): (KEY, (Keycode.GUI, Keycode.C)), (1): (KEY, (Keycode.GUI, Keycode.V)), (2): (KEY, [Keycode.THREE]), (3): (KEY, [Keycode.FOUR]), (4): (KEY, [Keycode.FIVE]), (5): (MEDIA, ConsumerControlCode.VOLUME_DECREMENT), (6): (MEDIA, ConsumerControlCode.VOLUME_INCREMENT), (7): (KEY, [Keycode.R]), (8): (KEY, [Keycode.G]), (9): (KEY, [Keycode.B]), (10): (KEY, [Keycode.UP_ARROW]), (11): (KEY, [Keycode.X]), # plus key (12): (KEY, [Keycode.Y]), (13): (KEY, [Keycode.Z]), (14): (KEY, [Keycode.I]), (15): (KEY, [Keycode.O]), (16): (KEY, [Keycode.LEFT_ARROW]), (17): (KEY, [Keycode.DOWN_ARROW]), (18): (KEY, [Keycode.RIGHT_ARROW]), (19): (KEY, [Keycode.ALT]), (20): (KEY, [Keycode.U]), }
Key Assignments
With that in mind, you can now fully customize the function of each key by editing this section of the code:
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:
(1): (KEY, (Keycode.GUI, Keycode.V)), (2): (KEY, [Keycode.THREE]),
Text editor powered by tinymce.