Code¶
Published on 2020-08-15 in Dorsch 40k Keyboard.
I know it’s not pretty, but it works. Mostly. For now.
———- more ———-
import board
import digitalio
import usb_hid
import time
COLS = (board.A6, board.A1, board.A4, board.A3, board.D6,
board.SCL, board.SDA, board.D12, board.D10, board.D13)
ROWS = (board.MOSI, board.AREF, board.D11, board.D5)
LEDS = (board.A2, board.A5, board.TX, board.RX, board.D9)
class Keyboard:
def __init__(self, matrix, cols=COLS, rows=ROWS):
self.matrix = matrix
self.cols = [digitalio.DigitalInOut(pin) for pin in cols]
self.rows = [digitalio.DigitalInOut(pin) for pin in rows]
for col in self.cols:
col.switch_to_output(value=0)
for row in self.rows:
row.switch_to_input(pull=digitalio.Pull.DOWN)
for self.device in usb_hid.devices:
if self.device.usage == 0x06 and self.device.usage_page == 0x01:
break
else:
raise RuntimeError("no HID keyboard device")
self.debounce = bytearray(len(cols))
self.last_state = bytearray(len(cols))
self.current_layer = 0
self.pressed_keys = set()
self.last_held = 0
self.release_next = 0
def scan(self):
if self.release_next:
try:
self.pressed_keys.remove(self.release_next)
except KeyError:
pass
self.release_next = 0
for x, col in enumerate(self.cols):
col.value = 1
debounce_bits = 0
for y, row in enumerate(self.rows):
state = row.value
debounce_bits |= state << y
if state != bool(self.debounce[x] & (1 << y)):
continue
last_state = bool(self.last_state[x] & (1 << y))
if state:
self.last_state[x] |= 1 << y
else:
self.last_state[x] &= ~(1 << y)
if state == last_state:
continue
if state:
self.press(x, y)
else:
self.release(x, y)
col.value = 0
self.debounce[x] = debounce_bits
def press(self, x, y):
code = self.matrix[self.current_layer][y][x]
if code == 0x0800:
self.current_layer = 1
return
if self.last_held:
if code & 0xff00 == 0x0800:
self.current_layer = 1
else:
self.pressed_keys.add(self.last_held & 0xff00)
self.last_held = 0
if code & 0xff00 and (code & 0xff) != 0:
self.last_held = code
return
self.pressed_keys.add(code)
def release(self, x, y):
code = self.matrix[self.current_layer][y][x]
if self.last_held == code:
self.pressed_keys.add(code & 0xff)
self.release_next = code & 0xff
try:
self.pressed_keys.remove(code & 0xff00)
except KeyError:
pass
if code & 0xff00 == 0x0800:
self.current_layer = 0
self.last_held = 0
return
if code == 0x0800:
self.current_layer = 0
return
for layer in 0, 1:
for mask in 0xffff, 0xff00, 0x00ff:
try:
self.pressed_keys.remove(self.matrix[layer][y][x] & mask)
except KeyError:
pass
def send_report(self, pressed_keys):
report = bytearray(8)
report_mod_keys = memoryview(report)[0:1]
report_no_mod_keys = memoryview(report)[2:]
keys = 0
for code in pressed_keys:
if code == 0:
continue
elif code == 0x0800:
continue
elif code & 0xff00 and code & 0xff == 0:
modifier = (code >> 8) - 1
report_mod_keys[0] |= 1 << modifier
elif keys < 6:
report_no_mod_keys[keys] = code
keys += 1
self.device.send_report(report)
def run(self):
last_pressed_keys = set()
while True:
self.scan()
if self.pressed_keys != last_pressed_keys:
self.send_report(self.pressed_keys)
last_pressed_keys = set(self.pressed_keys)
print(last_pressed_keys, self.last_held, self.release_next)
time.sleep(0.01)
And then there is the key definition:
import keyboard
from micropython import const
_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)
_1 = const(30)
_2 = const(31)
_3 = const(32)
_4 = const(33)
_5 = const(34)
_6 = const(35)
_7 = const(36)
_8 = const(37)
_9 = const(38)
_0 = const(39)
_ENT = const(40) # Enter
_ESC = const(41) # Esc
_BS = const(42) # Backspace
_TAB = const(43) # Tab
_SPC = const(44) # Space
_MN = const(45) # Minus -/_
_EQ = const(46) # Equal =/+
_LB = const(47) # Left bracket [/{
_RB = const(48) # Right bracket ]/}
_BSL = const(49) # Backslash \/|
_SC = const(51) # Semicolon ;/:
_QT = const(52) # Quote '/"
_GR = const(53) # Grave `/~
_CM = const(54) # Comma ,/<
_DT = const(55) # Dot ./>
_SL = const(56) # Slash //?
_CAPS = const(57) # Caps lock
_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)
_PS = const(70) # Print screen
_SCL = const(71) # Scroll lock
_PA = const(72) # Pause
_INS = const(73) # Insert
_HOME = const(74) # Home
_PGUP = const(75) # Page up
_DEL = const(76) # Delete
_END = const(77) # End
_PGDN = const(78) # Page down
_AR = const(79) # Arrow right
_AL = const(80) # Arrow left
_AD = const(81) # Arrow down
_AU = const(82) # Arrow up
_LCT = const(0x0100) # Left control
_LSH = const(0x0200) # Left shift
_LAL = const(0x0300) # Left alternate
_LSP = const(0x0400) # Left super
_RCT = const(0x0500) # Right control
_RSH = const(0x0600) # Right shift
_RAL = const(0x0700) # Right alternate
_FN = const(0x0800) # Function
MATRIX = (
(_Q, _W, _E, _R, _T, _Y, _U, _I, _O, _P),
(_A, _S, _D, _F, _G, _H, _J, _K, _L, _ENT),
(_Z, _X, _C, _V, _B, _N, _M, _CM, _AU, _SL),
(_LSH | _CAPS,
_LCT | _ESC,
_LAL | _INS,
_FN | _TAB,
_BS,
_SPC,
_RAL | _DEL,
_AL,
_AD,
_AR),
), (
(_1, _2, _3, _4, _5, _6, _7, _8, _9, _0),
(_B, _F2, _F3, _F4, _F5, _MN, _EQ, _GR, _QT, _SC),
(_F6, _F7, _F8, _F9, _F10, _LB, _RB, _DT, _PGUP, _BSL),
(_CAPS,
_ESC,
_INS,
_TAB,
_F11,
_F12,
_DEL,
_HOME,
_PGDN,
_END),
)
keyboard.Keyboard(MATRIX).run()