@ -9,51 +9,103 @@ from adafruit_hid.keycode import Keycode
from adafruit_hid.mouse import Mouse
from adafruit_neotrellis.neotrellis import NeoTrellis
OFF = ( 1 , 0 , 0 )
OFF = ( 5 , 0 , 5 )
LIT = ( 40 , 20 , 0 )
SWITCH_PINS = ( board . D5 , board . D6 , board . D9 , board . D10 , board . D11 , board . D12 , board . A1 )
BOUNCE_SECS = 0.100
UNDEF = ( 1 , 1 , 1 )
PRESSED = ( 80 , 40 , 0 )
BOUNCE_SECS = 0.250
NEOTRELLIS_COUNT = 16
class SnakeSwitch :
def __init__ ( self ) :
def __init__ ( self , switch_pins , layouts ) :
i2c_bus = busio . I2C ( board . SCL , board . SDA )
self . trellis = NeoTrellis ( i2c_bus )
self . kbd = Keyboard ( )
self . switch_ins = tuple ( digitalio . DigitalInOut ( pin ) for pin in SWITCH_PINS )
for switch_in in self . switch_ins :
switch_in . switch_to_input ( pull = digitalio . Pull . UP )
self . switch_input s = tuple ( digitalio . DigitalInOut ( pin ) for pin in switch_pins )
for switch_input in self . switch_input s :
switch_input . switch_to_input ( pull = digitalio . Pull . UP )
self . kbd = Keyboard ( )
self . states = { }
self . layouts = layouts
self . active_layouts = { }
self . active_switches = { }
for i in range ( 16 ) :
self . st ates[ i ] = False
for i in range ( NEOTRELLIS_COUNT ) :
self . ac tiv e_layout s [ 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 . toggle
self . trellis . callbacks [ i ] = self . handle_neotrellis_event
self . toggle_layout ( sorted ( self . layouts . keys ( ) ) [ 0 ] )
self . paint_trellis ( )
def toggle ( self , event ) :
# turn the LED on when a rising edge is detected
def handle_neotrellis_event ( self , event ) :
if event . edge == NeoTrellis . EDGE_RISING :
self . states [ event . number ] = not self . states [ event . number ]
# 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 ( )
for state , value in self . states . items ( ) :
if value :
self . trellis . pixels [ state ] = LIT
else :
self . trellis . pixels [ state ] = OFF
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 ) :
for switch , switch_in in enumerate ( self . switch_ins ) :
self . trellis . sync ( )
if not switch_in . value :
# If switch is pressed, it's pulled low. Debounce by waiting for bounce time.
time . sleep ( BOUNCE_SECS )
print ( ' switch: ' , switch )
print ( gc . mem_free ( ) )
self . kbd . send ( Keycode . LEFT_GUI , Keycode . TAB )
# Wait for switch to be released.
while not switch_in . value :
pass
# 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