From 8c12d1ebbec21cf53bd0b0ad0d5039f64b3823ed Mon Sep 17 00:00:00 2001 From: Yihui Xiong Date: Tue, 8 Sep 2020 16:10:35 +0800 Subject: [PATCH 01/12] not get ble connection interval as it may block forever --- keyboard/__init__.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/keyboard/__init__.py b/keyboard/__init__.py index acfcb2d..6ef8f40 100644 --- a/keyboard/__init__.py +++ b/keyboard/__init__.py @@ -189,11 +189,12 @@ def check(self): self.backlight.set_bt_led(None) for c in self.ble.connections: try: - if c.connection_interval > 11.25: - c.connection_interval = 11.25 - except _bleio.BluetoothError: - self.log("failed to set ble connection interval") - self.log('ble connection interval {}'.format(c.connection_interval)) + # 11.25 ms is the min connection interval for most systems + c.connection_interval = 11.25 + except Exception: + print("Failed to set ble connection interval") + # Avoid getting connection_interval, as it may block forever + # self.log('ble connection interval {}'.format(c.connection_interval)) elif time.time() > self.adv_timeout: self.stop_advertising() From 375c7f8746647638f0470244fc9b0490099a70b6 Mon Sep 17 00:00:00 2001 From: Yihui Xiong Date: Tue, 8 Sep 2020 16:40:12 +0800 Subject: [PATCH 02/12] add battery level --- keyboard/__init__.py | 13 +++++++++++-- keyboard/model/__init__.py | 8 ++++---- keyboard/model/m60.py | 28 ++++++++++++++++++++++++++++ keyboard/model/pitaya_go.py | 3 +++ 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/keyboard/__init__.py b/keyboard/__init__.py index 6ef8f40..3c21100 100644 --- a/keyboard/__init__.py +++ b/keyboard/__init__.py @@ -10,10 +10,11 @@ import adafruit_ble from adafruit_ble.advertising import Advertisement from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from adafruit_ble.services.standard import BatteryService from adafruit_ble.services.standard.hid import HIDService from .hid import HID -from .model import Matrix, COORDS, Backlight +from .model import Matrix, COORDS, Backlight, battery_level from .action_code import * @@ -160,7 +161,10 @@ def __init__(self, keymap=KEYMAP, pairs=(), verbose=True): self.heatmap = memoryview(self.data)[4:] ble_hid = HIDService() - self.advertisement = ProvideServicesAdvertisement(ble_hid) + self.battery = BatteryService() + self.battery.level = battery_level() + self.battery_update_time = time.time() + 360 + self.advertisement = ProvideServicesAdvertisement(ble_hid, self.battery) self.advertisement.appearance = 961 self.ble = adafruit_ble.BLERadio() self.change_bt(self.ble_id) @@ -216,6 +220,11 @@ def check(self): self.backlight.set_hid_leds(leds) self.log('keyboard leds {}'.format(bin(leds))) self.update_current_conn() + + # update battery level + if time.time() > self.battery_update_time: + self.battery_update_time = time.time() + 360 + self.battery.level = battery_level() def setup(self): convert = lambda a: array.array('H', (get_action_code(k) for k in a)) diff --git a/keyboard/model/__init__.py b/keyboard/model/__init__.py index 89b3fb8..e977592 100644 --- a/keyboard/model/__init__.py +++ b/keyboard/model/__init__.py @@ -2,7 +2,7 @@ machine = os.uname().machine -if machine.find('M60 Keyboard') >= 0: - from .m60 import Matrix, COORDS, Backlight -elif machine.find('Pitaya Go') >= 0: - from .pitaya_go import Matrix, COORDS, Backlight +if machine.find("M60 Keyboard") >= 0: + from .m60 import Matrix, COORDS, Backlight, battery_level +elif machine.find("Pitaya Go") >= 0: + from .pitaya_go import Matrix, COORDS, Backlight, battery_level diff --git a/keyboard/model/m60.py b/keyboard/model/m60.py index 65894f9..0897ee4 100644 --- a/keyboard/model/m60.py +++ b/keyboard/model/m60.py @@ -1,4 +1,6 @@ +import analogio +import microcontroller from .is32fl3733 import IS31FL3733 try: @@ -27,6 +29,32 @@ ) +BATTERY_LIMIT = 3100 # Cutoff voltage [mV]. +BATTERY_FULLLIMIT = 4190 # Full charge definition [mV]. +BATTERY_DELTA = 10 # mV between each element in the SoC vector. + +BATTERY_VOLTAGE = ( + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, + 4, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 11, 12, 13, 13, 14, 15, 16, + 18, 19, 22, 25, 28, 32, 36, 40, 44, 47, 51, 53, 56, 58, 60, 62, 64, 66, 67, 69, + 71, 72, 74, 76, 77, 79, 81, 82, 84, 85, 85, 86, 86, 86, 87, 88, 88, 89, 90, 91, + 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 100 +) + +battery_in = analogio.AnalogIn(microcontroller.pin.P0_02) + +def battery_level(): + # (3300 * 2 * battery.value) >> 16 + voltage = (3300 * battery_in.value) >> 15 + i = (voltage - BATTERY_LIMIT) // BATTERY_DELTA + if i >= len(BATTERY_VOLTAGE): + i = len(BATTERY_VOLTAGE) - 1 + elif i < 0: + i = 0 + return BATTERY_VOLTAGE[i] + + class Backlight: def __init__(self): self.dev = IS31FL3733() diff --git a/keyboard/model/pitaya_go.py b/keyboard/model/pitaya_go.py index a22c58b..8a4c707 100644 --- a/keyboard/model/pitaya_go.py +++ b/keyboard/model/pitaya_go.py @@ -19,3 +19,6 @@ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 52, 0, 53, 55, 54, 0, 0, 56, 0, 0, 57, 58, 59, 60, 0, 0 ) + +def battery_level(): + return 100 From 4205d4b622b8be4576fa5e4e30e1e7c7b2c0d92e Mon Sep 17 00:00:00 2001 From: Yihui Xiong Date: Tue, 8 Sep 2020 16:48:23 +0800 Subject: [PATCH 03/12] reset layer_mask when keymap is changed --- keyboard/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/keyboard/__init__.py b/keyboard/__init__.py index 3c21100..4529005 100644 --- a/keyboard/__init__.py +++ b/keyboard/__init__.py @@ -174,10 +174,8 @@ def __init__(self, keymap=KEYMAP, pairs=(), verbose=True): def update_current_conn(self): if usb_is_connected() and self.usb_status == 3: conn = "USB" - elif self.ble.connected: - conn = "BT%d" % self.ble_id else: - conn = "" + conn = "BT%d" % self.ble_id if conn != self._current_conn: self._current_conn = conn if conn in self.action_maps: @@ -186,6 +184,9 @@ def update_current_conn(self): self.current_keymap = self.actonmap print("Current connection changed to %s" % self._current_conn) + # reset `layer_mask` when keymap is changed + self.layer_mask = 1 + def check(self): if self.adv_timeout: if self.ble.connected: From 66e02f2baab330df7f5f36a9bf56b9a6bc42787c Mon Sep 17 00:00:00 2001 From: Yihui Xiong Date: Tue, 8 Sep 2020 17:16:52 +0800 Subject: [PATCH 04/12] format with black --- keyboard/__init__.py | 89 +++++++++++++++++++++--------------- keyboard/action_code.py | 1 + keyboard/hid.py | 19 +++++--- keyboard/matrix.py | 28 ++++++------ keyboard/model/backlight.py | 4 +- keyboard/model/is32fl3733.py | 17 ++++--- keyboard/model/m60.py | 4 +- keyboard/model/pitaya_go.py | 1 + 8 files changed, 92 insertions(+), 71 deletions(-) diff --git a/keyboard/__init__.py b/keyboard/__init__.py index 4529005..98cae3a 100644 --- a/keyboard/__init__.py +++ b/keyboard/__init__.py @@ -1,4 +1,3 @@ - import array import time import struct @@ -25,9 +24,9 @@ L3B = LAYER_TAP(3, B) # Semicolon & Ctrl -SCC = MODS_TAP(MODS(RCTRL), ';') - +SCC = MODS_TAP(MODS(RCTRL), ";") +# fmt: off KEYMAP = ( # layer 0 ( @@ -65,14 +64,18 @@ ___, ___, ___, ___, ___, ___, ___, ___ ), ) +# fmt: on + @micropython.asm_thumb def mem(r0): ldr(r0, [r0, 0]) + def usb_is_connected(): return mem(0x40000438) == 0x3 + def reset_into_bootloader(): microcontroller.on_next_reset(microcontroller.RunMode.BOOTLOADER) microcontroller.reset() @@ -87,7 +90,9 @@ def is_tapped(matrix, key): if target == matrix.view(0): return True else: - n = matrix.wait(200 - matrix.ms(matrix.time() - matrix.get_keydown_time(key))) + n = matrix.wait( + 200 - matrix.ms(matrix.time() - matrix.get_keydown_time(key)) + ) if n == 2 and target == matrix.view(1): return True @@ -152,11 +157,12 @@ def __init__(self, keymap=KEYMAP, pairs=(), verbose=True): self._current_conn = "" - self.data = array.array('L', microcontroller.nvm[:272]) - if self.data[0] != 0x424b5950: - self.data[0] = 0x424b5950 + self.data = array.array("L", microcontroller.nvm[:272]) + if self.data[0] != 0x424B5950: + self.data[0] = 0x424B5950 self.data[1] = 1 - for i in range(4, 68): self.data[i] = 0 + for i in range(4, 68): + self.data[i] = 0 self.ble_id = self.data[1] self.heatmap = memoryview(self.data)[4:] @@ -188,7 +194,7 @@ def update_current_conn(self): self.layer_mask = 1 def check(self): - if self.adv_timeout: + if self.adv_timeout: if self.ble.connected: self.adv_timeout = 0 self.backlight.set_bt_led(None) @@ -219,21 +225,23 @@ def check(self): if leds != self.leds: self.leds = leds self.backlight.set_hid_leds(leds) - self.log('keyboard leds {}'.format(bin(leds))) + self.log("keyboard leds {}".format(bin(leds))) self.update_current_conn() # update battery level if time.time() > self.battery_update_time: self.battery_update_time = time.time() + 360 self.battery.level = battery_level() - + def setup(self): - convert = lambda a: array.array('H', (get_action_code(k) for k in a)) + convert = lambda a: array.array("H", (get_action_code(k) for k in a)) self.actonmap = tuple(convert(layer) for layer in self.keymap) self.action_maps = {} for key in self.profiles: - self.action_maps[key] = tuple(convert(layer) for layer in self.profiles[key]) + self.action_maps[key] = tuple( + convert(layer) for layer in self.profiles[key] + ) for pair in self.pairs: for key in pair: @@ -263,18 +271,18 @@ def change_bt(self, n): if 0 > n or n > 9: return - uid = self.uid[n:n+6] + uid = self.uid[n : n + 6] uid[-1] = uid[-1] | 0xC0 address = _bleio.Address(uid, _bleio.Address.RANDOM_STATIC) try: self.ble._adapter.address = address - name = 'PYKB {}'.format(n) + name = "PYKB {}".format(n) self.advertisement.complete_name = name self.ble.name = name self.ble_id = n if self.data[1] != n: self.data[1] = n - microcontroller.nvm[:272] = struct.pack('68L', *self.data) + microcontroller.nvm[:272] = struct.pack("68L", *self.data) except Exception as e: print(e) self.log(self.ble._adapter.address) @@ -304,7 +312,7 @@ def action_code(self, position): for layer in range(len(self.current_keymap) - 1, -1, -1): if (layer_mask >> layer) & 1: code = self.current_keymap[layer][position] - if code == 1: # TRANSPARENT + if code == 1: # TRANSPARENT continue return code return 0 @@ -384,7 +392,9 @@ def run(self): if n == 1: key = matrix.view(0) if key < 0x80 and key in self.pair_keys: - n = matrix.wait(10 - ms(matrix.time() - matrix.get_keydown_time(key))) + n = matrix.wait( + 10 - ms(matrix.time() - matrix.get_keydown_time(key)) + ) if n >= 2: pair = {matrix.view(0), matrix.view(1)} @@ -393,8 +403,10 @@ def run(self): key1 = self.get() key2 = self.get() - dt = ms(matrix.get_keydown_time(key2) - matrix.get_keydown_time(key1)) - log('pair keys {} {}, dt = {}'.format(pair_index, pair, dt)) + dt = ms( + matrix.get_keydown_time(key2) - matrix.get_keydown_time(key1) + ) + log("pair keys {} {}, dt = {}".format(pair_index, pair, dt)) if callable(self.pairs_handler): try: self.pairs_handler(dev, pair_index) @@ -412,7 +424,7 @@ def run(self): self.press(action_code) if self.verbose: dt = ms(matrix.time() - matrix.get_keydown_time(key)) - log('{} \\ {} latency {}'.format(key, hex(action_code), dt)) + log("{} \\ {} latency {}".format(key, hex(action_code), dt)) else: kind = action_code >> 12 if kind < ACT_MODS_TAP: @@ -424,7 +436,7 @@ def run(self): elif kind < ACT_USAGE: # MODS_TAP if is_tapped(matrix, key): - log('TAP') + log("TAP") keycode = action_code & 0xFF keys[key] = keycode self.press(keycode) @@ -439,13 +451,13 @@ def run(self): # todo pass elif kind == ACT_LAYER_TAP or kind == ACT_LAYER_TAP_EXT: - layer = ((action_code >> 8) & 0x1F) + layer = (action_code >> 8) & 0x1F mask = 1 << layer if is_tapped(matrix, key): - log('TAP') + log("TAP") keycode = action_code & 0xFF if keycode & 0xE0 == 0xC0: - log('LAYER_MODS') + log("LAYER_MODS") mods = keycode & 0x1F keycodes = mods_to_keycodes(mods) self.press(*keycodes) @@ -453,17 +465,19 @@ def run(self): keys[key] = keycode self.press(keycode) else: - log('toggle {}'.format(self.layer_mask)) - self.layer_mask = (self.layer_mask & ~mask) | (mask & ~self.layer_mask) + log("toggle {}".format(self.layer_mask)) + self.layer_mask = (self.layer_mask & ~mask) | ( + mask & ~self.layer_mask + ) else: if action_code & 0xE0 == 0xC0: - log('LAYER_MODS') + log("LAYER_MODS") mods = action_code & 0x1F keycodes = mods_to_keycodes(mods) self.press(*keycodes) self.layer_mask |= mask - log('layers {}'.format(self.layer_mask)) + log("layers {}".format(self.layer_mask)) elif kind == ACT_MACRO: if callable(self.macro_handler): i = action_code & 0xFFF @@ -479,7 +493,9 @@ def run(self): elif action_code == SHUTDOWN: microcontroller.reset() elif action_code == HEATMAP: - microcontroller.nvm[:272] = struct.pack('68L', *self.data) + microcontroller.nvm[:272] = struct.pack( + "68L", *self.data + ) if usb_is_connected(): microcontroller.reset() elif action_code == USB_TOGGLE: @@ -488,11 +504,11 @@ def run(self): self.toggle_bt() elif BT(0) <= action_code and action_code <= BT(9): i = action_code - BT(0) - log('switch to bt {}'.format(i)) + log("switch to bt {}".format(i)) self.change_bt(i) dt = ms(matrix.time() - matrix.get_keydown_time(key)) - log('{} \\ {} latency {}'.format(key, hex(keys[key]), dt)) + log("{} \\ {} latency {}".format(key, hex(keys[key]), dt)) else: action_code = keys[key] if action_code < 0xFF: @@ -516,16 +532,16 @@ def run(self): elif kind == ACT_MOUSEKEY: pass elif kind == ACT_LAYER_TAP or kind == ACT_LAYER_TAP_EXT: - layer = ((action_code >> 8) & 0x1F) + layer = (action_code >> 8) & 0x1F keycode = action_code & 0xFF if keycode != OP_TAP_TOGGLE: if keycode & 0xE0 == 0xC0: - log('LAYER_MODS') + log("LAYER_MODS") mods = keycode & 0x1F keycodes = mods_to_keycodes(mods) self.release(*keycodes) self.layer_mask &= ~(1 << layer) - log('layers {}'.format(self.layer_mask)) + log("layers {}".format(self.layer_mask)) elif kind == ACT_MACRO: if callable(self.macro_handler): i = action_code & 0xFFF @@ -536,5 +552,4 @@ def run(self): if self.verbose: dt = ms(matrix.time() - matrix.get_keyup_time(key)) - log('{} / {} latency {}'.format(key, hex(action_code), dt)) - + log("{} / {} latency {}".format(key, hex(action_code), dt)) diff --git a/keyboard/action_code.py b/keyboard/action_code.py index ef560f7..a2982cf 100644 --- a/keyboard/action_code.py +++ b/keyboard/action_code.py @@ -3,6 +3,7 @@ # reference: # + https://gist.github.com/MightyPork/6da26e382a7ad91b5496ee55fdc73db2 # +# fmt: off NO = '\x00' TRANSPARENT = '\x01' diff --git a/keyboard/hid.py b/keyboard/hid.py index 9ac2a9b..6b28793 100644 --- a/keyboard/hid.py +++ b/keyboard/hid.py @@ -1,12 +1,15 @@ - import struct def find_device(devices, usage_page, usage): for device in devices: - if device.usage_page == usage_page and device.usage == usage and hasattr(device, 'send_report'): + if ( + device.usage_page == usage_page + and device.usage == usage + and hasattr(device, "send_report") + ): return device - raise ValueError('Not found') + raise ValueError("Not found") class HID: @@ -24,7 +27,11 @@ def __init__(self, devices): self.report_keys = memoryview(self.report)[2:] for device in devices: - if device.usage_page == 0x1 and device.usage == 0x6 and hasattr(device, 'report'): + if ( + device.usage_page == 0x1 + and device.usage == 0x6 + and hasattr(device, "report") + ): self._leds = device break else: @@ -32,7 +39,7 @@ def __init__(self, devices): def press(self, *keycodes): for keycode in keycodes: - if 0xe0 <= keycode and keycode < 0xe8: + if 0xE0 <= keycode and keycode < 0xE8: self.report[0] |= 1 << (keycode & 0x7) continue @@ -48,7 +55,7 @@ def press(self, *keycodes): def release(self, *keycodes): for keycode in keycodes: - if 0xe0 <= keycode and keycode < 0xe8: + if 0xE0 <= keycode and keycode < 0xE8: self.report[0] &= ~(1 << (keycode & 0x7)) continue diff --git a/keyboard/matrix.py b/keyboard/matrix.py index 987bfef..58211ff 100644 --- a/keyboard/matrix.py +++ b/keyboard/matrix.py @@ -1,8 +1,6 @@ - - - import digitalio import time + # from microcontroller.pin import * @@ -20,7 +18,7 @@ def __init__(self): self.tail = 0 self.length = 0 - self.rows = [] # row as output + self.rows = [] # row as output for pin in self.ROWS: io = digitalio.DigitalInOut(pin) io.direction = digitalio.Direction.OUTPUT @@ -28,7 +26,7 @@ def __init__(self): io.value = 0 self.rows.append(io) - self.cols = [] # col as input + self.cols = [] # col as input for pin in self.COLS: io = digitalio.DigitalInOut(pin) io.direction = digitalio.Direction.INPUT @@ -37,8 +35,8 @@ def __init__(self): # row selected value depends on diodes' direction self.pressed = bool(self.ROW2COL) - self.t0 = [0] * self.keys # key pressed time - self.t1 = [0] * self.keys # key released time + self.t0 = [0] * self.keys # key pressed time + self.t1 = [0] * self.keys # key released time self.mask = 0 self.count = 0 self._debounce_time = 20000000 @@ -55,38 +53,38 @@ def scan(self): count = 0 key_index = -1 for row in self.rows: - row.value = pressed # select row + row.value = pressed # select row for col in cols: key_index += 1 if col.value == pressed: key_mask = 1 << key_index if not (last_mask & key_mask): if t - self.t1[key_index] < self._debounce_time: - print('debonce') + print("debonce") continue - + self.t0[key_index] = t self.put(key_index) - + mask |= key_mask count += 1 elif last_mask and (last_mask & (1 << key_index)): if t - self.t0[key_index] < self._debounce_time: - print('debonce') + print("debonce") mask |= 1 << key_index continue - + self.t1[key_index] = t self.put(0x80 | key_index) row.value = not pressed self.mask = mask self.count = count - + return self.length def wait(self, timeout=0): - last = self.length + last = self.length if timeout: end_time = time.monotonic_ns() + timeout * 1000000 while True: diff --git a/keyboard/model/backlight.py b/keyboard/model/backlight.py index d30044d..6b753ae 100644 --- a/keyboard/model/backlight.py +++ b/keyboard/model/backlight.py @@ -1,5 +1,3 @@ - - class Backlight: def __init__(self): pass @@ -23,4 +21,4 @@ def set_bt_led(self, v): pass def update(self): - pass \ No newline at end of file + pass diff --git a/keyboard/model/is32fl3733.py b/keyboard/model/is32fl3733.py index 70ba992..d82a117 100644 --- a/keyboard/model/is32fl3733.py +++ b/keyboard/model/is32fl3733.py @@ -1,4 +1,3 @@ - import board import busio import digitalio @@ -69,15 +68,15 @@ def set_brightness(self, n): self.write(1, n) def pixel(self, i, r, g, b): - row = i >> 4 # i // 16 - col = i & 15 # i % 16 + row = i >> 4 # i // 16 + col = i & 15 # i % 16 self.pixels[row * 48 + col] = g self.pixels[row * 48 + 16 + col] = r self.pixels[row * 48 + 32 + col] = b def update_pixel(self, i, r, g, b): - row = i >> 4 # i // 16 - col = i & 15 # i % 16 + row = i >> 4 # i // 16 + col = i & 15 # i % 16 self.pixels[row * 48 + col] = g self.pixels[row * 48 + 16 + col] = r self.pixels[row * 48 + 32 + col] = b @@ -95,7 +94,7 @@ def update(self): self.i2c.writeto(self.address, self._buffer) def any(self): - '''Check if any pixel is not zero''' + """Check if any pixel is not zero""" for pixel in self.pixels: if pixel > 0: return True @@ -119,9 +118,9 @@ def breathing_pixel(self, i, mode=2): if not self.power.value: self.power.value = 1 self.page(2) - row = i >> 4 # i // 16 - col = i & 15 # i % 16 - self.write(row * 48 + 32 + col, mode) # blue + row = i >> 4 # i // 16 + col = i & 15 # i % 16 + self.write(row * 48 + 32 + col, mode) # blue # self.write(row * 48 + col, mode) # green # self.write(row * 48 + 16 + col, mode) # red diff --git a/keyboard/model/m60.py b/keyboard/model/m60.py index 0897ee4..f3c5734 100644 --- a/keyboard/model/m60.py +++ b/keyboard/model/m60.py @@ -1,10 +1,11 @@ +# fmt: off import analogio import microcontroller from .is32fl3733 import IS31FL3733 try: - # using built-in matrix if it is available + # usebuilt-in matrix if it is available from matrix import Matrix except ImportError: from ..matrix import Matrix @@ -44,6 +45,7 @@ battery_in = analogio.AnalogIn(microcontroller.pin.P0_02) + def battery_level(): # (3300 * 2 * battery.value) >> 16 voltage = (3300 * battery_in.value) >> 15 diff --git a/keyboard/model/pitaya_go.py b/keyboard/model/pitaya_go.py index 8a4c707..0681e0f 100644 --- a/keyboard/model/pitaya_go.py +++ b/keyboard/model/pitaya_go.py @@ -1,3 +1,4 @@ +# fmt: off from ..matrix import Matrix from board import P27, P13, P30, P20, P3, P26, P31, P29, P28, P5, P4, P24, P25, P23, P22, P14, P15, P16, P17 From a59cb9a962adfef93095d33e861deecac1f15655 Mon Sep 17 00:00:00 2001 From: Yihui Xiong Date: Tue, 8 Sep 2020 18:48:23 +0800 Subject: [PATCH 05/12] add docstring --- keyboard/__init__.py | 3 +++ keyboard/matrix.py | 25 ++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/keyboard/__init__.py b/keyboard/__init__.py index 98cae3a..fb53ac3 100644 --- a/keyboard/__init__.py +++ b/keyboard/__init__.py @@ -69,6 +69,7 @@ @micropython.asm_thumb def mem(r0): + """Read memory from the address""" ldr(r0, [r0, 0]) @@ -82,6 +83,7 @@ def reset_into_bootloader(): def is_tapped(matrix, key): + """Check if the key is tapped (press & release quickly)""" n = len(matrix) if n == 0: n = matrix.wait(500 - matrix.ms(matrix.time() - matrix.get_keydown_time(key))) @@ -94,6 +96,7 @@ def is_tapped(matrix, key): 200 - matrix.ms(matrix.time() - matrix.get_keydown_time(key)) ) if n == 2 and target == matrix.view(1): + # Fast typing: A down, B down, A up, B up return True return False diff --git a/keyboard/matrix.py b/keyboard/matrix.py index 58211ff..a38a184 100644 --- a/keyboard/matrix.py +++ b/keyboard/matrix.py @@ -5,10 +5,16 @@ class Matrix: + """ + Implement the drive of keyboard matrix and provide an event queue. + """ + # ROWS = (P0_05, P0_06, P0_07, P0_08, P1_09, P1_08, P0_12, P0_11) # COLS = (P0_19, P0_20, P0_21, P0_22, P0_23, P0_24, P0_25, P0_26) ROWS = () COLS = () + + # direction of diode ROW2COL = False def __init__(self): @@ -42,6 +48,11 @@ def __init__(self): self._debounce_time = 20000000 def scan(self): + """ + Scan keyboard matrix and save key event into the queue. + + :return: length of the key event queue. + """ t = time.monotonic_ns() # use local variables to speed up @@ -83,7 +94,8 @@ def scan(self): return self.length - def wait(self, timeout=0): + def wait(self, timeout=1000): + """Wait for a new key event or timeout""" last = self.length if timeout: end_time = time.monotonic_ns() + timeout * 1000000 @@ -98,6 +110,7 @@ def wait(self, timeout=0): return n def put(self, data): + """Put a key event into the queue""" self.queue[self.head] = data self.head += 1 if self.head >= self.keys: @@ -105,6 +118,7 @@ def put(self, data): self.length += 1 def get(self): + """Remove and return the first event from the queue.""" data = self.queue[self.tail] self.tail += 1 if self.tail >= self.keys: @@ -113,24 +127,31 @@ def get(self): return data def view(self, n): + """Return the specified event""" return self.queue[(self.tail + n) % self.keys] def __getitem__(self, n): + """Return the specified event""" return self.queue[(self.tail + n) % self.keys] def __len__(self): + """Return the number of events in the queue""" return self.length def get_keydown_time(self, key): + """Return the key pressed time""" return self.t0[key] def get_keyup_time(self, key): + """Return the key released time""" return self.t1[key] def time(self): + """Return current time""" return time.monotonic_ns() def ms(self, t): + """Convert time to milliseconds""" return t // 1000000 @property @@ -139,7 +160,9 @@ def debounce_time(self): @debounce_time.setter def debounce_time(self, t): + """Set debounce time""" self._debounce_time = t * 1000000 def suspend(self): + """Suspend keyboard""" pass From 38f79bdba497aaf6600dcbe48f67ad5504268d75 Mon Sep 17 00:00:00 2001 From: Yihui Xiong Date: Tue, 8 Sep 2020 21:17:26 +0800 Subject: [PATCH 06/12] fix LAYER_MODS --- keyboard/__init__.py | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/keyboard/__init__.py b/keyboard/__init__.py index fb53ac3..377ebd8 100644 --- a/keyboard/__init__.py +++ b/keyboard/__init__.py @@ -456,28 +456,25 @@ def run(self): elif kind == ACT_LAYER_TAP or kind == ACT_LAYER_TAP_EXT: layer = (action_code >> 8) & 0x1F mask = 1 << layer - if is_tapped(matrix, key): + if action_code & 0xE0 == 0xC0: + log("LAYER_MODS") + mods = action_code & 0x1F + keycodes = mods_to_keycodes(mods) + self.press(*keycodes) + self.layer_mask |= mask + elif is_tapped(matrix, key): log("TAP") keycode = action_code & 0xFF - if keycode & 0xE0 == 0xC0: - log("LAYER_MODS") - mods = keycode & 0x1F - keycodes = mods_to_keycodes(mods) - self.press(*keycodes) - elif keycode != OP_TAP_TOGGLE: - keys[key] = keycode - self.press(keycode) - else: - log("toggle {}".format(self.layer_mask)) + if keycode == OP_TAP_TOGGLE: + log("TOGGLE {}".format(layer)) self.layer_mask = (self.layer_mask & ~mask) | ( mask & ~self.layer_mask ) + keys[key] = 0 + else: + keys[key] = keycode + self.press(keycode) else: - if action_code & 0xE0 == 0xC0: - log("LAYER_MODS") - mods = action_code & 0x1F - keycodes = mods_to_keycodes(mods) - self.press(*keycodes) self.layer_mask |= mask log("layers {}".format(self.layer_mask)) @@ -537,14 +534,13 @@ def run(self): elif kind == ACT_LAYER_TAP or kind == ACT_LAYER_TAP_EXT: layer = (action_code >> 8) & 0x1F keycode = action_code & 0xFF - if keycode != OP_TAP_TOGGLE: - if keycode & 0xE0 == 0xC0: - log("LAYER_MODS") - mods = keycode & 0x1F - keycodes = mods_to_keycodes(mods) - self.release(*keycodes) - self.layer_mask &= ~(1 << layer) - log("layers {}".format(self.layer_mask)) + if keycode & 0xE0 == 0xC0: + log("LAYER_MODS") + mods = keycode & 0x1F + keycodes = mods_to_keycodes(mods) + self.release(*keycodes) + self.layer_mask &= ~(1 << layer) + log("layers {}".format(self.layer_mask)) elif kind == ACT_MACRO: if callable(self.macro_handler): i = action_code & 0xFFF From 0f8e6be52eb08d3f0a8d0decf9063ce53acfe5b1 Mon Sep 17 00:00:00 2001 From: Micah Halter Date: Wed, 9 Sep 2020 21:58:10 -0400 Subject: [PATCH 07/12] Added ability to adjust tap and pair key delays --- keyboard/__init__.py | 46 +++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/keyboard/__init__.py b/keyboard/__init__.py index 377ebd8..2eed51e 100644 --- a/keyboard/__init__.py +++ b/keyboard/__init__.py @@ -82,25 +82,6 @@ def reset_into_bootloader(): microcontroller.reset() -def is_tapped(matrix, key): - """Check if the key is tapped (press & release quickly)""" - n = len(matrix) - if n == 0: - n = matrix.wait(500 - matrix.ms(matrix.time() - matrix.get_keydown_time(key))) - target = key | 0x80 - if n == 1: - if target == matrix.view(0): - return True - else: - n = matrix.wait( - 200 - matrix.ms(matrix.time() - matrix.get_keydown_time(key)) - ) - if n == 2 and target == matrix.view(1): - # Fast typing: A down, B down, A up, B up - return True - - return False - class Device: def __init__(self, kbd): @@ -157,6 +138,8 @@ def __init__(self, keymap=KEYMAP, pairs=(), verbose=True): self.uid = microcontroller.cpu.uid * 2 self.usb_status = 0 self.leds = None + self.tap_delay = 500 + self.pair_delay = 10 self._current_conn = "" @@ -264,6 +247,25 @@ def stop_advertising(self): except Exception as e: print(e) + def is_tapped(self, matrix, key): + """Check if the key is tapped (press & release quickly)""" + n = len(matrix) + if n == 0: + n = matrix.wait(self.tap_delay - matrix.ms(matrix.time() - matrix.get_keydown_time(key))) + target = key | 0x80 + if n == 1: + if target == matrix.view(0): + return True + else: + n = matrix.wait( + 200 - matrix.ms(matrix.time() - matrix.get_keydown_time(key)) + ) + if n == 2 and target == matrix.view(1): + # Fast typing: A down, B down, A up, B up + return True + + return False + def change_bt(self, n): if self.ble.connected: for c in self.ble.connections: @@ -396,7 +398,7 @@ def run(self): key = matrix.view(0) if key < 0x80 and key in self.pair_keys: n = matrix.wait( - 10 - ms(matrix.time() - matrix.get_keydown_time(key)) + self.pair_delay - ms(matrix.time() - matrix.get_keydown_time(key)) ) if n >= 2: @@ -438,7 +440,7 @@ def run(self): self.press(*keycodes) elif kind < ACT_USAGE: # MODS_TAP - if is_tapped(matrix, key): + if self.is_tapped(matrix, key): log("TAP") keycode = action_code & 0xFF keys[key] = keycode @@ -462,7 +464,7 @@ def run(self): keycodes = mods_to_keycodes(mods) self.press(*keycodes) self.layer_mask |= mask - elif is_tapped(matrix, key): + elif self.is_tapped(matrix, key): log("TAP") keycode = action_code & 0xFF if keycode == OP_TAP_TOGGLE: From c73cb05d4e73863c7e79f3c223b1b10806b0c9da Mon Sep 17 00:00:00 2001 From: Micah Halter Date: Thu, 10 Sep 2020 08:36:06 -0400 Subject: [PATCH 08/12] Added adjustment ability to the fast typing threshold and renamed is_tapped to is_tap_key --- keyboard/__init__.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/keyboard/__init__.py b/keyboard/__init__.py index 2eed51e..b239224 100644 --- a/keyboard/__init__.py +++ b/keyboard/__init__.py @@ -139,6 +139,7 @@ def __init__(self, keymap=KEYMAP, pairs=(), verbose=True): self.usb_status = 0 self.leds = None self.tap_delay = 500 + self.fast_type_thresh = 200 self.pair_delay = 10 self._current_conn = "" @@ -247,7 +248,7 @@ def stop_advertising(self): except Exception as e: print(e) - def is_tapped(self, matrix, key): + def is_tap_key(self, matrix, key): """Check if the key is tapped (press & release quickly)""" n = len(matrix) if n == 0: @@ -258,7 +259,7 @@ def is_tapped(self, matrix, key): return True else: n = matrix.wait( - 200 - matrix.ms(matrix.time() - matrix.get_keydown_time(key)) + self.fast_type_thresh - matrix.ms(matrix.time() - matrix.get_keydown_time(key)) ) if n == 2 and target == matrix.view(1): # Fast typing: A down, B down, A up, B up @@ -440,7 +441,7 @@ def run(self): self.press(*keycodes) elif kind < ACT_USAGE: # MODS_TAP - if self.is_tapped(matrix, key): + if self.is_tap_key(matrix, key): log("TAP") keycode = action_code & 0xFF keys[key] = keycode @@ -464,7 +465,7 @@ def run(self): keycodes = mods_to_keycodes(mods) self.press(*keycodes) self.layer_mask |= mask - elif self.is_tapped(matrix, key): + elif self.is_tap_key(matrix, key): log("TAP") keycode = action_code & 0xFF if keycode == OP_TAP_TOGGLE: From deabec28bd8726f3e2ae76ee494b04f1a1547227 Mon Sep 17 00:00:00 2001 From: Micah Halter Date: Mon, 14 Sep 2020 14:17:30 -0400 Subject: [PATCH 09/12] Added RGB mode to light up on keypress --- code.py | 5 +++++ keyboard/__init__.py | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/code.py b/code.py index 3f468f0..b6b69cb 100644 --- a/code.py +++ b/code.py @@ -138,4 +138,9 @@ def pairs_handler(dev, n): # Pairs: J & K, U & I keyboard.pairs = [{35, 36}, {20, 19}] +# Set a default light color and enable light on keypress +keyboard.set_default_color(r=0xFF, g=0, b=0xFF) +keyboard.light_on_press = True + + keyboard.run() diff --git a/keyboard/__init__.py b/keyboard/__init__.py index b239224..3fab785 100644 --- a/keyboard/__init__.py +++ b/keyboard/__init__.py @@ -141,6 +141,8 @@ def __init__(self, keymap=KEYMAP, pairs=(), verbose=True): self.tap_delay = 500 self.fast_type_thresh = 200 self.pair_delay = 10 + self.default_color = [0xFF, 0xFF, 0xFF] + self.light_on_press = False self._current_conn = "" @@ -164,6 +166,9 @@ def __init__(self, keymap=KEYMAP, pairs=(), verbose=True): self.ble_hid = HID(ble_hid.devices) self.usb_hid = HID(usb_hid.devices) + def set_default_color(self, r=0xFF, g=0xFF, b=0xFF): + self.default_color = [r, g, b] + def update_current_conn(self): if usb_is_connected() and self.usb_status == 3: conn = "USB" @@ -379,6 +384,17 @@ def get(self): key = self.matrix.get() if key & 0x80 == 0: self.heatmap[key] += 1 + if self.light_on_press: + self.backlight.pixel(key, *self.default_color) + if key == 56: + self.backlight.pixel(61, *self.default_color) + self.backlight.pixel(62, *self.default_color) + elif self.light_on_press: + self.backlight.pixel(key & 0x7F, 0, 0, 0) + if (key & 0x7f) == 56: + self.backlight.pixel(61, 0, 0, 0) + self.backlight.pixel(62, 0, 0, 0) + self.backlight.update() return key def run(self): From ba8a128bca4cd236c57574d6c67484af7c1d5b61 Mon Sep 17 00:00:00 2001 From: Micah Halter Date: Mon, 14 Sep 2020 17:24:12 -0400 Subject: [PATCH 10/12] Cleaned up implementation and separated out RGB updating --- keyboard/__init__.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/keyboard/__init__.py b/keyboard/__init__.py index 3fab785..52a2502 100644 --- a/keyboard/__init__.py +++ b/keyboard/__init__.py @@ -380,21 +380,23 @@ def send_consumer(self, keycode): except Exception as e: print(e) + def update_rgb(self, event): + pressed = (event & 0x80) == 0 + key = event if pressed else (event & 0x7F) + color = self.default_color if pressed else [0, 0, 0] + if self.light_on_press: + self.backlight.pixel(key, *color) + if key == 56: + self.backlight.pixel(61, *color) + self.backlight.pixel(62, *color) + self.backlight.update() + + def get(self): key = self.matrix.get() if key & 0x80 == 0: self.heatmap[key] += 1 - if self.light_on_press: - self.backlight.pixel(key, *self.default_color) - if key == 56: - self.backlight.pixel(61, *self.default_color) - self.backlight.pixel(62, *self.default_color) - elif self.light_on_press: - self.backlight.pixel(key & 0x7F, 0, 0, 0) - if (key & 0x7f) == 56: - self.backlight.pixel(61, 0, 0, 0) - self.backlight.pixel(62, 0, 0, 0) - self.backlight.update() + self.update_rgb(key) return key def run(self): From 1c3a0d0945920c365f71b7b8e0215574d6609a3b Mon Sep 17 00:00:00 2001 From: Micah Halter Date: Mon, 14 Sep 2020 18:00:46 -0400 Subject: [PATCH 11/12] Added a few more RGB modes including a fill and a blackout mode --- keyboard/__init__.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/keyboard/__init__.py b/keyboard/__init__.py index 52a2502..c20574b 100644 --- a/keyboard/__init__.py +++ b/keyboard/__init__.py @@ -143,6 +143,11 @@ def __init__(self, keymap=KEYMAP, pairs=(), verbose=True): self.pair_delay = 10 self.default_color = [0xFF, 0xFF, 0xFF] self.light_on_press = False + self.RGB_OFF = 0 + self.RGB_TAP = 1 + self.RGB_FILL = 2 + self.RGB_EMPTY = 3 + self.rgb_mode = self.RGB_OFF self._current_conn = "" @@ -381,10 +386,16 @@ def send_consumer(self, keycode): print(e) def update_rgb(self, event): + if self.rgb_mode == self.RGB_EMPTY and not self.backlight.dev.any(): + self.backlight.on(*self.default_color) + return pressed = (event & 0x80) == 0 key = event if pressed else (event & 0x7F) - color = self.default_color if pressed else [0, 0, 0] - if self.light_on_press: + if self.rgb_mode == self.RGB_EMPTY: + color = [0, 0, 0] + else: + color = self.default_color if pressed else [0, 0, 0] + if self.rgb_mode == self.RGB_TAP or ((self.rgb_mode == self.RGB_FILL or self.rgb_mode == self.RGB_EMPTY) and pressed): self.backlight.pixel(key, *color) if key == 56: self.backlight.pixel(61, *color) From 1063dde48678664fc990466bd60d460e043d7352 Mon Sep 17 00:00:00 2001 From: Micah Halter Date: Mon, 14 Sep 2020 18:05:08 -0400 Subject: [PATCH 12/12] Simplified calculating keypresses to save on some latency --- keyboard/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/keyboard/__init__.py b/keyboard/__init__.py index c20574b..63c845e 100644 --- a/keyboard/__init__.py +++ b/keyboard/__init__.py @@ -385,11 +385,10 @@ def send_consumer(self, keycode): except Exception as e: print(e) - def update_rgb(self, event): + def update_rgb(self, event, pressed): if self.rgb_mode == self.RGB_EMPTY and not self.backlight.dev.any(): self.backlight.on(*self.default_color) return - pressed = (event & 0x80) == 0 key = event if pressed else (event & 0x7F) if self.rgb_mode == self.RGB_EMPTY: color = [0, 0, 0] @@ -405,9 +404,10 @@ def update_rgb(self, event): def get(self): key = self.matrix.get() - if key & 0x80 == 0: + pressed = key & 0x80 == 0 + if pressed: self.heatmap[key] += 1 - self.update_rgb(key) + self.update_rgb(key, pressed) return key def run(self):