Skip to content

Commit cfbdf71

Browse files
committed
USB NKRO example for MacroPad
1 parent 2562c2c commit cfbdf71

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed

CircuitPython_MacroPad_NKRO/boot.py

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import usb_hid
2+
3+
BITMAP_KEYBOARD_DESCRIPTOR_REPORT_ID = 7
4+
REPORT_BYTES = 16
5+
bitmap_keyboard_descriptor = bytes((
6+
0x05, 0x01, # Usage Page (Generic Desktop),
7+
0x09, 0x06, # Usage (Keyboard),
8+
0xA1, 0x01, # Collection (Application),
9+
0x85, 0xFF, # 6,7 Report ID [SET AT RUNTIME]
10+
# bitmap of modifiers
11+
0x75, 0x01, # Report Size (1),
12+
0x95, 0x08, # Report Count (8),
13+
0x05, 0x07, # Usage Page (Key Codes),
14+
0x19, 0xE0, # Usage Minimum (224),
15+
0x29, 0xE7, # Usage Maximum (231),
16+
0x15, 0x00, # Logical Minimum (0),
17+
0x25, 0x01, # Logical Maximum (1),
18+
0x81, 0x02, # Input (Data, Variable, Absolute), ;Modifier byte
19+
# LED output report
20+
0x95, 0x05, # Report Count (5),
21+
0x75, 0x01, # Report Size (1),
22+
0x05, 0x08, # Usage Page (LEDs),
23+
0x19, 0x01, # Usage Minimum (1),
24+
0x29, 0x05, # Usage Maximum (5),
25+
0x91, 0x02, # Output (Data, Variable, Absolute),
26+
0x95, 0x01, # Report Count (1),
27+
0x75, 0x03, # Report Size (3),
28+
0x91, 0x03, # Output (Constant),
29+
# bitmap of keys
30+
0x95, (REPORT_BYTES-1)*8, # Report Count (),
31+
0x75, 0x01, # Report Size (1),
32+
0x15, 0x00, # Logical Minimum (0),
33+
0x25, 0x01, # Logical Maximum(1),
34+
0x05, 0x07, # Usage Page (Key Codes),
35+
0x19, 0x00, # Usage Minimum (0),
36+
0x29, (REPORT_BYTES-1)*8-1, # Usage Maximum (),
37+
0x81, 0x02, # Input (Data, Variable, Absolute),
38+
0xc0 # End Collection
39+
))
40+
41+
bitmap_keyboard = usb_hid.Device(
42+
report_descriptor = bitmap_keyboard_descriptor,
43+
usage_page = 0x1,
44+
usage = 0x6,
45+
in_report_length = 16,
46+
out_report_length = 1,
47+
report_id_index = BITMAP_KEYBOARD_DESCRIPTOR_REPORT_ID,
48+
)
49+
50+
print(bitmap_keyboard)
51+
devices = [
52+
bitmap_keyboard,
53+
usb_hid.Device.CONSUMER_CONTROL,
54+
usb_hid.Device.MOUSE,
55+
]
56+
usb_hid.enable(devices)
57+
print("enabled HID with custom keyboard device")

CircuitPython_MacroPad_NKRO/code.py

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import keypad
2+
import board
3+
import usb_hid
4+
from adafruit_hid.keyboard import Keyboard, find_device
5+
from adafruit_hid.keycode import Keycode
6+
7+
key_pins = (
8+
board.KEY1,
9+
board.KEY2,
10+
board.KEY3,
11+
board.KEY4,
12+
board.KEY5,
13+
board.KEY6,
14+
board.KEY7,
15+
board.KEY8,
16+
board.KEY9,
17+
board.KEY10,
18+
board.KEY11,
19+
board.KEY12,
20+
)
21+
22+
keys = keypad.Keys(key_pins, value_when_pressed=False, pull=True)
23+
24+
class BitmapKeyboard(Keyboard):
25+
def __init__(self, devices):
26+
device = find_device(devices, usage_page=0x1, usage=0x6)
27+
28+
try:
29+
device.send_report(b'\0' * 16)
30+
except ValueError:
31+
print("found keyboard, but it did not accept a 16-byte report. check that boot.py is installed properly")
32+
33+
self._keyboard_device = device
34+
35+
# report[0] modifiers
36+
# report[1:16] regular key presses bitmask
37+
self.report = bytearray(16)
38+
39+
self.report_modifier = memoryview(self.report)[0:1]
40+
self.report_bitmap = memoryview(self.report)[1:]
41+
42+
def _add_keycode_to_report(self, keycode):
43+
modifier = Keycode.modifier_bit(keycode)
44+
if modifier:
45+
# Set bit for this modifier.
46+
self.report_modifier[0] |= modifier
47+
else:
48+
self.report_bitmap[keycode >> 3] |= 1 << (keycode & 0x7)
49+
50+
def _remove_keycode_from_report(self, keycode):
51+
modifier = Keycode.modifier_bit(keycode)
52+
if modifier:
53+
# Set bit for this modifier.
54+
self.report_modifier[0] &= ~modifier
55+
else:
56+
self.report_bitmap[keycode >> 3] &= ~(1 << (keycode & 0x7))
57+
58+
def release_all(self):
59+
for i in range(len(self.report)):
60+
self.report[i] = 0
61+
self._keyboard_device.send_report(self.report)
62+
63+
kbd = BitmapKeyboard(usb_hid.devices)
64+
65+
keymap = [
66+
Keycode.ONE, Keycode.TWO, Keycode.THREE,
67+
Keycode.Q, Keycode.W, Keycode.E,
68+
Keycode.A, Keycode.S, Keycode.D,
69+
Keycode.Z, Keycode.X, Keycode.C]
70+
71+
while True:
72+
ev = keys.events.get()
73+
if ev is not None:
74+
key = keymap[ev.key_number]
75+
if ev.pressed:
76+
kbd.press(key)
77+
else:
78+
kbd.release(key)

0 commit comments

Comments
 (0)