refactorings

This commit is contained in:
2022-01-02 21:56:44 +01:00
parent 1f8f7e2e56
commit 0ecb350d5f
19 changed files with 195 additions and 252 deletions

View File

@@ -1,56 +0,0 @@
import usb_hid
from adafruit_hid.keyboard import Keyboard, find_device
from adafruit_hid.keycode import Keycode
import usb_hid
class NkroKeyboard():
report: bytearray
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")
self._keyboard_device = device
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)
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)

View File

@@ -1,166 +0,0 @@
from adafruit_hid.keycode import Keycode
from adafruit_rgbled import RGBLED
from adafruit_mcp230xx.mcp23017 import MCP23017
import digitalio
import busio
import board
import usb_hid
import time
PRESSED = False
UNPRESSED = True
MCP_ADDRESS = 32
PINOUT = [
[7, 11, 15, 0],
[8, 4, 14, 1],
[6, 10, 13, 2],
[9, 5, 12, 3]
]
class KeyboardStateManager:
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_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.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:
self.mcp = MCP23017(busio.I2C(board.GP1, board.GP0), MCP_ADDRESS)
def initialize_pins(self, pinout: list[list[int]]) -> None:
self.pinout = pinout
self.pins = []
self.state = []
for pinout_row in self.pinout:
pin_row = []
previous_state_row = []
for pinout in pinout_row:
pin = self.mcp.get_pin(pinout)
pin.direction = digitalio.Direction.INPUT
pin.pull = digitalio.Pull.UP
pin_row.append(pin)
previous_state_row.append(UNPRESSED)
self.pins.append(pin_row)
self.state.append(previous_state_row)
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]
value = pin.value
currentlyPressed = value is PRESSED
previouslyPressed = previously is PRESSED
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
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
# layer keys take a type, a set layer, and a color combination
# class LayerKeyType(Enum):
# hold = 1
# toggle = 2
# def handle_key(raw_keymap:list[list]]):
# for row in raw_keymap:
# for key in row:
# if key in Keycode:
# # handle as keycode
# pass
# if key.
# class LayerKey:
# type: LayerKeyType
# def __init__(self, type: LayerKeyType, layer: int):
# class KeycodeKey:
# def __init__(self):
# pass
#

View File

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

0
boot.py Normal file → Executable file
View File

View File

@@ -1,6 +0,0 @@
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

23
code.py Executable file → Normal file
View File

@@ -1,21 +1,6 @@
import time
from adafruit_hid.keycode import Keycode
from KeyboardStateManager import KeyboardStateManager
from keyboard import Keyboard
from config import io_extenders_pinout, pinout, keymap, rgb_pins
keyboard_state_manager = Keyboard(io_extenders_pinout, pinout, keymap, rgb_pins)
keymap_rows = [
[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, 0x2000],
]
# Sleep to avoid OS problems
time.sleep(1)
keyboard_state_manager = KeyboardStateManager(keymap_rows)
while True:
keyboard_state_manager.start()
keyboard_state_manager.start()

21
config.py Executable file
View File

@@ -0,0 +1,21 @@
from adafruit_hid.keycode import Keycode
import board
io_extenders_pinout = [(0x20, board.GP1, board.GP0)]
pinout = [
(0,7), (0,11), (0,15), (0,0),
(0,8), (0,4), (0,14), (0,1),
(0,6), (0,10), (0,13), (0,2),
(0,9), (0,5), (0,12), (0,3)
]
keymap = [
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, 0x2000,
]
rgb_pins = (board.LED_R, board.LED_G, board.LED_B)

170
keyboard.py Executable file
View File

@@ -0,0 +1,170 @@
from adafruit_mcp230xx.digital_inout import DigitalInOut
from adafruit_mcp230xx.mcp23017 import MCP23017
import digitalio
import busio
import usb_hid
import pwmio
from micropython import const
__INVERT_8_BIT_INTEGER_BITMASK = const(0xffff)
class RGBLED:
rgb_led: tuple[pwmio.PWMOut, pwmio.PWMOut, pwmio.PWMOut]
def __init__(self, rgb_pins: tuple[int, int, int]):
led_r = pwmio.PWMOut(rgb_pins[0])
led_r.duty_cycle = 0xffff
led_b = pwmio.PWMOut(rgb_pins[1])
led_b.duty_cycle = 0xffff
led_g = pwmio.PWMOut(rgb_pins[2])
led_g.duty_cycle = 0xffff
self.rgb_led = (led_r, led_b, led_g)
def set(self, rgb_values: tuple[int, int, int]):
for i in range(3):
value = self.__to_inverse_8_bit_value(rgb_values[i])
self.rgb_led[i].duty_cycle = value
def off(self):
self.set((0, 0, 0))
def __to_inverse_8_bit_value(self, value: int) -> int:
return ~(value * 257) & __INVERT_8_BIT_INTEGER_BITMASK
__PRESSED = False
__UNPRESSED = True
class Keyboard:
pressed_keys_last_cycle: set[int]
pressed_keys: set[int]
pins: list[digitalio.DigitalInOut]
pin_states_last_cycle: list[int]
keymap: list[int]
keyboard_device: usb_hid.Device
led: RGBLED
def __init__(self, io_extenders_pinout: list[tuple[int, int, int]], pinout: list[tuple[int,int]], keymap: list[int], rgb_pins: tuple[int, int, int]):
"""Initialize new keyboard
'io_extenders_pinout': list of tuple containing the i2c address, clock pin and data pin of the device. The order of the extenders are decided by the order in the list.
'pinout': list of tuple containing the index of the io_extender provided in 'io_extenders_pinout' and the pin of that extender
'keymap': list of keycodes that will be mapped against the pinout provided
'rgb_pins': tuple of three values in the inclusive range of 0-255 deciding the brightness of the red, green and blue leds"""
self.keyboard_device, self.media_device = self.initialize_hid()
io_extenders = self.initialize_io_extenders(io_extenders_pinout)
self.pins = self.initialize_pins(io_extenders, pinout)
self.keymap = keymap
self.led = RGBLED(rgb_pins)
self.led.off()
def initialize_hid(self) -> tuple[usb_hid.Device, usb_hid.Device]:
"""Initializes keyboard and media device if availabe"""
for device in usb_hid.devices:
if device.usage == 0x06 and device.usage_page == 0x01:
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:
media_device = device
if not keyboard_device:
raise RuntimeError("No keyboard device was found")
return (keyboard_device, media_device)
def initialize_io_extenders(self, io_extenders_pinout: list[tuple[int, int, int]]) -> list[MCP23017]:
io_extenders = []
for pinout in io_extenders_pinout:
address, clock_pin, data_pin = pinout
extender = MCP23017(busio.I2C(clock_pin, data_pin), address)
io_extenders.append(extender)
if not len(io_extenders):
raise ValueError("No io extenders were initialized")
return io_extenders
def initialize_pins(self, io_extenders: list[MCP23017], pinouts: list[tuple[int, int]]) -> list[DigitalInOut]:
pins = []
for pinout in pinouts:
io_extender_index, pin_number = pinout
io_extender = io_extenders[io_extender_index]
pin = io_extender.get_pin(pin_number)
pin.direction = digitalio.Direction.INPUT
pin.pull = digitalio.Pull.UP
pins.append(pin)
return pins
def start(self):
self.pressed_keys = set()
self.pressed_keys_last_cycle = set()
self.pin_states_last_cycle = []
for pin in self.pins: self.pin_states_last_cycle.append(__UNPRESSED)
while True:
for pin in self.pins:
pin_index = self.pins.index(pin)
keycode = self.keymap[pin_index]
previously = self.pin_states_last_cycle[pin_index]
value = pin.value
currentlyPressed = value is __PRESSED
previouslyPressed = previously is __PRESSED
if currentlyPressed:
if not previouslyPressed:
self.pressed_keys.add(keycode)
else:
if previouslyPressed:
try:
self.pressed_keys.remove(keycode)
# Catch silenly if same keycode is pressed twice then released
except KeyError:
pass
self.pin_states_last_cycle[pin_index] = value
if self.pressed_keys != self.pressed_keys_last_cycle:
self.send_nkro_report()
self.pressed_keys_last_cycle = set(self.pressed_keys)
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_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)
# 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
# layer keys take a type, a set layer, and a color combination
# class LayerKeyType(Enum):
# hold = 1
# toggle = 2
# def handle_key(raw_keymap:list[list]]):
# for row in raw_keymap:
# for key in row:
# if key in Keycode:
# # handle as keycode
# pass
# if key.
# class LayerKey:
# type: LayerKeyType
# def __init__(self, type: LayerKeyType, layer: int):
# class KeycodeKey:
# def __init__(self):
# pass
#

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

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

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

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

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

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

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

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

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

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

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