Merge master and reconcile tapdance with splits

This took a bit of brain surgery to firmware.py that deserves its own
commit message beyond the default merge commit message - tl;dr though,
it appears to work fine.
This commit is contained in:
Josh Klar 2018-11-05 21:14:20 -08:00
commit 94ac6d0cbd
No known key found for this signature in database
GPG Key ID: 220F99BD7DB7A99E
6 changed files with 238 additions and 12 deletions

3
.gitignore vendored
View File

@ -112,3 +112,6 @@ venv.bak/
# Pycharms cruft # Pycharms cruft
.idea .idea
# Personal dev scripts
.scripts

38
docs/split_keyboards.md Normal file
View File

@ -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)
```

View File

@ -34,8 +34,10 @@ import kmk.internal_state # isort:skip
# Thanks for sticking around. Now let's do real work, starting below # Thanks for sticking around. Now let's do real work, starting below
import busio
import gc import gc
import supervisor
from kmk.consts import LeaderMode, UnicodeModes from kmk.consts import LeaderMode, UnicodeModes
from kmk.hid import USB_HID from kmk.hid import USB_HID
from kmk.internal_state import InternalState from kmk.internal_state import InternalState
@ -59,6 +61,15 @@ class Firmware:
hid_helper = USB_HID 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): def __init__(self):
self._state = InternalState(self) self._state = InternalState(self)
@ -75,12 +86,78 @@ class Firmware:
self._state.remove_key(key) self._state.remove_key(key)
self._send_hid() 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): def go(self):
assert self.keymap, 'must define a keymap with at least one row' 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.row_pins, 'no GPIO pins defined for matrix rows'
assert self.col_pins, 'no GPIO pins defined for matrix columns' assert self.col_pins, 'no GPIO pins defined for matrix columns'
assert self.diode_orientation is not None, 'diode orientation must be defined' 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( self.matrix = MatrixScanner(
cols=self.col_pins, cols=self.col_pins,
rows=self.row_pins, rows=self.row_pins,
@ -95,18 +172,24 @@ class Firmware:
print("Firin' lazers. Keyboard is booted.") print("Firin' lazers. Keyboard is booted.")
while True: while True:
matrix_report = self.matrix.scan_for_changes()
state_changed = False state_changed = False
if matrix_report is not None: if self.split_type is not None and self._master_half:
self._state.matrix_changed( update = self._receive_from_slave()
matrix_report[0], if update is not None:
matrix_report[1], self._handle_matrix_report(update)
matrix_report[2],
)
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: if self._state.hid_pending:
self._send_hid() self._send_hid()
@ -118,12 +201,14 @@ class Firmware:
state_changed = True state_changed = True
if self._state.macros_pending: 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 macro in self._state.macros_pending:
for key in macro(self): for key in macro(self):
self._send_key(key) self._send_key(key)
self._state.resolve_macro() self._state.resolve_macro()
state_changed = True
if self.debug_enabled and state_changed: if self.debug_enabled and state_changed:
print('New State: {}'.format(self._state._to_dict())) print('New State: {}'.format(self._state._to_dict()))

View File

@ -16,9 +16,9 @@ keyboard.diode_orientation = DiodeOrientation.COLUMNS
# ------------------User level config variables --------------------------------------- # ------------------User level config variables ---------------------------------------
keyboard.unicode_mode = UnicodeModes.LINUX keyboard.unicode_mode = UnicodeModes.LINUX
keyboard.tap_time = 900 keyboard.tap_time = 350
keyboard.leader_timeout = 2000 keyboard.leader_timeout = 2000
keyboard.debug_enabled = True keyboard.debug_enabled = False
emoticons = compile_unicode_string_sequences({ emoticons = compile_unicode_string_sequences({
# Emoticons, but fancier # Emoticons, but fancier

View File

@ -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()

View File

@ -9,6 +9,7 @@ keyboard = Firmware()
keyboard.debug_enabled = True keyboard.debug_enabled = True
keyboard.unicode_mode = UnicodeModes.LINUX keyboard.unicode_mode = UnicodeModes.LINUX
keyboard.tap_time = 750
emoticons = cuss({ emoticons = cuss({
# Emojis # Emojis