continued to break out keys and pins

This commit is contained in:
2022-01-06 20:11:17 +01:00
parent 5476f0cd55
commit cb59e3145e
4 changed files with 305 additions and 255 deletions

View File

@@ -3,6 +3,7 @@
import board
from keyboard import Hold, Toggle
from keycodes import SE
from keytypes import Keycode, Modifier
io_extenders_pinout = [(0x20, board.GP1, board.GP0)]
@@ -15,29 +16,29 @@ pinout: tuple[int, int] = [
keymap = [[
Hold(1), Toggle(2), Toggle(3), Toggle(4),
Toggle(0), SE.B, SE.C, SE.D,
SE.E, SE.F, SE.G, SE.H,
SE.I, SE.J, SE.K, SE.L
Toggle(0), Keycode(SE.B), Keycode(SE.C), Keycode(SE.D),
Keycode(SE.E), Keycode(SE.F), Keycode(SE.G), Keycode(SE.H),
Keycode(SE.I), Keycode(SE.J), Keycode(SE.K), Keycode(SE.L)
], [
Hold(1), Toggle(2), Toggle(3), Toggle(4),
SE.ONE, Hold(1), SE.C, SE.D,
SE.E, SE.F, SE.G, SE.H,
SE.BACKSPACE, SE.J, SE.K, SE.L
Keycode(SE.ONE), Hold(1), Keycode(SE.C), Keycode(SE.D),
Keycode(SE.E), Keycode(SE.F), Keycode(SE.G), Keycode(SE.H),
Keycode(SE.BACKSPACE), Keycode(SE.J), Keycode(SE.K), Keycode(SE.L)
], [
Hold(1), Toggle(2), Toggle(3), Toggle(4),
SE.A, Hold(1), Hold(2), SE.D,
SE.E, SE.F, SE.G, SE.H,
SE.I, SE.J, SE.K, SE.L
Keycode(SE.A), Hold(1), Hold(2), Keycode(SE.D),
Keycode(SE.E), Keycode(SE.F), Keycode(SE.G), Keycode(SE.H),
Keycode(SE.I), Keycode(SE.J), Keycode(SE.K), Keycode(SE.L)
], [
Hold(1), Toggle(2), Toggle(3), Toggle(4),
SE.A, SE.B, Hold(2), Hold(3),
SE.E, SE.F, SE.G, SE.H,
SE.I, SE.J, SE.K, SE.L
Keycode(SE.A), Keycode(SE.B), Hold(2), Hold(3),
Keycode(SE.E), Keycode(SE.F), Keycode(SE.G), Keycode(SE.H),
Keycode(SE.I), Keycode(SE.J), Keycode(SE.K), Keycode(SE.L)
], [
Hold(1), Toggle(2), Toggle(3), Toggle(4),
SE.A, SE.B, SE.C, Hold(3),
SE.E, SE.F, SE.G, SE.H,
SE.I, SE.J, SE.K, SE.LEFT_ALT
Keycode(SE.A), Keycode(SE.B), Keycode(SE.C), Hold(3),
Keycode(SE.E), Keycode(SE.F), Keycode(SE.G), Keycode(SE.H),
Keycode(SE.I), Keycode(SE.J), Keycode(SE.K), Modifier(SE.LEFT_ALT)
]]
layer_colors = [(255, 255, 255), (0, 255, 0), (0, 0, 255),

View File

@@ -7,7 +7,7 @@ import usb_hid
import pwmio
import time
from micropython import const
from keytypes import LayerKey, Toggle, Hold
from keytypes import Key, LayerKey, Pin, Toggle, Hold
__RED = (255, 0, 0)
@@ -92,10 +92,9 @@ class Keyboard:
pressed_keys_last_cycle: set[int]
pressed_keys: set[int]
pins: list[digitalio.DigitalInOut]
pin_states_last_cycle: list[int]
pins: list[Pin]
keymap: list[list[int]]
keymap: list[list[Key]]
keyboard_device: usb_hid.Device
led: RGBLED
@@ -104,7 +103,7 @@ class Keyboard:
self,
io_extenders_pinout: list[tuple[int, int, int]],
pinout: list[tuple[int, int]],
keymap: list[list[int]],
keymap: list[list[Key]],
rgb_pins: tuple[int, int, int],
layer_colors: list[tuple[int, int, int]],
debug_repl: bool = False):
@@ -158,105 +157,43 @@ class Keyboard:
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]:
def initialize_pins(self, io_extenders: list[MCP23017], pinouts: list[tuple[int, int]]) -> list[Pin]:
pins = []
for pinout in pinouts:
io_extender_index, pin_number = pinout
io_extender = io_extenders[io_extender_index]
pin_index = pinouts.index(pinout)
pin = io_extender.get_pin(pin_number)
pin.direction = digitalio.Direction.INPUT
pin.pull = digitalio.Pull.UP
pins.append(pin)
pins.append(Pin(pin_index, pin))
return pins
def _initialize_session_values(self):
self.toggled_layer = 0
self.held_layer = None
self.pressed_keys = set()
self.pressed_keys_last_cycle = set()
def _get_current_layer_index(self) -> int:
return self.held_layer if self.held_layer != None else self.toggled_layer
def start(self):
if self.debug_repl:
print("Starting keyboard")
try:
self.toggled_layer = 0
self.held_layer = None
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,))
self._initialize_session_values()
if self.debug_repl:
print("Keyboard started")
while True:
for pin in self.pins:
pin_index = self.pins.index(pin)
key = self.keymap[self.held_layer if self.held_layer !=
None else self.toggled_layer][pin_index]
previousValue = self.pin_states_last_cycle[pin_index][0]
value = pin.value
currentlyPressed = value == __PRESSED
previouslyPressed = previousValue == __PRESSED
previouslyToggled = previousValue == __TOGGLED_PRESSED
previouslyToggledReleased = previousValue == __TOGGLED_RELEASED
previouslyUntoggledPressed = previousValue == __UNTOGGLED_PRESSED
previouslyDebounced = previousValue == __DEBOUNCE
if not isinstance(key, LayerKey):
if currentlyPressed:
if not previouslyPressed:
self.pressed_keys.add(key.keycode)
else:
if previouslyPressed:
try:
self.pressed_keys.remove(key.keycode)
# Catch silenly if same keycode is pressed twice then released
except KeyError:
pass
self.pin_states_last_cycle[pin_index] = (value,)
continue
# todo: Release all keys not the same as the old layer
layer = self._get_current_layer_index()
key = self.keymap[layer][pin.index]
if type(key) is Hold:
if not currentlyPressed and previouslyDebounced:
self.pin_states_last_cycle[pin_index] = (
__UNPRESSED,)
if currentlyPressed and not previouslyPressed and not previouslyDebounced:
self.held_layer = key.layer
self.pin_states_last_cycle[pin_index] = (
__PRESSED, key)
continue
if previouslyPressed and not currentlyPressed:
self.held_layer = None
self.pin_states_last_cycle[pin_index] = (
__UNPRESSED,)
continue
# todo: Release all keys not the same as the old layer
# todo: Debounce old toggle when pressing new toggle
continue
if type(key) is Toggle:
if currentlyPressed and not previouslyToggled and not previouslyToggledReleased and not previouslyUntoggledPressed:
self.toggled_layer = key.layer
self.held_layer = None
self.pin_states_last_cycle[pin_index] = (
__TOGGLED_PRESSED, key)
for i in range(len(self.pin_states_last_cycle)):
if i == pin_index or len(self.pin_states_last_cycle[i]) == 1:
continue
value, last_keycode = self.pin_states_last_cycle[i]
if type(last_keycode) is Toggle:
self.pin_states_last_cycle[i] = (
__UNPRESSED,)
if type(last_keycode) is Hold:
self.held_layer = None
self.pin_states_last_cycle[i] = (
__DEBOUNCE,)
continue
if not currentlyPressed and previouslyToggled:
self.pin_states_last_cycle[pin_index] = (
__TOGGLED_RELEASED, key)
continue
if currentlyPressed and previouslyToggledReleased:
self.toggled_layer = 0
self.pin_states_last_cycle[pin_index] = (
__UNTOGGLED_PRESSED, key)
continue
if not currentlyPressed and previouslyUntoggledPressed:
self.pin_states_last_cycle[pin_index] = (
__UNPRESSED,)
continue
continue
key.handle(self, pin)
active_layer = self.held_layer if self.held_layer != None else self.toggled_layer
if len(self.layer_colors) >= (active_layer + 1):
self.led.set(self.layer_colors[active_layer])
@@ -274,6 +211,16 @@ class Keyboard:
self.led.indicate_boot()
self.start()
def add_keycode_to_report(self, keycode: int):
self.pressed_keys.add(keycode)
def remove_keycode_from_report(self, keycode: int):
try:
self.pressed_keys.remove(keycode)
except KeyError:
# silently pass errors when same keycode has been issued twice
pass
# todo: add boot mode
def send_nkro_report(self):
"""Sends the USB HID NKRO keyboard report."""

View File

@@ -1,138 +1,137 @@
from micropython import const
from keytypes import Keycode, Modifier
class SE:
# Letters
A = Keycode(const(4))
B = Keycode(const(5))
C = Keycode(const(6))
D = Keycode(const(7))
E = Keycode(const(8))
F = Keycode(const(9))
G = Keycode(const(10))
H = Keycode(const(11))
I = Keycode(const(12))
J = Keycode(const(13))
K = Keycode(const(14))
L = Keycode(const(15))
M = Keycode(const(16))
N = Keycode(const(17))
O = Keycode(const(18))
P = Keycode(const(19))
Q = Keycode(const(20))
R = Keycode(const(21))
S = Keycode(const(22))
T = Keycode(const(23))
U = Keycode(const(24))
V = Keycode(const(25))
W = Keycode(const(26))
X = Keycode(const(27))
Y = Keycode(const(28))
Z = Keycode(const(29))
Å = Keycode(const(47))
Ä = Keycode(const(52))
Ö = Keycode(const(51))
A = const(4)
B = const(5)
C = const(6)
D = const(7)
E = const(8)
F = const(9)
G = const(10)
H = const(11)
I = const(12)
J = const(13)
K = const(14)
L = const(15)
M = const(16)
N = const(17)
O = const(18)
P = const(19)
Q = const(20)
R = const(21)
S = const(22)
T = const(23)
U = const(24)
V = const(25)
W = const(26)
X = const(27)
Y = const(28)
Z = const(29)
Å = const(47)
Ä = const(52)
Ö = const(51)
# Numbers
ZERO = Keycode(const(39))
ONE = Keycode(const(30))
TWO = Keycode(const(31))
THREE = Keycode(const(32))
FOUR = Keycode(const(33))
FIVE = Keycode(const(34))
SIX = Keycode(const(35))
SEVEN = Keycode(const(36))
EIGHT = Keycode(const(37))
NINE = Keycode(const(38))
ZERO = const(39)
ONE = const(30)
TWO = const(31)
THREE = const(32)
FOUR = const(33)
FIVE = const(34)
SIX = const(35)
SEVEN = const(36)
EIGHT = const(37)
NINE = const(38)
# Signs
HYPHEN = Keycode(const(56)) # - and _
DOT = Keycode(const(55)) # . and :
COMMA = Keycode(const(54)) # , and ;
PARAGRAPH = Keycode(const(53)) # § and ½
QUOTE = Keycode(const(49)) # ' and *
UMLAUT = Keycode(const(48)) # ¨ and ~
TICK = Keycode(const(46)) # ´ and `
PLUS = Keycode(const(45)) # + and ?
EQUAL = Keycode(const(103)) # =
ANGLE_BRACKET = Keycode(const(100)) # < and >
HYPHEN = const(56) # - and _
DOT = const(55) # . and :
COMMA = const(54) # , and ;
PARAGRAPH = const(53) # § and ½
QUOTE = const(49) # ' and *
UMLAUT = const(48) # ¨ and ~
TICK = const(46) # ´ and `
PLUS = const(45) # + and ?
EQUAL = const(103) # =
ANGLE_BRACKET = const(100) # < and >
# Function keys
F1 = Keycode(const(58))
F2 = Keycode(const(59))
F3 = Keycode(const(60))
F4 = Keycode(const(61))
F5 = Keycode(const(62))
F6 = Keycode(const(63))
F7 = Keycode(const(64))
F8 = Keycode(const(65))
F9 = Keycode(const(66))
F10 = Keycode(const(67))
F11 = Keycode(const(68))
F12 = Keycode(const(69))
PRINTSCREEN = Keycode(const(70))
CAPSLOCK = Keycode(const(57))
ESCAPE = Keycode(const(41))
SCROLLLOCK = Keycode(const(71))
PAUSEBREAK = Keycode(const(72))
INSERT = Keycode(const(73))
MENU = Keycode(const(101))
F1 = const(58)
F2 = const(59)
F3 = const(60)
F4 = const(61)
F5 = const(62)
F6 = const(63)
F7 = const(64)
F8 = const(65)
F9 = const(66)
F10 = const(67)
F11 = const(68)
F12 = const(69)
PRINTSCREEN = const(70)
CAPSLOCK = const(57)
ESCAPE = const(41)
SCROLLLOCK = const(71)
PAUSEBREAK = const(72)
INSERT = const(73)
MENU = const(101)
# Function keys tested in gnome
TOUCHPAD_TOGGLE = Keycode(const(112))
TOUCHPAD_ON = Keycode(const(113))
TOUCHPAD_OFF = Keycode(const(114))
MIC_MUTE = Keycode(const(111))
POWER = Keycode(const(102))
SETTINGS = Keycode(const(104))
HELP = Keycode(const(117))
TOUCHPAD_TOGGLE = const(112)
TOUCHPAD_ON = const(113)
TOUCHPAD_OFF = const(114)
MIC_MUTE = const(111)
POWER = const(102)
SETTINGS = const(104)
HELP = const(117)
# Spacing
TAB = Keycode(const(43))
SPACE = Keycode(const(44))
ENTER = Keycode(const(40))
TAB = const(43)
SPACE = const(44)
ENTER = const(40)
# Navigation
BACKSPACE = Keycode(const(42))
DELETE = Keycode(const(76))
HOME = Keycode(const(74))
END = Keycode(const(77))
PAGEUP = Keycode(const(75))
PAGEDOWN = Keycode(const(78))
RIGHT = Keycode(const(79))
LEFT = Keycode(const(80))
UP = Keycode(const(81))
DOWN = Keycode(const(82))
BACKSPACE = const(42)
DELETE = const(76)
HOME = const(74)
END = const(77)
PAGEUP = const(75)
PAGEDOWN = const(78)
RIGHT = const(79)
LEFT = const(80)
UP = const(81)
DOWN = const(82)
# Modifiers
LEFT_CTRL = Modifier(const(0x0100))
LEFT_SHIFT = Modifier(const(0x0200))
LEFT_ALT = Modifier(const(0x0400))
LEFT_SUPER = Modifier(const(0x0800))
RIGHT_CTRL = Modifier(const(0x1000))
RIGHT_SHIFT = Modifier(const(0x2000))
RIGHT_ALT = Modifier(const(0x4000))
LEFT_CTRL = const(0x0100)
LEFT_SHIFT = const(0x0200)
LEFT_ALT = const(0x0400)
LEFT_SUPER = const(0x0800)
RIGHT_CTRL = const(0x1000)
RIGHT_SHIFT = const(0x2000)
RIGHT_ALT = const(0x4000)
# Others
XF86_LAUNCH_5 = Keycode(const(105))
XF86_LAUNCH_6 = Keycode(const(106))
XF86_LAUNCH_7 = Keycode(const(107))
XF86_LAUNCH_8 = Keycode(const(108))
XF86_LAUNCH_9 = Keycode(const(109))
XF86_OPEN = Keycode(const(116))
SUN_FRONT = Keycode(const(119))
SUN_PROPS = Keycode(const(118))
XF86_LAUNCH_5 = const(105)
XF86_LAUNCH_6 = const(106)
XF86_LAUNCH_7 = const(107)
XF86_LAUNCH_8 = const(108)
XF86_LAUNCH_9 = const(109)
XF86_OPEN = const(116)
SUN_FRONT = const(119)
SUN_PROPS = const(118)
# Numpad
NUM_DOT = Keycode(const(99))
NUM_0 = Keycode(const(98))
NUM_1 = Keycode(const(89))
NUM_2 = Keycode(const(90))
NUM_3 = Keycode(const(91))
NUM_4 = Keycode(const(92))
NUM_5 = Keycode(const(93))
NUM_6 = Keycode(const(94))
NUM_7 = Keycode(const(95))
NUM_8 = Keycode(const(96))
NUM_9 = Keycode(const(97))
NUM_ENTER = Keycode(const(88))
NUM_PLUS = Keycode(const(87)) # +
NUM_MINUS = Keycode(const(86)) # -
NUM_ASTERISK = Keycode(const(85)) # *
NUM_SLASH = Keycode(const(84)) # /
NUMLOCK = Keycode(const(83))
NUM_DOT = const(99)
NUM_0 = const(98)
NUM_1 = const(89)
NUM_2 = const(90)
NUM_3 = const(91)
NUM_4 = const(92)
NUM_5 = const(93)
NUM_6 = const(94)
NUM_7 = const(95)
NUM_8 = const(96)
NUM_9 = const(97)
NUM_ENTER = const(88)
NUM_PLUS = const(87) # +
NUM_MINUS = const(86) # -
NUM_ASTERISK = const(85) # *
NUM_SLASH = const(84) # /
NUMLOCK = const(83)
# Need fix
# SE_MUTE = const(-226) # Mute

View File

@@ -1,7 +1,18 @@
from keyboard import Keyboard
import digitalio
from micropython import const
class PinState:
PRESSED: int = const(0)
RELEASED: int = const(1)
class PinEvent:
NONE: int = const(0)
PRESSED: int = const(1)
RELEASED: int = const(2)
class KeyState:
"""Interpretations of hardware pin state"""
PRESSED = const(0)
@@ -22,7 +33,7 @@ class KeyEvent:
"""Interpretations of KeyStates as single events"""
NONE: int = const(0)
"""No new event has occurred"""
PRESSED: int = const(1)
PRESSED = ""
"""The key was pressed"""
RELEASED: int = const(2)
"""The key was released"""
@@ -30,8 +41,48 @@ class KeyEvent:
"""The key was released programatically, and not by an actual physical release of a button."""
class PinStates:
current: int
previous: int
def __init__(self, current: int, previous: int):
self.current = current
self.previous = previous
class Pin:
index: int
hw_pin: digitalio.DigitalInOut
previous_state: int
def __init__(self, index, hw_pin: digitalio.DigitalInOut):
self.index = index
self.hw_pin = hw_pin
self.previous_state = PinState.RELEASED
def _consume_next_state(self) -> PinStates:
states = PinStates(int(self.hw_pin.value), int(self.previous_state))
self.previous_state = states.current
return states
def read_event(self) -> int:
current, previous = self._consume_next_state()
if previous == PinState.RELEASED:
if current == PinState.RELEASED:
return PinEvent.NONE
if current == PinState.PRESSED:
return PinEvent.PRESSED
if previous == PinState.PRESSED:
if current == PinState.PRESSED:
return PinEvent.NONE
if current == PinState.RELEASED:
return PinEvent.RELEASED
raise NotImplementedError()
class Key:
index: int
previous_state: KeyState
def __init__(self):
pass
@@ -44,6 +95,15 @@ class Key:
assert self.index is not None
class KeyStates:
current: int
previous: int
def __init__(self, current: int, previous: int):
self.current = current
self.previous = previous
class Keycode(Key):
keycode: int
@@ -51,14 +111,24 @@ class Keycode(Key):
super().__init__()
self.keycode = keycode
def __interpret_hardware_changes(self, keyboard: Keyboard) -> tuple[KeyState, KeyState]:
def _consume_next_state(self, pin: Pin) -> KeyStates:
"""Interprets the changes to hardware pins as current and previous KeyState"""
current = keyboard.pins[self.index].value
previous = self.pin_states_last_cycle[self.index][0]
return (current, previous)
event = pin.read_event()
if event == PinEvent.NONE:
return KeyStates(self.previous_state, self.previous_state)
if event == PinEvent.PRESSED:
states = KeyStates(KeyState.PRESSED, int(self.previous_state))
self.previous_state = states.current
return states
if event == PinEvent.RELEASED:
states = KeyStates(KeyState.UNPRESSED, int(self.previous_state))
self.previous_state = states.current
return states
raise NotImplementedError()
def __interpret_event(self, states: tuple[KeyState, KeyState]) -> KeyEvent:
def _read_event(self, pin) -> KeyEvent:
"""Interprets the current and previous KeyState as a KeyEvent"""
states = self._consume_next_state(pin)
current, previous = states
if previous == KeyState.UNPRESSED:
if current == KeyState.UNPRESSED:
@@ -70,40 +140,20 @@ class Keycode(Key):
return KeyEvent.NONE
if current == KeyState.UNPRESSED:
return KeyEvent.RELEASED
else:
raise NotImplementedError()
raise NotImplementedError()
def react(self, keyboard: Keyboard, event: KeyEvent) -> None:
def _react_to_event(self, event: KeyEvent, keyboard) -> None:
if event == KeyEvent.NONE:
return
if event == KeyEvent.PRESSED:
keyboard.pressed_keys.add(self.keycode)
keyboard.add_keycode_to_report(self.keycode)
if event == KeyEvent.RELEASED:
try:
keyboard.pressed_keys.remove(self.keycode)
except KeyError:
# silently pass errors when same keycode has been issued twice
pass
keyboard.pin_states_last_cycle[self.index] = ()
keyboard.remove_keycode_from_report(self.keycode)
def handle(self, keyboard):
def handle(self, keyboard, pin):
super().handle()
states = self.__interpret_hardware_changes(keyboard)
event = self.__interpret_event(states)
self.react(keyboard, event)
if not isinstance(key, LayerKey):
if currentlyPressed:
if not previouslyPressed:
self.pressed_keys.add(key.keycode)
else:
if previouslyPressed:
try:
self.pressed_keys.remove(key.keycode)
# Catch silenly if same keycode is pressed twice then released
except KeyError:
pass
self.pin_states_last_cycle[pin_index] = (value,)
event = self._read_event(pin)
self._react_to_event(event, keyboard)
class Modifier(Keycode):
@@ -123,7 +173,60 @@ class Toggle(LayerKey):
def __init__(self, layer: int):
super().__init__(layer)
# todo: Release all keys not the same as the old layer
# todo: Debounce old toggle when pressing new toggle
# if type(key) is Toggle:
# if currentlyPressed and not previouslyToggled and not previouslyToggledReleased and not previouslyUntoggledPressed:
# self.toggled_layer = key.layer
# self.held_layer = None
# self.pin_states_last_cycle[pin_index] = (
# __TOGGLED_PRESSED, key)
# for i in range(len(self.pin_states_last_cycle)):
# if i == pin_index or len(self.pin_states_last_cycle[i]) == 1:
# continue
# value, last_keycode = self.pin_states_last_cycle[i]
# if type(last_keycode) is Toggle:
# self.pin_states_last_cycle[i] = (
# __UNPRESSED,)
# if type(last_keycode) is Hold:
# self.held_layer = None
# self.pin_states_last_cycle[i] = (
# __DEBOUNCE,)
# continue
# if not currentlyPressed and previouslyToggled:
# self.pin_states_last_cycle[pin_index] = (
# __TOGGLED_RELEASED, key)
# continue
# if currentlyPressed and previouslyToggledReleased:
# self.toggled_layer = 0
# self.pin_states_last_cycle[pin_index] = (
# __UNTOGGLED_PRESSED, key)
# continue
# if not currentlyPressed and previouslyUntoggledPressed:
# self.pin_states_last_cycle[pin_index] = (
# __UNPRESSED,)
# continue
#
class Hold(LayerKey):
def __init__(self, layer: int):
super().__init__(layer)
# todo: Release all keys not the same as the old layer
# if type(key) is Hold:
# if not currentlyPressed and previouslyDebounced:
# self.pin_states_last_cycle[pin_index] = (
# __UNPRESSED,)
#
# if currentlyPressed and not previouslyPressed and not previouslyDebounced:
# self.held_layer = key.layer
# self.pin_states_last_cycle[pin_index] = (
# __PRESSED, key)
# continue
# if previouslyPressed and not currentlyPressed:
# self.held_layer = None
# self.pin_states_last_cycle[pin_index] = (
# __UNPRESSED,)
# continue
#