diff --git a/.gitignore b/.gitignore index 3e4a0f3..5e7a62f 100644 --- a/.gitignore +++ b/.gitignore @@ -112,3 +112,6 @@ venv.bak/ # Pycharms cruft .idea + +# Personal dev scripts +.scripts diff --git a/docs/split_keyboards.md b/docs/split_keyboards.md new file mode 100644 index 0000000..67b421f --- /dev/null +++ b/docs/split_keyboards.md @@ -0,0 +1,38 @@ +# Split Keyboards +Split keyboards are mostly the same as unsplit and very easy to adapt a keymap for. Currently +UART is supported, though other modes will come later such as Bluetooth and i2c. + +Useful config options: +```python +keyboard.split_flip = True # If your boards are identical but one is flipped, this option is for you +keyboard.split_offsets = [6, 6, 6, 6] # This is the how many keys are on each column on the "Master" half +``` + +## Master Half +Choosing the master half can be done one of 2 ways. You can set this true or false if you only pleg in on one side. +```python +keyboard.split_master_left = True +``` + +## EE HANDS +If you want to plug in on either side, it can be done fairly easily but requires setup. + +On each half of your keyboard make a file called kmk_side.py and add one of these lines to the file +depending on where each piece is physically located. +```python +split_side = "Left" +OR +split_side = "Right" +``` + +and then in your keymap, add the line +```python +from kmk_side import split_side +``` + +# UART +To enable uart it's as simple as adding this line, of course changing the pin +```python +keyboard.split_type = "UART" +keyboard.uart = keyboard.init_uart(tx=board.SCL) +``` diff --git a/kmk/firmware.py b/kmk/firmware.py index 62ba578..aaf8651 100644 --- a/kmk/firmware.py +++ b/kmk/firmware.py @@ -34,8 +34,10 @@ import kmk.internal_state # isort:skip # Thanks for sticking around. Now let's do real work, starting below +import busio import gc +import supervisor from kmk.consts import LeaderMode, UnicodeModes from kmk.hid import USB_HID from kmk.internal_state import InternalState @@ -59,6 +61,15 @@ class Firmware: hid_helper = USB_HID + split_offsets = () + split_flip = False + split_side = None + split_type = None + split_master_left = True + is_master = None + uart = None + uart_flip = True + def __init__(self): self._state = InternalState(self) @@ -75,12 +86,78 @@ class Firmware: self._state.remove_key(key) self._send_hid() + 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 _receive_from_slave(self): + if self.uart is not None and self.uart.in_waiting > 0: + update = bytearray(self.uart.read(3)) + # Built in debug mode switch + if update == b'DEB': + # TODO Pretty up output + print(self.uart.readline()) + return None + return update + + return None + + 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 _master_half(self): + return supervisor.runtime.serial_connected + + def init_uart(self, tx=None, rx=None, timeout=20): + if self._master_half(): + # If running with one wire, only receive on master + if rx is None or self.uart_flip: + return busio.UART(tx=rx, rx=None, timeout=timeout) + else: + return busio.UART(tx=tx, rx=rx, timeout=timeout) + + else: + return busio.UART(tx=tx, rx=rx, timeout=timeout) + def go(self): assert self.keymap, 'must define a keymap with at least one row' 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' + self.is_master == self._master_half() + + if self.split_flip and not self._master_half(): + self.col_pins = list(reversed(self.col_pins)) + + if self.split_side == "Left": + self.split_master_left = self.is_master + elif self.split_side == "Right": + self.split_master_left = not self.is_master + self.matrix = MatrixScanner( cols=self.col_pins, rows=self.row_pins, @@ -95,17 +172,23 @@ class Firmware: print("Firin' lazers. Keyboard is booted.") while True: - matrix_report = self.matrix.scan_for_changes() state_changed = False - if matrix_report is not None: - self._state.matrix_changed( - matrix_report[0], - matrix_report[1], - matrix_report[2], - ) + if self.split_type is not None and self._master_half: + update = self._receive_from_slave() + if update is not None: + self._handle_matrix_report(update) + state_changed = True - state_changed = True + update = self.matrix.scan_for_changes() + + if update is not None: + if self._master_half(): + self._handle_matrix_report(update) + state_changed = True + else: + # This keyboard is a slave, and needs to send data to master + self._send_to_master(update) if self._state.hid_pending: self._send_hid() @@ -118,12 +201,14 @@ class Firmware: state_changed = True if self._state.macros_pending: + # Blindly assume macros are going to change state, which is almost + # always a safe assumption + state_changed = True for macro in self._state.macros_pending: for key in macro(self): self._send_key(key) - self._state.resolve_macro() - state_changed = True + self._state.resolve_macro() if self.debug_enabled and state_changed: print('New State: {}'.format(self._state._to_dict())) diff --git a/user_keymaps/kdb424/klanck.py b/user_keymaps/kdb424/klanck.py index 8d3c9f4..fe593ab 100644 --- a/user_keymaps/kdb424/klanck.py +++ b/user_keymaps/kdb424/klanck.py @@ -16,9 +16,9 @@ keyboard.diode_orientation = DiodeOrientation.COLUMNS # ------------------User level config variables --------------------------------------- keyboard.unicode_mode = UnicodeModes.LINUX -keyboard.tap_time = 900 +keyboard.tap_time = 350 keyboard.leader_timeout = 2000 -keyboard.debug_enabled = True +keyboard.debug_enabled = False emoticons = compile_unicode_string_sequences({ # Emoticons, but fancier diff --git a/user_keymaps/kdb424/levinson_m4.py b/user_keymaps/kdb424/levinson_m4.py new file mode 100644 index 0000000..b20f8ce --- /dev/null +++ b/user_keymaps/kdb424/levinson_m4.py @@ -0,0 +1,99 @@ +import board +import busio + +from kmk.consts import DiodeOrientation, LeaderMode, UnicodeModes +from kmk.keycodes import KC +from kmk.keycodes import generate_leader_dictionary_seq as glds +from kmk.macros.simple import send_string +from kmk.macros.unicode import compile_unicode_string_sequences +from kmk.mcus.circuitpython_samd51 import Firmware +from kmk.pins import Pin as P +from kmk.types import AttrDict + +keyboard = Firmware() + +keyboard.col_pins = (P.D10, P.D9, P.D7, P.D5, P.A4, P.A5) +keyboard.row_pins = (P.A0, P.A1, P.A2, P.A3) +keyboard.diode_orientation = DiodeOrientation.COLUMNS + +keyboard.split_type = "UART" +keyboard.split_flip = True +keyboard.split_offsets = [6, 6, 6, 6] +keyboard.uart_flip = False +keyboard.uart = keyboard.init_uart(tx=board.TX, rx=board.RX) + +# ------------------User level config variables --------------------------------------- +keyboard.leader_mode = LeaderMode.TIMEOUT +keyboard.unicode_mode = UnicodeModes.LINUX +keyboard.tap_time = 150 +keyboard.leader_timeout = 2000 +keyboard.debug_enabled = True + +emoticons = compile_unicode_string_sequences({ + # Emoticons, but fancier + 'ANGRY_TABLE_FLIP': r'(ノಠ痊ಠ)ノ彡┻━┻', + 'CHEER': r'+。:.゚ヽ(´∀。)ノ゚.:。+゚゚+。:.゚ヽ(*´∀)ノ゚.:。+゚', + 'TABLE_FLIP': r'(╯°□°)╯︵ ┻━┻', + 'WAT': r'⊙.☉', + 'FF': r'凸(゚Д゚#)', + 'F': r'( ̄^ ̄)凸', + 'MEH': r'╮( ̄_ ̄)╭', + 'YAY': r'o(^▽^)o', +}) + +# ---------------------- Leader Key Macros -------------------------------------------- + +keyboard.leader_dictionary = { + glds('flip'): emoticons.ANGRY_TABLE_FLIP, + glds('cheer'): emoticons.CHEER, + glds('wat'): emoticons.WAT, + glds('ff'): emoticons.FF, + glds('f'): emoticons.F, + glds('meh'): emoticons.MEH, + glds('yay'): emoticons.YAY, +} + +WPM = send_string("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Bibendum arcu vitae elementum curabitur vitae nunc sed. Facilisis sed odio morbi quis.") + +# ---------------------- Keymap --------------------------------------------------------- + +keyboard.keymap = [ + [ + # Default + [KC.GESC, KC.QUOTE, KC.COMMA, KC.DOT, KC.P, KC.Y, KC.F, KC.G, KC.C, KC.R, KC.L, KC.BKSP], + [KC.TAB, KC.A, KC.O, KC.E, KC.U, KC.I, KC.D, KC.H, KC.T, KC.N, KC.S, KC.ENT], + [KC.LSFT, KC.SCLN, KC.Q, KC.J, KC.K, KC.X, KC.B, KC.M, KC.W, KC.V, KC.Z, KC.SLSH], + [KC.LCTRL, KC.LGUI, KC.LALT, KC.LEAD, KC.MO(2), KC.LT(3, KC.SPC), KC.LT(3, KC.SPC), KC.MO(4), KC.LEFT, KC.DOWN, KC.UP, KC.RIGHT], + ], + [ + # Gaming + [KC.TAB, KC.QUOT, KC.COMM, KC.DOT, KC.P, KC.Y, KC.F, KC.G, KC.C, KC.R, KC.L, KC.BKSP], + [KC.ESC, KC.A, KC.O, KC.E, KC.U, KC.I, KC.D, KC.H, KC.T, KC.N, KC.S, KC.ENT], + [KC.LSFT, KC.SCLN, KC.Q, KC.J, KC.K, KC.X, KC.B, KC.M, KC.W, KC.V, KC.Z, KC.SLSH], + [KC.LCTRL, KC.LGUI, KC.LALT, KC.F1, KC.F2, KC.SPC, KC.SPC, KC.MO(4), KC.LEFT, KC.DOWN, KC.UP, KC.RIGHT], + ], + [ + # Raise1 + [KC.TILD, KC.EXLM, KC.AT, KC.HASH, KC.DLR, KC.PERC, KC.CIRC, KC.AMPR, KC.ASTR, KC.LPRN, KC.RPRN, KC.DEL], + [KC.TRNS, KC.NO, KC.NO, KC.NO, KC.NO, KC.NO, KC.NO, KC.NO, KC.NO, KC.LBRC, KC.RBRC, KC.BSLS], + [KC.TRNS, KC.NO, KC.NO, KC.NO, KC.NO, KC.NO, KC.NO, KC.NO, KC.INS, KC.PGDN, KC.PGUP, KC.MINS], + [KC.RESET, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.NO, KC.NO, KC.EQL, KC.HOME, KC.VOLD, KC.VOLU, KC.END], + ], + [ + # Raise2 + [KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.N7, KC.N8, KC.N9, KC.BKSP], + [KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.N4, KC.N5, KC.N6, KC.NO], + [KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.N1, KC.N2, KC.N3, KC.NO], + [KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.N0, KC.N0, KC.PDOT, KC.ENT], + ], + [ + # Raise3 + [WPM, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.F10, KC.F11, KC.F12, KC.LSHIFT(KC.INS)], + [KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.F7, KC.F8, KC.F9, KC.NO], + [KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.F4, KC.F5, KC.F6, KC.NO], + [KC.DF(0), KC.DF(1), KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.TRNS, KC.F1, KC.F2, KC.F3, KC.NO], + ], +] + +if __name__ == '__main__': + keyboard.go() diff --git a/user_keymaps/klardotsh/klarank_featherm4.py b/user_keymaps/klardotsh/klarank_featherm4.py index d8cf199..cff7814 100644 --- a/user_keymaps/klardotsh/klarank_featherm4.py +++ b/user_keymaps/klardotsh/klarank_featherm4.py @@ -9,6 +9,7 @@ keyboard = Firmware() keyboard.debug_enabled = True keyboard.unicode_mode = UnicodeModes.LINUX +keyboard.tap_time = 750 emoticons = cuss({ # Emojis