diff --git a/kmk/firmware_slave.py b/kmk/firmware_slave.py new file mode 100644 index 0000000..cadbc11 --- /dev/null +++ b/kmk/firmware_slave.py @@ -0,0 +1,107 @@ +import kmk.matrix # isort:skip + +# Thanks for sticking around. Now let's do real work, starting below + +import busio +import gc + +from kmk.internal_state_slave import InternalState +from kmk.matrix import MatrixScanner + + +class Firmware: + debug_enabled = False + + row_pins = None + col_pins = None + diode_orientation = None + + extra_data_pin = None + split_offsets = () + split_flip = False + split_side = None + split_type = None + split_master_left = True + is_master = False + uart = None + uart_flip = True + uart_pin = None + + def __init__(self): + self._state = InternalState(self) + + def _handle_matrix_report(self, update=None): + ''' + Bulk processing of update code for each cycle + :param update: + ''' + if update is not None: + self._state.matrix_changed( + update[0], + update[1], + update[2], + ) + + def _send_to_master(self, update): + if self.split_master_left: + update[1] += self.split_offsets[update[0]] + else: + update[1] -= self.split_offsets[update[0]] + if self.uart is not None: + self.uart.write(update) + + def _send_debug(self, message): + ''' + Prepends DEB and appends a newline to allow debug messages to + be detected and handled differently than typical keypresses. + :param message: Debug message + ''' + if self.uart is not None: + self.uart.write('DEB') + self.uart.write(message, '\n') + + def init_uart(self, pin, timeout=20): + return busio.UART(tx=pin, rx=None, timeout=timeout) + + def go(self): + assert self.row_pins, 'no GPIO pins defined for matrix rows' + assert self.col_pins, 'no GPIO pins defined for matrix columns' + assert self.diode_orientation is not None, 'diode orientation must be defined' + + if self.split_flip: + self.col_pins = list(reversed(self.col_pins)) + + # Split keyboard Init + if self.split_side == "Left": + self.split_master_left = True + elif self.split_side == "Right": + self.split_master_left = False + + if self.uart_pin is not None: + self.uart = self.init_uart(self.uart_pin) + + self.matrix = MatrixScanner( + cols=self.col_pins, + rows=self.row_pins, + diode_orientation=self.diode_orientation, + rollover_cols_every_rows=getattr(self, 'rollover_cols_every_rows', None), + swap_indicies=getattr(self, 'swap_indicies', None), + ) + + if self.debug_enabled: + print("Firin' lazers. Keyboard is booted.") + + while True: + state_changed = False + + update = self.matrix.scan_for_changes() + + if update is not None: + # This keyboard is a slave, and needs to send data to master + self._send_to_master(update) + + + if self.debug_enabled and state_changed: + print('New State: {}'.format(self._state._to_dict())) + + gc.collect() diff --git a/kmk/internal_state_slave.py b/kmk/internal_state_slave.py new file mode 100644 index 0000000..4dd4846 --- /dev/null +++ b/kmk/internal_state_slave.py @@ -0,0 +1,76 @@ +from kmk.util import intify_coordinate + + +class InternalState: + keys_pressed = set() + coord_keys_pressed = {} + + def __init__(self, config): + self.config = config + + def __repr__(self): + return 'InternalState({})'.format(self._to_dict()) + + def _to_dict(self): + ret = { + 'keys_pressed': self.keys_pressed, + } + + return ret + + def _find_key_in_map(self, row, col): + # Later-added layers have priority. Sift through the layers + # in reverse order until we find a valid keycode object + for layer in self.reversed_active_layers: + layer_key = self.config.keymap[layer][row][col] + + if self.config.debug_enabled: + print('Resolved key: {}'.format(layer_key)) + + return layer_key + + def matrix_changed(self, row, col, is_pressed): + if self.config.debug_enabled: + print('Matrix changed (col, row, pressed?): {}, {}, {}'.format( + col, row, is_pressed, + )) + + int_coord = intify_coordinate(row, col) + kc_changed = self._find_key_in_map(row, col) + + if kc_changed is None: + print('No key accessible for col, row: {}, {}'.format(row, col)) + return self + + return self.process_key(kc_changed, is_pressed, int_coord, (row, col)) + + def process_key(self, key, is_pressed, coord_int=None, coord_raw=None): + if self.tapping: + self._process_tap_dance(key, is_pressed) + else: + if is_pressed: + key._on_press(self, coord_int, coord_raw) + else: + key._on_release(self, coord_int, coord_raw) + + if self.config.leader_mode % 2 == 1: + self._process_leader_mode() + + return self + + def remove_key(self, keycode): + self.keys_pressed.discard(keycode) + return self.process_key(keycode, False) + + def add_key(self, keycode): + self.keys_pressed.add(keycode) + return self.process_key(keycode, True) + + def tap_key(self, keycode): + self.add_key(keycode) + # On the next cycle, we'll remove the key. + self.set_timeout(False, lambda: self.remove_key(keycode)) + + return self + + diff --git a/kmk/mcus/circuitpython_samd21.py b/kmk/mcus/circuitpython_samd21.py new file mode 100644 index 0000000..5a4a69f --- /dev/null +++ b/kmk/mcus/circuitpython_samd21.py @@ -0,0 +1,5 @@ +from kmk.firmware_slave import Firmware as _Firmware + + +class Firmware(_Firmware): + hid_helper = None diff --git a/user_keymaps/kdb424/nyquist_converter_slave.py b/user_keymaps/kdb424/nyquist_converter_slave.py new file mode 100644 index 0000000..6b5d808 --- /dev/null +++ b/user_keymaps/kdb424/nyquist_converter_slave.py @@ -0,0 +1,26 @@ +import board + +from kmk.consts import DiodeOrientation +from kmk.mcus.circuitpython_samd21 import Firmware +from kmk.pins import Pin as P + +keyboard = Firmware() + +keyboard.col_pins = (P.RX, P.A1, P.A2, P.A3, P.A4, P.A5) +keyboard.row_pins = (P.D13, P.D11, P.D10, P.D9, P.D7) +keyboard.diode_orientation = DiodeOrientation.COLUMNS + +keyboard.split_type = "UART" +keyboard.split_flip = True +keyboard.split_offsets = [6, 6, 6, 6, 6] +keyboard.uart_pin = board.SDA +keyboard.extra_data_pin = board.SCL + +# ------------------User level config variables --------------------------------------- +keyboard.debug_enabled = True + +# ---------------------- Keymap --------------------------------------------------------- + + +if __name__ == '__main__': + keyboard.go()