You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

111 lines
4.2 KiB

import time
import board
import digitalio
import gc
import busio
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
from adafruit_hid.mouse import Mouse
from adafruit_neotrellis.neotrellis import NeoTrellis
OFF = (5, 0, 5)
LIT = (40, 20, 0)
UNDEF = (1, 1, 1)
PRESSED = (80, 40, 0)
BOUNCE_SECS = 0.250
NEOTRELLIS_COUNT = 16
class SnakeSwitch:
def __init__(self, switch_pins, layouts):
i2c_bus = busio.I2C(board.SCL, board.SDA)
self.trellis = NeoTrellis(i2c_bus)
self.kbd = Keyboard()
self.switch_inputs = tuple(digitalio.DigitalInOut(pin) for pin in switch_pins)
for switch_input in self.switch_inputs:
switch_input.switch_to_input(pull=digitalio.Pull.UP)
self.layouts = layouts
self.active_layouts = {}
self.active_switches = {}
for i in range(NEOTRELLIS_COUNT):
self.active_layouts[i] = False
# activate rising / falling edge events on all keys, set up callback
self.trellis.activate_key(i, NeoTrellis.EDGE_RISING)
self.trellis.activate_key(i, NeoTrellis.EDGE_FALLING)
self.trellis.callbacks[i] = self.handle_neotrellis_event
self.toggle_layout(sorted(self.layouts.keys())[0])
self.paint_trellis()
def handle_neotrellis_event(self, event):
if event.edge == NeoTrellis.EDGE_RISING:
# Highlight the currently pressed key:
self.trellis.pixels[event.number] = PRESSED
elif event.edge == NeoTrellis.EDGE_FALLING:
# On release, activate a button if it has a defined layout:
if event.number in self.layouts:
self.toggle_layout(event.number)
self.paint_trellis()
def toggle_layout(self, layout_id):
print(time.monotonic(), 'switching layout: ', layout_id)
self.active_layouts[layout_id] = not self.active_layouts[layout_id]
def paint_trellis(self):
for layout_button, active in self.active_layouts.items():
if active:
# Layout is currently activated:
self.trellis.pixels[layout_button] = LIT
elif layout_button in self.layouts:
# Layout is defined, but deactivate:
self.trellis.pixels[layout_button] = OFF
else:
# Layout isn't defined in the config file:
self.trellis.pixels[layout_button] = UNDEF
def advance_frame(self):
# XXX: trellis sync may need to happen less frequently
self.trellis.sync()
current_layouts = [self.layouts[layout] for layout, active in self.active_layouts.items() if active]
for switch, switch_input in enumerate(self.switch_inputs):
# If switch is un-pressed, it's pulled high. Make sure to release
# any keys attached to it and mark it non-active.
if switch_input.value:
if switch in self.active_switches and self.active_switches[switch]:
print(time.monotonic(), 'marking switch', switch, ' inactive')
keys = self.get_keys_for_switch(switch, current_layouts)
for key in keys:
print(time.monotonic(), 'release key:', key)
self.kbd.release(key)
self.active_switches[switch] = False
continue
if switch in self.active_switches and self.active_switches[switch]:
# If we're already marked active, do nothing.
continue
# If switch is pressed, it's pulled low. Debounce by waiting for bounce time:
time.sleep(BOUNCE_SECS)
print(time.monotonic(), 'marking switch', switch, ' active')
self.active_switches[switch] = True
for key in self.get_keys_for_switch(switch, current_layouts):
print(time.monotonic(), 'press key:', key)
self.kbd.press(key)
def get_keys_for_switch(self, switch, current_layouts):
keys = []
for layout in current_layouts:
if switch in layout:
if isinstance(layout[switch], tuple):
keys.extend(layout[switch])
else:
keys.append(layout[switch])
return keys