Encoder module! (#211)
* added atreus62 board * Uploaded module for encoder support * Update README.md Co-authored-by: Ryan Pullen <rpullen@martinuav.com>
This commit is contained in:
parent
eb5756f530
commit
15918db7ac
16
boards/atreus62/README.md
Normal file
16
boards/atreus62/README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Atreus62
|
||||||
|
|
||||||
|
![Atreus62](https://assets.bigcartel.com/product_images/189335282/BIlqCtd.jpg?auto=format&fit=max&w=1200)
|
||||||
|
|
||||||
|
Atreus62 is a 60% column staggered keyboard pinky stagger
|
||||||
|
|
||||||
|
kb.py is designed to work with the Teensy 4.1
|
||||||
|
|
||||||
|
Retailers (USA)
|
||||||
|
[Atreus62](https://shop.profetkeyboards.com/product/atreus62-keyboard)
|
||||||
|
|
||||||
|
Extentions enabled by default
|
||||||
|
- [Layers](https://github.com/KMKfw/kmk_firmware/tree/master/docs/layers.md) Need more keys than switches? Use layers.
|
||||||
|
- [RGB](https://github.com/KMKfw/kmk_firmware/tree/master/docs/rgb.md) Light it up
|
||||||
|
- [Encoder](https://github.com/KMKfw/kmk_firmware/tree/master/docs/encoder.md) Twist control for all the things
|
||||||
|
|
28
boards/atreus62/kb.py
Normal file
28
boards/atreus62/kb.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import board
|
||||||
|
|
||||||
|
from kmk.kmk_keyboard import KMKKeyboard as _KMKKeyboard
|
||||||
|
from kmk.matrix import DiodeOrientation
|
||||||
|
|
||||||
|
# from kmk.matrix import intify_coordinate as ic
|
||||||
|
|
||||||
|
|
||||||
|
class KMKKeyboard(_KMKKeyboard):
|
||||||
|
col_pins = (
|
||||||
|
board.D24,
|
||||||
|
board.D25,
|
||||||
|
board.D26,
|
||||||
|
board.D27,
|
||||||
|
board.D28,
|
||||||
|
board.D29,
|
||||||
|
board.D30,
|
||||||
|
board.D31,
|
||||||
|
board.D32,
|
||||||
|
board.D33,
|
||||||
|
board.D34,
|
||||||
|
board.D35,
|
||||||
|
)
|
||||||
|
|
||||||
|
row_pins = (board.D3, board.D4, board.D5, board.D6, board.D7, board.D8)
|
||||||
|
|
||||||
|
diode_orientation = DiodeOrientation.ROWS
|
||||||
|
# diode_orientation = DiodeOrientation.COLUMNS
|
220
boards/atreus62/main.py
Normal file
220
boards/atreus62/main.py
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
import board
|
||||||
|
|
||||||
|
from kb import KMKKeyboard
|
||||||
|
|
||||||
|
from kmk.handlers.sequences import send_string, simple_key_sequence
|
||||||
|
from kmk.keys import KC
|
||||||
|
from kmk.modules.encoder import EncoderHandler
|
||||||
|
from kmk.modules.layers import Layers
|
||||||
|
|
||||||
|
# local_increment = None
|
||||||
|
# local_decrement = None
|
||||||
|
|
||||||
|
keyboard = KMKKeyboard()
|
||||||
|
|
||||||
|
# custom keys used for encoder actions
|
||||||
|
Zoom_in = KC.LCTRL(KC.EQUAL)
|
||||||
|
Zoom_out = KC.LCTRL(KC.MINUS)
|
||||||
|
|
||||||
|
# standard filler keys
|
||||||
|
_______ = KC.TRNS
|
||||||
|
XXXXXXX = KC.NO
|
||||||
|
|
||||||
|
# for use in the encoder extension
|
||||||
|
encoder_map = [
|
||||||
|
[
|
||||||
|
(
|
||||||
|
KC.VOLU,
|
||||||
|
KC.VOLD,
|
||||||
|
2,
|
||||||
|
), # Only 1 encoder is being used, so only one tuple per layer is required
|
||||||
|
],
|
||||||
|
[
|
||||||
|
(Zoom_in, Zoom_out, 1),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
(_______, _______, 1), # no action taken by the encoder on this layer
|
||||||
|
],
|
||||||
|
]
|
||||||
|
|
||||||
|
layers_ext = Layers()
|
||||||
|
|
||||||
|
encoder_ext = EncoderHandler([board.D40], [board.D41], encoder_map)
|
||||||
|
encoder_ext.encoders[0].is_inverted = True
|
||||||
|
|
||||||
|
keyboard.modules = [layers_ext, encoder_ext]
|
||||||
|
|
||||||
|
keyboard.tap_time = 250
|
||||||
|
keyboard.debug_enabled = False
|
||||||
|
|
||||||
|
|
||||||
|
# custom keys
|
||||||
|
NEW = KC.LCTL(KC.N)
|
||||||
|
NEW_DIR = KC.LCTL(KC.LSFT(KC.N))
|
||||||
|
CAD = KC.LCTL(KC.LALT(KC.DEL))
|
||||||
|
RES = KC.LCTL(KC.LSFT(KC.ESC))
|
||||||
|
FE = KC.LGUI(KC.E)
|
||||||
|
LT1_DEL = KC.LT(1, KC.DEL)
|
||||||
|
LT2_ENT = KC.LT(2, KC.ENT)
|
||||||
|
SAVE_AS = KC.LCTL(KC.LSFT(KC.S))
|
||||||
|
PSCR = KC.LGUI(KC.PSCR)
|
||||||
|
SNIP = simple_key_sequence(
|
||||||
|
(
|
||||||
|
KC.LGUI,
|
||||||
|
KC.MACRO_SLEEP_MS(25),
|
||||||
|
KC.S,
|
||||||
|
KC.N,
|
||||||
|
KC.I,
|
||||||
|
KC.P,
|
||||||
|
KC.MACRO_SLEEP_MS(25),
|
||||||
|
KC.ENT,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# programming layer keys
|
||||||
|
UINT = simple_key_sequence(
|
||||||
|
(
|
||||||
|
KC.U,
|
||||||
|
KC.I,
|
||||||
|
KC.N,
|
||||||
|
KC.T,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
INT = simple_key_sequence(
|
||||||
|
(
|
||||||
|
KC.I,
|
||||||
|
KC.N,
|
||||||
|
KC.T,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
DOUBLE = simple_key_sequence(
|
||||||
|
(
|
||||||
|
KC.D,
|
||||||
|
KC.O,
|
||||||
|
KC.U,
|
||||||
|
KC.B,
|
||||||
|
KC.L,
|
||||||
|
KC.E,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
BOOL = simple_key_sequence(
|
||||||
|
(
|
||||||
|
KC.B,
|
||||||
|
KC.O,
|
||||||
|
KC.O,
|
||||||
|
KC.L,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
BYTE = simple_key_sequence(
|
||||||
|
(
|
||||||
|
KC.B,
|
||||||
|
KC.Y,
|
||||||
|
KC.T,
|
||||||
|
KC.E,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
SBYTE = simple_key_sequence(
|
||||||
|
(
|
||||||
|
KC.S,
|
||||||
|
KC.B,
|
||||||
|
KC.Y,
|
||||||
|
KC.T,
|
||||||
|
KC.E,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
CHAR = simple_key_sequence(
|
||||||
|
(
|
||||||
|
KC.C,
|
||||||
|
KC.H,
|
||||||
|
KC.A,
|
||||||
|
KC.R,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
GETSET = simple_key_sequence(
|
||||||
|
(
|
||||||
|
KC.LBRC,
|
||||||
|
KC.SPC,
|
||||||
|
KC.G,
|
||||||
|
KC.E,
|
||||||
|
KC.T,
|
||||||
|
KC.SCLN,
|
||||||
|
KC.SPC,
|
||||||
|
KC.S,
|
||||||
|
KC.E,
|
||||||
|
KC.T,
|
||||||
|
KC.SCLN,
|
||||||
|
KC.SPC,
|
||||||
|
KC.RBRC,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
PUBLIC = simple_key_sequence(
|
||||||
|
(
|
||||||
|
KC.P,
|
||||||
|
KC.U,
|
||||||
|
KC.B,
|
||||||
|
KC.L,
|
||||||
|
KC.I,
|
||||||
|
KC.C,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
DEBUGWL = simple_key_sequence(
|
||||||
|
(
|
||||||
|
KC.LSFT(KC.D),
|
||||||
|
KC.E,
|
||||||
|
KC.B,
|
||||||
|
KC.U,
|
||||||
|
KC.G,
|
||||||
|
KC.DOT,
|
||||||
|
KC.LSFT(KC.W),
|
||||||
|
KC.R,
|
||||||
|
KC.I,
|
||||||
|
KC.T,
|
||||||
|
KC.E,
|
||||||
|
KC.LSFT(KC.L),
|
||||||
|
KC.I,
|
||||||
|
KC.N,
|
||||||
|
KC.E,
|
||||||
|
KC.LSFT(KC.N9),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
PRINT = simple_key_sequence(
|
||||||
|
(
|
||||||
|
KC.P,
|
||||||
|
KC.R,
|
||||||
|
KC.I,
|
||||||
|
KC.N,
|
||||||
|
KC.T,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# make keymap
|
||||||
|
keyboard.keymap = [
|
||||||
|
[ # qwerty
|
||||||
|
KC.ESC, KC.N1, KC.N2, KC.N3, KC.N4, KC.N5, KC.N6, KC.N7, KC.N8, KC.N9, KC.N0, KC.MINS,
|
||||||
|
KC.CAPS, KC.Q, KC.W, KC.E, KC.R, KC.T, KC.Y, KC.U, KC.I, KC.O, KC.P, KC.PSLS,
|
||||||
|
KC.TAB, KC.A, KC.S, KC.D, KC.F, KC.G, KC.H, KC.J, KC.K, KC.L, KC.SCLN, KC.QUOT,
|
||||||
|
KC.TRNS, KC.Z, KC.X, KC.C, KC.V, KC.B, KC.N, KC.M, KC.COMM, KC.DOT, KC.SLSH, FE,
|
||||||
|
KC.BSPC, KC.DEL, KC.LALT, KC.LSFT, KC.LCTL, KC.BSPC, KC.SPC, KC.ENT, KC.RSFT, KC.RCTL, KC.ENT, KC.RGUI,
|
||||||
|
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, KC.MO(1), KC.MO(2), KC.MUTE, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
|
||||||
|
],
|
||||||
|
[ # navnum
|
||||||
|
KC.TRNS, SAVE_AS, PSCR, SNIP, KC.LGUI, NEW_DIR, KC.PSLS, KC.RGUI, KC.NO, KC.NO, KC.NO, KC.MINS,
|
||||||
|
KC.BSLS, KC.NO, KC.HOME, KC.UP, KC.END, NEW, KC.N5, KC.N6, KC.N7, KC.N8, KC.N9, KC.BSLS,
|
||||||
|
KC.F2, KC.NO, KC.LEFT, KC.DOWN, KC.RGHT, KC.HASH, KC.N0, KC.N1, KC.N2, KC.N3, KC.N4, KC.QUOT,
|
||||||
|
KC.LSFT, KC.NO, KC.NO, KC.NO, KC.TAB, KC.UNDS, KC.MINS, KC.PPLS, KC.MINS, KC.PAST, KC.PSLS, KC.LBRC,
|
||||||
|
KC.BSPC, KC.NO, KC.NO, KC.NO, KC.NO, KC.TRNS, KC.SPC, KC.EQL, KC.N0, KC.DOT, KC.ENT, KC.RGUI,
|
||||||
|
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, KC.TRNS, KC.TRNS, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
|
||||||
|
],
|
||||||
|
[ # sym/prog
|
||||||
|
KC.TRNS, KC.NO, KC.NO, KC.NO, KC.F2, KC.AMPR, PRINT, DEBUGWL, SAVE_AS, KC.NO, KC.NO, KC.NO,
|
||||||
|
KC.BSLS, KC.NO, KC.NO, KC.LCBR, KC.RCBR, KC.AT, INT, GETSET, KC.UP, KC.NO, KC.NO, KC.NO,
|
||||||
|
KC.TAB, KC.NO, KC.NO, KC.LPRN, KC.RPRN, KC.DLR, BOOL, KC.LEFT, KC.DOWN, KC.RGHT, KC.NO, KC.NO,
|
||||||
|
KC.LSFT, KC.NO, KC.NO, KC.LBRC, KC.RBRC, KC.PERC, UINT, DOUBLE, KC.NO, KC.NO, KC.NO, KC.NO,
|
||||||
|
KC.BSPC, KC.LGUI, KC.LALT, KC.LSFT, KC.LCTL, KC.DEL, KC.TRNS, PUBLIC, KC.RCTL, KC.RALT, KC.ENT, KC.RESET,
|
||||||
|
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, KC.TRNS, KC.TRNS, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
|
||||||
|
],
|
||||||
|
]
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
keyboard.go()
|
58
docs/encoder.md
Normal file
58
docs/encoder.md
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# Encoder
|
||||||
|
Add twist control to your keyboard! Volume, zoom, anything you want.
|
||||||
|
|
||||||
|
## Enabling the extension
|
||||||
|
The constructor takes a minimun of 3 arguments: a list of pad_a pins, a list of pad_b pins,
|
||||||
|
and an encoder_map. The encoder_map is modeled after the keymap and works the
|
||||||
|
same way. It should have as many layers as your keymap, and use KC.NO keys for
|
||||||
|
layers that you don't require any action. The encoder supports a velocity mode
|
||||||
|
if you desire to make something for video or sound editing. The direction of
|
||||||
|
increment/decrement can be changed to make sense for the direction the knob is
|
||||||
|
turning by setting the is_inverted flag.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
There is a complete example in the Atreus62 main.py
|
||||||
|
|
||||||
|
Create your special keys:
|
||||||
|
```python
|
||||||
|
Zoom_in = KC.LCTRL(KC.EQUAL)
|
||||||
|
Zoom_out = KC.LCTRL(KC.MINUS)
|
||||||
|
```
|
||||||
|
Create the encoder_map.
|
||||||
|
|
||||||
|
Anatomy of an encoder_map tuple: (increment_key, decrement_key, keys presses per encoder click)
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
# create the encoder map, modeled after the keymap
|
||||||
|
encoder_map = [
|
||||||
|
[
|
||||||
|
# Only 1 encoder is being used, so only one tuple per layer is required
|
||||||
|
# Increment key is volume up, decrement key is volume down, and sends 2
|
||||||
|
# key presses for every "click" felt while turning the encoder.
|
||||||
|
(KC.VOLU,KC.VOLD,2),
|
||||||
|
[
|
||||||
|
# only one key press sent per encoder click
|
||||||
|
(Zoom_in, Zoom_out,1),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
# No action keys sent here, the resolution is a dummy number, to be
|
||||||
|
# removed in the future.
|
||||||
|
(_______,_______,1),#
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
# create the encoder instance, and pass in a list of pad a pins, a lsit of pad b
|
||||||
|
# pins, and the encoder map created above
|
||||||
|
encoder_ext = EncoderHandler([board.D40],[board.D41], encoder_map)
|
||||||
|
|
||||||
|
# if desired, you can flip the incrfement/decrement direction of the knob by
|
||||||
|
# setting the is_inerted flag to True. If you turn the knob to the right and
|
||||||
|
# the volume goes down, setting this flag will make it go up. It's default
|
||||||
|
# setting is False
|
||||||
|
encoder_ext.encoders[0].is_inverted = True
|
||||||
|
|
||||||
|
# Make sure to add the encoder_ext to the modules list
|
||||||
|
keyboard.modules = [encoder_ext]
|
||||||
|
```
|
230
kmk/modules/encoder.py
Normal file
230
kmk/modules/encoder.py
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
import digitalio
|
||||||
|
|
||||||
|
from kmk.kmktime import ticks_ms
|
||||||
|
from kmk.modules import Module
|
||||||
|
|
||||||
|
|
||||||
|
class EncoderPadState:
|
||||||
|
OFF = False
|
||||||
|
ON = True
|
||||||
|
|
||||||
|
|
||||||
|
class EndcoderDirection:
|
||||||
|
Left = False
|
||||||
|
Right = True
|
||||||
|
|
||||||
|
|
||||||
|
class Encoder:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
pad_a,
|
||||||
|
pad_b,
|
||||||
|
button_pin=None,
|
||||||
|
):
|
||||||
|
self.pad_a = self.PreparePin(pad_a) # board pin for enc pin a
|
||||||
|
self.pad_a_state = False
|
||||||
|
self.pad_b = self.PreparePin(pad_b) # board pin for enc pin b
|
||||||
|
self.pad_b_state = False
|
||||||
|
self.button_pin = self.PreparePin(button_pin) # board pin for enc btn
|
||||||
|
self.button_state = None # state of pushbutton on encoder if enabled
|
||||||
|
self.encoder_value = 0 # clarify what this value is
|
||||||
|
self.encoder_state = (
|
||||||
|
self.pad_a_state,
|
||||||
|
self.pad_b_state,
|
||||||
|
) # quaderature encoder state
|
||||||
|
self.encoder_direction = None # arbitrary, tells direction of knob
|
||||||
|
self.last_encoder_state = None # not used yet
|
||||||
|
self.resolution = 2 # number of keys sent per position change
|
||||||
|
self.revolution_count = 20 # position changes per revolution
|
||||||
|
self.has_button = False # enable/disable button functionality
|
||||||
|
self.encoder_data = None # 6tuple containing all encoder data
|
||||||
|
self.position_change = None # revolution count, inc/dec as knob turns
|
||||||
|
self.last_encoder_value = 0 # not used
|
||||||
|
self.is_inverted = False # switch to invert knob direction
|
||||||
|
self.vel_mode = False # enable the velocity output
|
||||||
|
self.vel_ts = None # velocity timestamp
|
||||||
|
self.last_vel_ts = 0 # last velocity timestamp
|
||||||
|
self.encoder_speed = None # ms per position change(4 states)
|
||||||
|
self.encoder_map = None
|
||||||
|
self.eps = EncoderPadState()
|
||||||
|
self.encoder_pad_lookup = {
|
||||||
|
False: self.eps.OFF,
|
||||||
|
True: self.eps.ON,
|
||||||
|
}
|
||||||
|
self.edr = EndcoderDirection() # lookup for current encoder direction
|
||||||
|
self.encoder_dir_lookup = {
|
||||||
|
False: self.edr.Left,
|
||||||
|
True: self.edr.Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __repr__(self, idx):
|
||||||
|
return 'ENCODER_{}({})'.format(idx, self._to_dict())
|
||||||
|
|
||||||
|
def _to_dict(self):
|
||||||
|
return {
|
||||||
|
'Encoder_State': self.encoder_state,
|
||||||
|
'Direction': self.encoder_direction,
|
||||||
|
'Value': self.encoder_value,
|
||||||
|
'Position_Change': self.position_change,
|
||||||
|
'Speed': self.encoder_speed,
|
||||||
|
'Button_State': self.button_state,
|
||||||
|
}
|
||||||
|
|
||||||
|
# adapted for CircuitPython from raspi
|
||||||
|
def PreparePin(self, num):
|
||||||
|
if num is not None:
|
||||||
|
pad = digitalio.DigitalInOut(num)
|
||||||
|
pad.direction = digitalio.Direction.INPUT
|
||||||
|
pad.pull = digitalio.Pull.UP
|
||||||
|
return pad
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# checks encoder pins, reports encoder data
|
||||||
|
def report(self):
|
||||||
|
new_encoder_state = (
|
||||||
|
self.encoder_pad_lookup[int(self.pad_a.value)],
|
||||||
|
self.encoder_pad_lookup[int(self.pad_b.value)],
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.encoder_state == (self.eps.ON, self.eps.ON): # Resting position
|
||||||
|
if new_encoder_state == (self.eps.ON, self.eps.OFF): # Turned right 1
|
||||||
|
self.encoder_direction = self.edr.Right
|
||||||
|
elif new_encoder_state == (self.eps.OFF, self.eps.ON): # Turned left 1
|
||||||
|
self.encoder_direction = self.edr.Left
|
||||||
|
elif self.encoder_state == (self.eps.ON, self.eps.OFF): # R1 or L3 position
|
||||||
|
if new_encoder_state == (self.eps.OFF, self.eps.OFF): # Turned right 1
|
||||||
|
self.encoder_direction = self.edr.Right
|
||||||
|
elif new_encoder_state == (self.eps.ON, self.eps.ON): # Turned left 1
|
||||||
|
if self.encoder_direction == self.edr.Left:
|
||||||
|
self.encoder_value = self.encoder_value - 1
|
||||||
|
elif self.encoder_state == (self.eps.OFF, self.eps.ON): # R3 or L1
|
||||||
|
if new_encoder_state == (self.eps.OFF, self.eps.OFF): # Turned left 1
|
||||||
|
self.encoder_direction = self.edr.Left
|
||||||
|
elif new_encoder_state == (self.eps.ON, self.eps.ON): # Turned right 1
|
||||||
|
if self.encoder_direction == self.edr.Right:
|
||||||
|
self.encoder_value = self.encoder_value + 1
|
||||||
|
else: # self.encoder_state == '11'
|
||||||
|
if new_encoder_state == (self.eps.ON, self.eps.OFF): # Turned left 1
|
||||||
|
self.encoder_direction = self.edr.Left
|
||||||
|
elif new_encoder_state == (self.eps.OFF, self.eps.ON): # Turned right 1
|
||||||
|
self.encoder_direction = self.edr.Right # 'R'
|
||||||
|
elif new_encoder_state == (
|
||||||
|
self.eps.ON,
|
||||||
|
self.eps.ON,
|
||||||
|
): # Skipped intermediate 01 or 10 state, however turn completed
|
||||||
|
if self.encoder_direction == self.edr.Left:
|
||||||
|
self.encoder_value = self.encoder_value - 1
|
||||||
|
elif self.encoder_direction == self.edr.Right:
|
||||||
|
self.encoder_value = self.encoder_value + 1
|
||||||
|
|
||||||
|
self.encoder_state = new_encoder_state
|
||||||
|
|
||||||
|
if self.vel_mode:
|
||||||
|
self.vel_ts = ticks_ms()
|
||||||
|
|
||||||
|
if self.encoder_state != self.last_encoder_state:
|
||||||
|
self.position_change = self.invert_rotation(
|
||||||
|
self.encoder_value, self.last_encoder_value
|
||||||
|
)
|
||||||
|
|
||||||
|
self.last_encoder_state = self.encoder_state
|
||||||
|
self.last_encoder_value = self.encoder_value
|
||||||
|
|
||||||
|
if self.position_change > 0:
|
||||||
|
self._to_dict()
|
||||||
|
# return self.increment_key
|
||||||
|
return 0
|
||||||
|
elif self.position_change < 0:
|
||||||
|
self._to_dict()
|
||||||
|
# return self.decrement_key
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# invert knob direction if encoder pins are soldered backwards
|
||||||
|
def invert_rotation(self, new, old):
|
||||||
|
if self.is_inverted:
|
||||||
|
return -(new - old)
|
||||||
|
else:
|
||||||
|
return new - old
|
||||||
|
|
||||||
|
# returns knob velocity as milliseconds between position changes(detents)
|
||||||
|
def vel_report(self):
|
||||||
|
self.encoder_speed = self.vel_ts - self.last_vel_ts
|
||||||
|
self.last_vel_ts = self.vel_ts
|
||||||
|
return self.encoder_speed
|
||||||
|
|
||||||
|
|
||||||
|
class EncoderHandler(Module):
|
||||||
|
|
||||||
|
encoders = []
|
||||||
|
debug_enabled = False # not working as inttended, do not use for now
|
||||||
|
|
||||||
|
def __init__(self, pad_a, pad_b, encoder_map):
|
||||||
|
self.pad_a = pad_a
|
||||||
|
self.pad_b = pad_b
|
||||||
|
self.encoder_count = len(self.pad_a)
|
||||||
|
self.encoder_map = encoder_map
|
||||||
|
self.make_encoders()
|
||||||
|
|
||||||
|
def on_runtime_enable(self, keyboard):
|
||||||
|
return
|
||||||
|
|
||||||
|
def on_runtime_disable(self, keyboard):
|
||||||
|
return
|
||||||
|
|
||||||
|
def during_bootup(self, keyboard):
|
||||||
|
return
|
||||||
|
|
||||||
|
def before_matrix_scan(self, keyboard):
|
||||||
|
'''
|
||||||
|
Return value will be injected as an extra matrix update
|
||||||
|
'''
|
||||||
|
return self.get_reports(keyboard)
|
||||||
|
|
||||||
|
def after_matrix_scan(self, keyboard):
|
||||||
|
'''
|
||||||
|
Return value will be replace matrix update if supplied
|
||||||
|
'''
|
||||||
|
return
|
||||||
|
|
||||||
|
def before_hid_send(self, keyboard):
|
||||||
|
return
|
||||||
|
|
||||||
|
def after_hid_send(self, keyboard):
|
||||||
|
return
|
||||||
|
|
||||||
|
def on_powersave_enable(self, keyboard):
|
||||||
|
return
|
||||||
|
|
||||||
|
def on_powersave_disable(self, keyboard):
|
||||||
|
return
|
||||||
|
|
||||||
|
def make_encoders(self):
|
||||||
|
for i in range(self.encoder_count):
|
||||||
|
self.encoders.append(
|
||||||
|
Encoder(
|
||||||
|
self.pad_a[i], # encoder pin a
|
||||||
|
self.pad_b[i], # encoder pin b
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def send_encoder_keys(self, keyboard, encoder_key, encoder_idx):
|
||||||
|
# position in the encoder map tuple
|
||||||
|
encoder_resolution = 2
|
||||||
|
for _ in range(
|
||||||
|
self.encoder_map[keyboard.active_layers[0]][encoder_idx][encoder_resolution]
|
||||||
|
):
|
||||||
|
keyboard.tap_key(
|
||||||
|
self.encoder_map[keyboard.active_layers[0]][encoder_idx][encoder_key]
|
||||||
|
)
|
||||||
|
return keyboard
|
||||||
|
|
||||||
|
def get_reports(self, keyboard):
|
||||||
|
for idx in range(self.encoder_count):
|
||||||
|
if self.debug_enabled: # not working as inttended, do not use for now
|
||||||
|
print(self.encoders[idx].__repr__(idx))
|
||||||
|
encoder_key = self.encoders[idx].report()
|
||||||
|
if encoder_key is not None:
|
||||||
|
return self.send_encoder_keys(keyboard, encoder_key, idx)
|
Loading…
x
Reference in New Issue
Block a user