power integrated fully, and guarded against i2c locks

power integrated fully, and guarded against i2c locks

led update

touchup
This commit is contained in:
Kyle Brown
2020-11-04 13:01:42 -08:00
parent 34b808efb7
commit 8a166ac91f
14 changed files with 164 additions and 64 deletions

View File

@@ -23,6 +23,7 @@ class KMKKeyboard(_KMKKeyboard):
uart_pin = board.P0_08 uart_pin = board.P0_08
rgb_pixel_pin = board.P0_06 rgb_pixel_pin = board.P0_06
i2c = board.I2C i2c = board.I2C
powersave_pin = board.P0_13
coord_mapping = [] coord_mapping = []
coord_mapping.extend(ic(0, x) for x in range(12)) coord_mapping.extend(ic(0, x) for x in range(12))

View File

@@ -43,3 +43,9 @@ class Extension:
def after_hid_send(self, keyboard): def after_hid_send(self, keyboard):
raise NotImplementedError raise NotImplementedError
def on_powersave_enable(self, keyboard):
raise NotImplementedError
def on_powersave_disable(self, keyboard):
raise NotImplementedError

View File

@@ -12,7 +12,9 @@ from storage import getmount
class BLE_Split(Extension): class BLE_Split(Extension):
'''Enables splitting keyboards wirelessly''' '''Enables splitting keyboards wirelessly'''
def __init__(self, split_flip=True, split_side=None, hid_type=HIDModes.BLE): def __init__(
self, split_flip=True, split_side=None, psave_ms=30, hid_type=HIDModes.BLE
):
self._is_target = True self._is_target = True
self._uart_buffer = [] self._uart_buffer = []
self.hid_type = hid_type self.hid_type = hid_type
@@ -27,6 +29,8 @@ class BLE_Split(Extension):
self._uart_connection = None self._uart_connection = None
self._advertisment = None self._advertisment = None
self._advertising = False self._advertising = False
self._psave_ms = psave_ms
self._psave_enable = False
def __repr__(self): def __repr__(self):
return f'BLE_SPLIT({self._to_dict()})' return f'BLE_SPLIT({self._to_dict()})'
@@ -90,6 +94,16 @@ class BLE_Split(Extension):
def after_hid_send(self, keyboard): def after_hid_send(self, keyboard):
return return
def on_powersave_enable(self, keyboard):
if self._uart_connection and not self._psave_enable:
self._uart_connection.connection_interval = self._psave_ms
self._psave_enable = True
def on_powersave_disable(self, keyboard):
if self._uart_connection and self._psave_enable:
self._uart_connection.connection_interval = 11.25
self._psave_enable = False
def _check_all_connections(self): def _check_all_connections(self):
'''Validates the correct number of BLE connections''' '''Validates the correct number of BLE connections'''
self._connection_count = len(self._ble.connections) self._connection_count = len(self._ble.connections)

View File

@@ -51,3 +51,9 @@ class International(Extension):
def after_hid_send(self, keyboard): def after_hid_send(self, keyboard):
return return
def on_powersave_enable(self, keyboard):
return
def on_powersave_disable(self, keyboard):
return

View File

@@ -82,7 +82,13 @@ class Layers(Extension):
def before_hid_send(self, keyboard): def before_hid_send(self, keyboard):
return return
def _after_hid_send(self, keyboard): def after_hid_send(self, keyboard):
return
def on_powersave_enable(self, keyboard):
return
def on_powersave_disable(self, keyboard):
return return
@staticmethod @staticmethod

View File

@@ -76,6 +76,12 @@ class Leader(Extension):
def after_hid_send(self, keyboard): def after_hid_send(self, keyboard):
return return
def on_powersave_enable(self, keyboard):
return
def on_powersave_disable(self, keyboard):
return
@staticmethod @staticmethod
def _compile_sequences(sequences): def _compile_sequences(sequences):

View File

@@ -90,6 +90,12 @@ class LED(Extension):
return keyboard return keyboard
def on_powersave_enable(self, keyboard):
return
def on_powersave_disable(self, keyboard):
return
def _init_effect(self): def _init_effect(self):
self._pos = 0 self._pos = 0
self._effect_init = False self._effect_init = False
@@ -154,13 +160,10 @@ class LED(Extension):
self._pos = (self._pos + self.animation_speed) % 256 self._pos = (self._pos + self.animation_speed) % 256
self.set_brightness(self._brightness) self.set_brightness(self._brightness)
return self
def effect_static(self): def effect_static(self):
self.set_brightness(self._brightness) self.set_brightness(self._brightness)
# Set animation mode to none to prevent cycles from being wasted # Set animation mode to none to prevent cycles from being wasted
self.animation_mode = None self.animation_mode = None
return self
def animate(self): def animate(self):
''' '''
@@ -184,30 +187,23 @@ class LED(Extension):
self.animation_mode = AnimationModes.STATIC self.animation_mode = AnimationModes.STATIC
self._enabled = not self._enabled self._enabled = not self._enabled
return state
def _key_led_inc(self, key, state, *args, **kwargs): def _key_led_inc(self, key, state, *args, **kwargs):
self.increase_brightness() self.increase_brightness()
return state
def _key_led_dec(self, key, state, *args, **kwargs): def _key_led_dec(self, key, state, *args, **kwargs):
self.decrease_brightness() self.decrease_brightness()
return state
def _key_led_ani(self, key, state, *args, **kwargs): def _key_led_ani(self, key, state, *args, **kwargs):
self.increase_ani() self.increase_ani()
return state
def _key_led_and(self, key, state, *args, **kwargs): def _key_led_and(self, key, state, *args, **kwargs):
self.decrease_ani() self.decrease_ani()
return state
def _key_led_mode_static(self, key, state, *args, **kwargs): def _key_led_mode_static(self, key, state, *args, **kwargs):
self._effect_init = True self._effect_init = True
self.animation_mode = AnimationModes.STATIC self.animation_mode = AnimationModes.STATIC
return state
def _key_led_mode_breathe(self, key, state, *args, **kwargs): def _key_led_mode_breathe(self, key, state, *args, **kwargs):
self._effect_init = True self._effect_init = True
self.animation_mode = AnimationModes.BREATHING self.animation_mode = AnimationModes.BREATHING
return state

View File

@@ -47,3 +47,9 @@ class MediaKeys(Extension):
def after_hid_send(self, keyboard): def after_hid_send(self, keyboard):
return return
def on_powersave_enable(self, keyboard):
return
def on_powersave_disable(self, keyboard):
return

View File

@@ -35,6 +35,12 @@ class ModTap(Extension):
def after_hid_send(self, keyboard): def after_hid_send(self, keyboard):
return return
def on_powersave_enable(self, keyboard):
return
def on_powersave_disable(self, keyboard):
return
def mt_pressed(self, key, state, *args, **kwargs): def mt_pressed(self, key, state, *args, **kwargs):
'''Sets the timer start and acts like a modifier otherwise''' '''Sets the timer start and acts like a modifier otherwise'''
state.keys_pressed.add(key.meta.mods) state.keys_pressed.add(key.meta.mods)

View File

@@ -8,23 +8,23 @@ from kmk.kmktime import sleep_ms, ticks_diff, ticks_ms
class Power(Extension): class Power(Extension):
def __init__(self, powersave_pin=None, enable=False, is_target=True): def __init__(self, powersave_pin=None):
self.enable = enable self.enable = False
self.powersave_pin = powersave_pin # Powersave pin board object self.powersave_pin = powersave_pin # Powersave pin board object
self.is_target = is_target
self._powersave_start = ticks_ms() self._powersave_start = ticks_ms()
self._usb_last_scan = ticks_ms() - 5000 self._usb_last_scan = ticks_ms() - 5000
self._psp = None # Powersave pin object self._psp = None # Powersave pin object
self._i2c = None self._i2c = None
self._loopcounter = 0
make_key( make_key(
names=('PS_TOG',), on_press=self._ps_tog, on_release=handler_passthrough names=('PS_TOG',), on_press=self._ps_tog, on_release=handler_passthrough
) )
make_key( make_key(
names=('PS_ENB',), on_press=self._ps_enable, on_release=handler_passthrough names=('PS_ON',), on_press=self._ps_enable, on_release=handler_passthrough
) )
make_key( make_key(
names=('PS_DIS',), on_press=self._ps_disable, on_release=handler_passthrough names=('PS_OFF',), on_press=self._ps_disable, on_release=handler_passthrough
) )
def __repr__(self): def __repr__(self):
@@ -34,7 +34,6 @@ class Power(Extension):
return f'''Power( return f'''Power(
enable={self.enable} enable={self.enable}
powersave_pin={self.powersave_pin} powersave_pin={self.powersave_pin}
is_target={self.is_target}
_powersave_start={self._powersave_start} _powersave_start={self._powersave_start}
_usb_last_scan={self._usb_last_scan} _usb_last_scan={self._usb_last_scan}
_psp={self._psp} ) _psp={self._psp} )
@@ -44,32 +43,47 @@ class Power(Extension):
return return
def on_runtime_disable(self, keyboard): def on_runtime_disable(self, keyboard):
self.disable_powersave self.disable_powersave()
def during_bootup(self, keyboard): def during_bootup(self, keyboard):
self._detect_i2c() self._i2c_scan()
self.enable = not bool(self.usb_scan) return
def before_matrix_scan(self, keyboard): def before_matrix_scan(self, keyboard):
if self.usb_rescan_timer(): return
self.enable = not bool(self.usb_scan)
def after_matrix_scan(self, keyboard, matrix_update): def after_matrix_scan(self, keyboard, matrix_update):
if matrix_update: if matrix_update:
self.psave_time_reset() self.psave_time_reset()
else: return
self.psleep()
def before_hid_send(self, keyboard): def before_hid_send(self, keyboard):
return return
def after_hid_send(self, keyboard): def after_hid_send(self, keyboard):
if self.enable:
self.psleep()
def on_powersave_enable(self, keyboard):
'''Gives 10 cycles to allow other extentions to clean up before powersave'''
if keyboard._trigger_powersave_enable:
if self._loopcounter > 10:
self._loopcounter += 1
return
self._loopcounter = 0
keyboard._trigger_powersave_enable = False
self.enable_powersave(keyboard)
return return
def enable_powersave(self): def on_powersave_disable(self, keyboard):
keyboard._trigger_powersave_disable = False
self.disable_powersave()
return
def enable_powersave(self, keyboard):
'''Enables power saving features''' '''Enables power saving features'''
print('Psave True') print('Psave True')
if self.powersave_pin: if keyboard.i2c_deinit_count >= self._i2c and self.powersave_pin:
# Allows power save to prevent RGB drain. # Allows power save to prevent RGB drain.
# Example here https://docs.nicekeyboards.com/#/nice!nano/pinout_schematic # Example here https://docs.nicekeyboards.com/#/nice!nano/pinout_schematic
@@ -98,18 +112,23 @@ class Power(Extension):
''' '''
Sleeps longer and longer to save power the more time in between updates. Sleeps longer and longer to save power the more time in between updates.
''' '''
if ticks_diff(ticks_ms(), self._powersave_start) <= 20000 and self.is_target: if ticks_diff(ticks_ms(), self._powersave_start) <= 60000:
sleep_ms(1)
elif ticks_diff(ticks_ms(), self._powersave_start) <= 40000 and self.is_target:
sleep_ms(4)
elif ticks_diff(ticks_ms(), self._powersave_start) <= 60000 and self.is_target:
sleep_ms(8) sleep_ms(8)
elif ticks_diff(ticks_ms(), self._powersave_start) >= 240000: elif ticks_diff(ticks_ms(), self._powersave_start) >= 240000:
sleep_ms(250) sleep_ms(180)
def psave_time_reset(self): def psave_time_reset(self):
self._powersave_start = ticks_ms() self._powersave_start = ticks_ms()
def _i2c_scan(self):
i2c = board.I2C()
while not i2c.try_lock():
pass
try:
self._i2c = len(i2c.scan())
finally:
i2c.unlock()
def usb_rescan_timer(self): def usb_rescan_timer(self):
return bool(ticks_diff(ticks_ms(), self._usb_last_scan) > 5000) return bool(ticks_diff(ticks_ms(), self._usb_last_scan) > 5000)
@@ -121,24 +140,16 @@ class Power(Extension):
# https://github.com/adafruit/circuitpython/pull/3513 # https://github.com/adafruit/circuitpython/pull/3513
return True return True
def _detect_i2c(self): def _ps_tog(self, key, keyboard, *args, **kwargs):
'''Detects i2c devices and disables cutting power to them'''
# TODO Figure out how this could deinit/reinit instead.
self._i2c = board.I2C()
devices = self._i2c.scan()
if devices != []:
self.powersave_pin = None
def _ps_tog(self):
if self.enable: if self.enable:
self.enable_powersave() keyboard._trigger_powersave_disable = True
else: else:
self.disable_powersave() keyboard._trigger_powersave_enable = True
def _ps_enable(self): def _ps_enable(self, key, keyboard, *args, **kwargs):
if not self.enable: if not self.enable:
self.enable_powersave() keyboard._trigger_powersave_enable = True
def _ps_disable(self): def _ps_disable(self, key, keyboard, *args, **kwargs):
if self.enable: if self.enable:
self.disable_powersave() keyboard._trigger_powersave_disable = True

View File

@@ -168,6 +168,12 @@ class RGB(Extension):
return keyboard return keyboard
def on_powersave_enable(self, keyboard):
return
def on_powersave_disable(self, keyboard):
self._do_update()
@staticmethod @staticmethod
def time_ms(): def time_ms():
return int(time.monotonic() * 1000) return int(time.monotonic() * 1000)

View File

@@ -96,6 +96,12 @@ class Split(Extension):
def after_hid_send(self, keyboard): def after_hid_send(self, keyboard):
return return
def on_powersave_enable(self, keyboard):
return
def on_powersave_disable(self, keyboard):
return
def _send(self, update): def _send(self, update):
if self.split_target_left: if self.split_target_left:
update[1] += self.split_offset update[1] += self.split_offset

View File

@@ -1,3 +1,5 @@
import gc
from kmk.consts import KMK_RELEASE, UnicodeMode from kmk.consts import KMK_RELEASE, UnicodeMode
from kmk.hid import BLEHID, USBHID, AbstractHID, HIDModes from kmk.hid import BLEHID, USBHID, AbstractHID, HIDModes
from kmk.keys import KC from kmk.keys import KC
@@ -39,6 +41,9 @@ class KMKKeyboard:
state_changed = False state_changed = False
_old_timeouts_len = None _old_timeouts_len = None
_new_timeouts_len = None _new_timeouts_len = None
_trigger_powersave_enable = False
_trigger_powersave_disable = False
i2c_deinit_count = 0
# this should almost always be PREpended to, replaces # this should almost always be PREpended to, replaces
# former use of reversed_active_layers which had pointless # former use of reversed_active_layers which had pointless
@@ -345,6 +350,10 @@ class KMKKeyboard:
return self return self
# Only one GC to allow for extentions to have room.
# There are random memory allocations without this
gc.collect()
def go(self, hid_type=HIDModes.USB, **kwargs): def go(self, hid_type=HIDModes.USB, **kwargs):
self.hid_type = hid_type self.hid_type = hid_type
@@ -372,7 +381,7 @@ class KMKKeyboard:
try: try:
self._handle_matrix_report(ext.before_matrix_scan(self)) self._handle_matrix_report(ext.before_matrix_scan(self))
except Exception as err: except Exception as err:
print('Failed to run pre matrix function: ', err) print('Failed to run pre matrix function: ', err, ext)
self.matrix_update = self.matrix.scan_for_changes() self.matrix_update = self.matrix.scan_for_changes()
@@ -384,7 +393,7 @@ class KMKKeyboard:
if self._matrix_modify is not None: if self._matrix_modify is not None:
self.matrix_update = self._matrix_modify self.matrix_update = self._matrix_modify
except Exception as err: except Exception as err:
print('Failed to run post matrix function: ', err) print('Failed to run post matrix function: ', err, ext)
self._handle_matrix_report(self.matrix_update) self._handle_matrix_report(self.matrix_update)
self.matrix_update = None self.matrix_update = None
@@ -393,7 +402,7 @@ class KMKKeyboard:
try: try:
ext.before_hid_send(self) ext.before_hid_send(self)
except Exception as err: except Exception as err:
print('Failed to run pre hid function: ', err) print('Failed to run pre hid function: ', err, ext)
if self.hid_pending: if self.hid_pending:
self._send_hid() self._send_hid()
@@ -411,7 +420,21 @@ class KMKKeyboard:
try: try:
ext.after_hid_send(self) ext.after_hid_send(self)
except Exception as err: except Exception as err:
print('Failed to run post hid function: ', err) print('Failed to run post hid function: ', err, ext)
if self._trigger_powersave_enable:
for ext in self.extensions:
try:
ext.on_powersave_enable(self)
except Exception as err:
print('Failed to run post hid function: ', err, ext)
if self._trigger_powersave_disable:
for ext in self.extensions:
try:
ext.on_powersave_disable(self)
except Exception as err:
print('Failed to run post hid function: ', err, ext)
if self.state_changed: if self.state_changed:
self._print_debug_cycle() self._print_debug_cycle()

View File

@@ -7,6 +7,7 @@ import terminalio
from adafruit_display_text import label from adafruit_display_text import label
from kb import KMKKeyboard from kb import KMKKeyboard
from kmk.extensions.ble_split import BLE_Split from kmk.extensions.ble_split import BLE_Split
from kmk.extensions.power import Power
from kmk.extensions.rgb import RGB from kmk.extensions.rgb import RGB
from kmk.handlers.sequences import send_string, simple_key_sequence from kmk.handlers.sequences import send_string, simple_key_sequence
from kmk.hid import HIDModes from kmk.hid import HIDModes
@@ -26,19 +27,25 @@ SUPER_L = KC.LM(4, KC.LGUI)
keyboard.tap_time = 320 keyboard.tap_time = 320
keyboard.debug_enabled = False keyboard.debug_enabled = False
# TODO Get this out of here rgb_ext = RGB(pixel_pin=keyboard.rgb_pixel_pin, num_pixels=27, val_limit=100, hue_default=190, sat_default=100, val_default=5)
rgb_pixel_pin = board.P0_06
rgb_ext = RGB(pixel_pin=rgb_pixel_pin, num_pixels=27, val_limit=100, hue_default=190, sat_default=100, val_default=5)
split = BLE_Split(split_side=split_side) split = BLE_Split(split_side=split_side)
keyboard.extensions = [split, rgb_ext] power = Power(powersave_pin=keyboard.powersave_pin)
keyboard.extensions = [split, rgb_ext, power]
enable_oled = False
if enable_oled:
displayio.release_displays() displayio.release_displays()
i2c = board.I2C() i2c = board.I2C()
display_bus = displayio.I2CDisplay(i2c, device_address=0x3c) display_bus = displayio.I2CDisplay(i2c, device_address=0x3c)
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=32) display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=32)
splash = displayio.Group(max_size=10) splash = displayio.Group(max_size=10)
display.show(splash) display.show(splash)
else:
displayio.release_displays()
keyboard.i2c_deinit_count += 1
keyboard.keymap = [ keyboard.keymap = [
# DVORAK # DVORAK
@@ -94,7 +101,7 @@ keyboard.keymap = [
# #
[ [
# RAISE1 # RAISE1
_______, _______, _______, _______, _______, _______, XXXXXXX, XXXXXXX, KC.N7, KC.N8, KC.N9, KC.DEL, \ _______, _______, _______, _______, _______, _______, KC.PS_TOG, XXXXXXX, KC.N7, KC.N8, KC.N9, KC.DEL, \
_______, _______, _______, _______, _______, _______, XXXXXXX, XXXXXXX, KC.N4, KC.N5, KC.N6, KC.BSLS, \ _______, _______, _______, _______, _______, _______, XXXXXXX, XXXXXXX, KC.N4, KC.N5, KC.N6, KC.BSLS, \
_______, _______, _______, _______, _______, _______, XXXXXXX, XXXXXXX, KC.N1, KC.N2, KC.N3, KC.MINS, \ _______, _______, _______, _______, _______, _______, XXXXXXX, XXXXXXX, KC.N1, KC.N2, KC.N3, KC.MINS, \
_______, _______, _______, _______, KC.EQL, KC.N0, _______, _______, _______, _______, KC.EQL, KC.N0,