This commit is contained in:
2022-01-02 18:23:10 +01:00
parent 753608d0d0
commit 1f8f7e2e56
18 changed files with 158 additions and 65 deletions

3
.vscode/settings.json vendored Normal file → Executable file
View File

@@ -8,6 +8,5 @@
"",
"/home/wholteza/.vscode/extensions/joedevivo.vscode-circuitpython-0.1.15/stubs",
"/home/wholteza/.config/Code/User/globalStorage/joedevivo.vscode-circuitpython/bundle/20211231/adafruit-circuitpython-bundle-py-20211231/lib"
],
"circuitpython.board.version": "7.0.0"
]
}

View File

@@ -1,44 +1,56 @@
import usb_hid
from micropython import const
__BYTE_ARRRAY_LENGTH = const(8)
__MAX_KEYPRESSES = const(6)
class Keyboard():
keyboard_device: usb_hid.Device
from adafruit_hid.keyboard import Keyboard, find_device
from adafruit_hid.keycode import Keycode
import usb_hid
class NkroKeyboard():
report: bytearray
def __init__(self):
self.keyboard_device = usb_hid.Device.KEYBOARD
self.report = bytearray(__BYTE_ARRRAY_LENGTH)
self.report_modifier = memoryview(self.report)[0:__BYTE_ARRRAY_LENGTH-__MAX_KEYPRESSES-1]
self.report_keys = memoryview(self.report)[__BYTE_ARRRAY_LENGTH-__MAX_KEYPRESSES:]
report_modifier: memoryview
report_bitmap: memoryview
def __init__(self, devices):
device = find_device(devices, usage_page=0x1, usage=0x6)
try:
device.send_report(b'\0' * 16)
except ValueError:
print("found keyboard, but it did not accept a 16-byte report. check that boot.py is installed properly")
def press(self, *keycodes: int) -> None:
for keycode in keycodes:
self.add_key_to_report(keycode)
self.keyboard_device.send_report(self.report)
def add_key_to_report(self, keycode: int) -> None:
for i in range(__MAX_KEYPRESSES):
if self.report_keys[i] == keycode:
return
for i in range(__MAX_KEYPRESSES):
if self.report_keys[i] == 0:
self.report_keys[i] = keycode
return
raise ValueError(
"bytearray full!"
)
def release(self, *keycodes: int) -> None:
for keycode in keycodes:
self.remove_key_from_report(keycode)
self.keyboard_device.send_report(self.report)
def remove_key_from_report(self, keycode: int) -> None:
for i in range(__MAX_KEYPRESSES):
if self.report_keys[i] == keycode:
self.report_keys[i] = 0
self._keyboard_device = device
def release_all(self) -> None:
"""Release all"""
for i in range(__BYTE_ARRRAY_LENGTH):
self.report = bytearray(16)
self.report_modifier = memoryview(self.report)[0:1]
self.report_bitmap = memoryview(self.report)[1:]
def _add_keycode_to_report(self, keycode):
modifier = Keycode.modifier_bit(keycode)
if modifier:
self.report_modifier[0] |= modifier
else:
self.report_bitmap[keycode >> 3] |= 1 << (keycode & 0x7)
def _remove_keycode_from_report(self, keycode):
modifier = Keycode.modifier_bit(keycode)
if modifier:
self.report_modifier[0] &= ~modifier
else:
self.report_bitmap[keycode >> 3] &= ~(1 << (keycode & 0x7))
def release_all(self):
for i in range(len(self.report)):
self.report[i] = 0
self.keyboard_device.send_report(self.report)
self._keyboard_device.send_report(self.report)
def send_nkro_report(self, pressed_keys):
"""Sends the USB HID NKRO keyboard report."""
report = bytearray(16)
report_mod_keys = memoryview(report)[0:1]
report_bitmap = memoryview(report)[1:]
for code in pressed_keys:
if code == 0:
continue
if code & 0xff00:
report_mod_keys[0] |= (code & 0xff00) >> 8
if code & 0x00ff:
report_bitmap[code >> 3] |= 1 << (code & 0x7)
self._keyboard_device.send_report(report)

91
KeyboardStateManager.py Executable file → Normal file
View File

@@ -1,13 +1,11 @@
from Keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.keycode import Keycode
from adafruit_rgbled import RGBLED
import digitalio
from adafruit_mcp230xx.mcp23017 import MCP23017
import digitalio
import busio
import board
import usb_hid
import time
PRESSED = False
UNPRESSED = True
@@ -23,23 +21,55 @@ PINOUT = [
class KeyboardStateManager:
active_layer: int
last_pressed: set[Keycode]
pressed: set[Keycode]
state: list[list[int]]
keymap: list[list[Keycode]]
pinout: list[list[int]]
pins: list[list[digitalio.DigitalInOut]]
keyboard: Keyboard
keyboard_device: usb_hid.Device
mcp: MCP23017
led: RGBLED
def __init__(self, keymap: list[list[Keycode]]):
for device in usb_hid.devices:
if device.usage == 0x06 and device.usage_page == 0x01:
self.keyboard_device = device
try:
device.send_report(b'\0' * 16)
except ValueError:
print("found keyboard, but it did not accept a 16-byte report. check that boot.py is installed properly")
if device.usage == 0x01 and device.usage_page == 0x0c:
self.media_device = device
if not self.keyboard_device:
raise RuntimeError("no HID keyboard device")
self.report = bytearray(16)
self.report_modifier = memoryview(self.report)[0:1]
self.report_bitmap = memoryview(self.report)[1:]
self.pressed = set()
self.last_pressed = set()
self.keymap = keymap
self.keyboard = Keyboard()
self.initialize_io_extender()
self.initialize_pins(PINOUT)
self.initialize_led()
print("done")
def initialize_led(self) -> None:
self.led = RGBLED(board.LED_R, board.LED_G, board.LED_B, True)
self.set_led((255,255,255))
time.sleep(0.05)
self.set_led((0,0,0))
time.sleep(0.05)
self.set_led((255,255,255))
time.sleep(0.05)
self.set_led((0,0,0))
time.sleep(0.05)
self.set_led((255,255,255))
time.sleep(0.05)
self.set_led((0,0,0))
self.reset_led()
def initialize_io_extender(self) -> None:
@@ -61,23 +91,31 @@ class KeyboardStateManager:
self.pins.append(pin_row)
self.state.append(previous_state_row)
def loop(self):
def start(self):
for row in self.pins:
for pin in row:
row_index, pin_index = self.pins.index(row), row.index(pin)
keycode = self.keymap[row_index][pin_index]
previously = self.state[row_index][pin_index]
key = pin.value == UNPRESSED
value = pin.value
currentlyPressed = value is PRESSED
previouslyPressed = previously is PRESSED
if key is PRESSED:
if previously is UNPRESSED:
self.keyboard.press(keycode)
self.set_led((1, 0, 0))
if key is UNPRESSED:
if previously is PRESSED:
self.keyboard.release(keycode)
self.reset_led()
self.state[row_index][pin_index] = key
if currentlyPressed:
if not previouslyPressed:
self.pressed.add(keycode)
else:
if previouslyPressed:
try:
self.pressed.remove(keycode)
# Catch silenly if same keycode is pressed twice then released
except KeyError:
pass
self.state[row_index][pin_index] = value
if self.pressed != self.last_pressed:
self.send_nkro_report()
self.last_pressed = set(self.pressed)
def set_led(self, value: tuple[int, int, int]):
self.led.color = value
@@ -85,6 +123,21 @@ class KeyboardStateManager:
def reset_led(self):
self.led.color = (0, 255, 0)
def send_nkro_report(self):
"""Sends the USB HID NKRO keyboard report."""
report = bytearray(16)
report_mod_keys = memoryview(report)[0:1]
report_bitmap = memoryview(report)[1:]
for code in self.pressed:
if code == 0:
continue
if code & 0xff00:
report_mod_keys[0] |= (code & 0xff00) >> 8
if code & 0x00ff:
report_bitmap[code >> 3] |= 1 << (code & 0x7)
self.keyboard_device.send_report(report)
# Create interface for key of different type
# interface has method to handle keypress and release and gets access to key keyboards state and keyboard instance
# keyboard state should be increased with layer
@@ -107,7 +160,7 @@ class KeyboardStateManager:
# type: LayerKeyType
# def __init__(self, type: LayerKeyType, layer: int):
# class KeycodeKey:
# def __init__(self):
# pass
#

5
KeypadPin.py Executable file
View File

@@ -0,0 +1,5 @@
import microcontroller
class KeypadPin(microcontroller.Pin):
def __init__(self) -> None:
super().__init__()

20
boot.py Normal file
View File

@@ -0,0 +1,20 @@
import usb_hid
print("setting up keyboard")
bitmap_keyboard = usb_hid.Device(
report_descriptor = (
b'\x05\x01\t\x06\xa1\x01\x85\x04u\x01\x95\x08\x05\x07\x19\xe0)\xe7\x15\x00%\x01\x81\x02\x95\x05u\x01\x05\x08\x19\x01)\x05\x91\x02\x95\x01u\x03\x91\x03\x95xu\x01\x15\x00%\x01\x05\x07\x19\x00)w\x81\x02\xc0'),
report_ids = (4,),
in_report_lengths = (16,),
out_report_lengths = (1,),
usage_page = 0x1,
usage = 0x6,
)
print(bitmap_keyboard)
devices = [
bitmap_keyboard,
usb_hid.Device.CONSUMER_CONTROL,
usb_hid.Device.MOUSE,
]
usb_hid.enable(devices)
print("enabled HID with custom keyboard device")

6
boot_out.txt Executable file → Normal file
View File

@@ -1,2 +1,6 @@
Adafruit CircuitPython 7.0.0 on 2021-09-20; Pimoroni Tiny 2040 with rp2040
Adafruit CircuitPython 7.2.0-alpha.1 on 2021-12-29; Pimoroni Tiny 2040 (8MB) with rp2040
Board ID:pimoroni_tiny2040
boot.py output:
setting up keyboard
<Device>
enabled HID with custom keyboard device

View File

@@ -4,18 +4,18 @@ from KeyboardStateManager import KeyboardStateManager
keymap_rows = [
[Keycode.ONE, Keycode.TWO, Keycode.THREE, Keycode.FOUR],
[Keycode.ONE, Keycode.TWO, Keycode.THREE, Keycode.A],
[Keycode.Q, Keycode.W, Keycode.E, Keycode.R],
[Keycode.A, Keycode.S, Keycode.D, Keycode.F],
[Keycode.Z, Keycode.X, Keycode.C, Keycode.LEFT_SHIFT],
[Keycode.Z, Keycode.X, Keycode.C, 0x2000],
]
# Sleep to avoid OS problems
time.sleep(1)
keyboard_state_manager = KeyboardStateManager( keymap_rows)
keyboard_state_manager = KeyboardStateManager(keymap_rows)
while True:
keyboard_state_manager.loop()
keyboard_state_manager.start()

0
lib/adafruit_hid/consumer_control_code.mpy Executable file → Normal file
View File

0
lib/adafruit_hid/keyboard_layout_base.mpy Executable file → Normal file
View File

0
lib/adafruit_hid/keyboard_layout_us.mpy Executable file → Normal file
View File

0
lib/adafruit_hid/keycode.mpy Executable file → Normal file
View File

0
lib/adafruit_mcp230xx/mcp23016.mpy Executable file → Normal file
View File

0
lib/adafruit_mcp230xx/mcp23017.mpy Executable file → Normal file
View File

0
lib/adafruit_mcp230xx/mcp230xx.mpy Executable file → Normal file
View File

0
lib/adafruit_mcp230xx/mcp23sxx.mpy Executable file → Normal file
View File

0
lib/adafruit_mcp230xx/mcp23xxx.mpy Executable file → Normal file
View File

0
lib/adafruit_rgbled.mpy Executable file → Normal file
View File

0
lib/simpleio.mpy Executable file → Normal file
View File