merge new_encoder.py to encoder.py
This commit is contained in:
		@@ -1,207 +0,0 @@
 | 
			
		||||
# How to use this module in your main / code file
 | 
			
		||||
#
 | 
			
		||||
# 1. load the module
 | 
			
		||||
# from kmk.modules.new_encoder import EncoderHandler
 | 
			
		||||
# encoder_handler = EncoderHandler()
 | 
			
		||||
# keyboard.modules = [layers, modtap, encoder_handler]
 | 
			
		||||
#
 | 
			
		||||
# 2. Define the pins for each encoder (pin_a, pin_b, pin_button, True for an inversed encoder)
 | 
			
		||||
# encoder_handler.pins = ((board.GP17, board.GP15, board.GP14, False), (encoder 2 definition), etc. )
 | 
			
		||||
#
 | 
			
		||||
# 3. Define the mapping of keys to be called (1 / layer)
 | 
			
		||||
# encoder_handler.map = [(( KC.A, KC.Z, KC.E),(encoder 2 mapping), (etc.)), # Layer 1
 | 
			
		||||
#                        ((KC.A, KC.Z, KC.N1),(encoder 2 mapping), (etc.)), # Layer 2
 | 
			
		||||
#                        ((KC.A, KC.Z, KC.N1),(encoder 2 mapping), (etc.)), # Layer 3
 | 
			
		||||
#                        ((KC.A, KC.Z, KC.N1),(encoder 2 mapping), (etc.)), # Layer 4
 | 
			
		||||
#                        ]
 | 
			
		||||
# 4. Encoder methods on_move_do and on_button_do can be overwritten for complex use cases
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
import digitalio
 | 
			
		||||
 | 
			
		||||
from kmk.kmktime import tick_ms
 | 
			
		||||
from kmk.modules import Module
 | 
			
		||||
 | 
			
		||||
# NB : not using rotaryio as it requires the pins to be consecutive
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Encoder:
 | 
			
		||||
 | 
			
		||||
    VELOCITY_MODE = True
 | 
			
		||||
    STATES = {
 | 
			
		||||
        # old_pos_a, old_pos_b, new_pos_a, new_pos_b
 | 
			
		||||
        # -1 : Left ; 1 : Right ; 0 : we don't care
 | 
			
		||||
        ((True, True), (True, False)): -1,
 | 
			
		||||
        ((True, True), (False, True)): 1,
 | 
			
		||||
        ((True, False), (False, False)): -1,
 | 
			
		||||
        ((True, False), (True, True)): 0,
 | 
			
		||||
        ((False, True), (False, False)): 1,
 | 
			
		||||
        ((False, True), (True, True)): 0,
 | 
			
		||||
        ((False, False), (True, False)): 0,
 | 
			
		||||
        ((False, False), (False, True)): 0,
 | 
			
		||||
        ((False, False), (True, True)): 0,
 | 
			
		||||
        ((False, True), (True, False)): 0,
 | 
			
		||||
        ((True, False), (False, True)): 0,
 | 
			
		||||
        ((True, True), (False, False)): 0,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def __init__(self, pin_a, pin_b, pin_button=None, is_inverted=False):
 | 
			
		||||
        self.pin_a = EncoderPin(pin_a)
 | 
			
		||||
        self.pin_b = EncoderPin(pin_b)
 | 
			
		||||
        self.pin_button = EncoderPin(pin_button, button_type=True)
 | 
			
		||||
        self.is_inverted = is_inverted
 | 
			
		||||
 | 
			
		||||
        self._state = (self.pin_a.get_value(), self.pin_b.get_value())
 | 
			
		||||
        self._direction = None
 | 
			
		||||
        self._pos = 0
 | 
			
		||||
        self._button_state = True
 | 
			
		||||
        self._velocity = 0
 | 
			
		||||
 | 
			
		||||
        self._movement = 0
 | 
			
		||||
        self._timestamp = tick_ms()
 | 
			
		||||
 | 
			
		||||
        # callback functions on events. Need to be defined externally
 | 
			
		||||
        self.on_move_do = None
 | 
			
		||||
        self.on_button_do = None
 | 
			
		||||
 | 
			
		||||
    def get_state(self):
 | 
			
		||||
        return {
 | 
			
		||||
            'direction': self.is_inverted
 | 
			
		||||
            and -self._direction
 | 
			
		||||
            or self._direction,
 | 
			
		||||
            'position': self.is_inverted and -self._pos or self._pos,
 | 
			
		||||
            'is_pressed': not self._button_state,
 | 
			
		||||
            'velocity': self.velocity
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    # Called in a loop to refresh encoder state
 | 
			
		||||
    def update_state(self):
 | 
			
		||||
        # Rotation events
 | 
			
		||||
        new_state = (self.pin_a.get_value(), self.pin_b.get_value())
 | 
			
		||||
        if new_state != self._state:
 | 
			
		||||
            self._movement += 1
 | 
			
		||||
            new_direction = self.STATES[(self._state, new_state)]
 | 
			
		||||
            if new_direction != 0:
 | 
			
		||||
                self._direction = new_direction
 | 
			
		||||
 | 
			
		||||
            # when the encoder settles on a position (every 2 steps)
 | 
			
		||||
            if (
 | 
			
		||||
                new_state == (True, True) and self._movement > 2
 | 
			
		||||
            ):  # if < 2 state changes, it is a misstep
 | 
			
		||||
                self._movement = 0
 | 
			
		||||
                self._pos += self._direction
 | 
			
		||||
                if self.on_move_do is not None:
 | 
			
		||||
                    self.on_move_do(self.get_state())
 | 
			
		||||
 | 
			
		||||
            self._state = new_state
 | 
			
		||||
 | 
			
		||||
        # Velocity
 | 
			
		||||
        if VELOCITY_MODE:
 | 
			
		||||
            new_timestamp = tick_ms()
 | 
			
		||||
            self._velocity = new_timestamp - self._timestamp
 | 
			
		||||
            self._timestamp = new_timestamp
 | 
			
		||||
 | 
			
		||||
        # Button events
 | 
			
		||||
        new_button_state = self.pin_button.get_value()
 | 
			
		||||
        if new_button_state != self._button_state:
 | 
			
		||||
            self._button_state = new_button_state
 | 
			
		||||
            if self.on_button_do is not None:
 | 
			
		||||
                self.on_button_do(self.get_state())
 | 
			
		||||
 | 
			
		||||
    # returnd knob velocity as milliseconds between position changes (detents)
 | 
			
		||||
    def vel_report(self):
 | 
			
		||||
        return self._velocity
 | 
			
		||||
 | 
			
		||||
    # callback for actions on move (set up externally)
 | 
			
		||||
    def on_move_do(self, :
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    # callback for actions on button press (set up externally)
 | 
			
		||||
    def on_button_do:
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EncoderPin:
 | 
			
		||||
    def __init__(self, pin, button_type=False):
 | 
			
		||||
        self.pin = pin
 | 
			
		||||
        self.button_type = button_type
 | 
			
		||||
        self.prepare_pin()
 | 
			
		||||
 | 
			
		||||
    def prepare_pin(self):
 | 
			
		||||
        if self.pin is not None:
 | 
			
		||||
            self.io = digitalio.DigitalInOut(self.pin)
 | 
			
		||||
            self.io.direction = digitalio.Direction.INPUT
 | 
			
		||||
            self.io.pull = digitalio.Pull.UP
 | 
			
		||||
        else:
 | 
			
		||||
            self.io = None
 | 
			
		||||
 | 
			
		||||
    def get_value(self):
 | 
			
		||||
        return self.io.value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EncoderHandler(Module):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.encoders = []
 | 
			
		||||
        self.pins = None
 | 
			
		||||
        self.map = None
 | 
			
		||||
 | 
			
		||||
    def on_runtime_enable(self, keyboard):
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    def on_runtime_disable(self, keyboard):
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    def during_bootup(self, keyboard):
 | 
			
		||||
        if self.pins and self.map:
 | 
			
		||||
            for idx, pins in enumerate(self.pins):
 | 
			
		||||
                gpio_pins = pins[:3]
 | 
			
		||||
                new_encoder = Encoder(*gpio_pins)
 | 
			
		||||
                # In our case, we need to define keybord and encoder_id for callbacks
 | 
			
		||||
                new_encoder.on_move_do = lambda x: self.on_move_do(keyboard, idx, x)
 | 
			
		||||
                new_encoder.on_button_do = lambda x: self.on_button_do(keyboard, idx, x)
 | 
			
		||||
                self.encoders.append(new_encoder)
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    def on_move_do(self, keyboard, encoder_id, state):
 | 
			
		||||
        if self.map:
 | 
			
		||||
            layer_id = keyboard.active_layers[0]
 | 
			
		||||
            # if Left, key index 0 else key index 1
 | 
			
		||||
            if state['direction'] == -1:
 | 
			
		||||
                key_index = 0
 | 
			
		||||
            else:
 | 
			
		||||
                key_index = 1
 | 
			
		||||
            key = self.map[layer_id][encoder_id][key_index]
 | 
			
		||||
            keyboard.tap_key(key)
 | 
			
		||||
 | 
			
		||||
    def on_button_do(self, keyboard, encoder_id, state):
 | 
			
		||||
        if state['is_pressed'] is True:
 | 
			
		||||
            layer_id = keyboard.active_layers[0]
 | 
			
		||||
            key = self.map[layer_id][encoder_id][2]
 | 
			
		||||
            keyboard.tap_key(key)
 | 
			
		||||
 | 
			
		||||
    def before_matrix_scan(self, keyboard):
 | 
			
		||||
        '''
 | 
			
		||||
        Return value will be injected as an extra matrix update
 | 
			
		||||
        '''
 | 
			
		||||
        for encoder in self.encoders:
 | 
			
		||||
            encoder.update_state()
 | 
			
		||||
 | 
			
		||||
        return 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
 | 
			
		||||
		Reference in New Issue
	
	Block a user