@@ -716,57 +716,92 @@ def _web_encoder_press(state):
716716# read_buttons() # returns a boolean list of the state of the 4 buttons.
717717# Code based on abstracting
718718# https://github.com/adafruit/Adafruit_CircuitPython_seesaw/blob/main/adafruit_seesaw/seesaw.py.
719+ #
720+ # Missing-hardware behaviour: if the Seesaw encoder breakout isn't on the
721+ # I2C bus (user built an AMYboard with no rotary encoder attached), the
722+ # helpers below catch the resulting OSError on the first try, cache the
723+ # offending device address in _seesaw_missing, and silently return safe
724+ # defaults from then on. This lets sketches like preset_selector.py still
725+ # import and run cleanly — they just don't see encoder motion or button
726+ # presses instead of crashing the board at sketch import time.
727+
728+ _seesaw_missing = set () # addresses we've already probed and confirmed absent
719729
720730def read_encoder (encoder = 0 , seesaw_dev = 0x49 , delay = 0.008 ):
721- """Read the cumulated value of encoder 0..3."""
731+ """Read the cumulated value of encoder 0..3.
732+
733+ Returns 0 if the seesaw device isn't present on the I2C bus (see
734+ module-level note on missing-hardware behaviour)."""
722735 if web ():
723736 return _web_encoder_pos
724- i2c = get_i2c ()
725- result = bytearray (4 )
726- ENCODER_BASE = 0x11
727- ENCODER_POSITION = 0x30
728- i2c .writeto (seesaw_dev , bytes ([ENCODER_BASE , ENCODER_POSITION + encoder ]))
729- time .sleep (delay )
730- i2c .readfrom_into (seesaw_dev , result )
731- return struct .unpack (">i" , result )[0 ]
737+ if seesaw_dev in _seesaw_missing :
738+ return 0
739+ try :
740+ i2c = get_i2c ()
741+ result = bytearray (4 )
742+ ENCODER_BASE = 0x11
743+ ENCODER_POSITION = 0x30
744+ i2c .writeto (seesaw_dev , bytes ([ENCODER_BASE , ENCODER_POSITION + encoder ]))
745+ time .sleep (delay )
746+ i2c .readfrom_into (seesaw_dev , result )
747+ return struct .unpack (">i" , result )[0 ]
748+ except OSError :
749+ _seesaw_missing .add (seesaw_dev )
750+ return 0
732751
733752def init_buttons (pins = (12 , 14 , 17 , 9 ), seesaw_dev = 0x49 ):
734- """Setup the seesaw quad encoder button pins to input_pullup."""
753+ """Setup the seesaw quad encoder button pins to input_pullup.
754+
755+ Silently no-ops if the seesaw device isn't on the I2C bus."""
735756 if web ():
736757 return
737- mask = 0
738- for p in pins :
739- mask |= (1 << p )
740- mask_bytes = struct .pack ('>I' , mask )
741- i2c = get_i2c ()
742- GPIO_BASE = 0x01
743- GPIO_DIRCLR_BULK = 0x03
744- GPIO_PULLENSET = 0x0B
745- GPIO_BULK_SET = 0x05
746- i2c .writeto (seesaw_dev , bytes ([GPIO_BASE , GPIO_DIRCLR_BULK ]) + mask_bytes )
747- i2c .writeto (seesaw_dev , bytes ([GPIO_BASE , GPIO_PULLENSET ]) + mask_bytes )
748- i2c .writeto (seesaw_dev , bytes ([GPIO_BASE , GPIO_BULK_SET ]) + mask_bytes )
758+ if seesaw_dev in _seesaw_missing :
759+ return
760+ try :
761+ mask = 0
762+ for p in pins :
763+ mask |= (1 << p )
764+ mask_bytes = struct .pack ('>I' , mask )
765+ i2c = get_i2c ()
766+ GPIO_BASE = 0x01
767+ GPIO_DIRCLR_BULK = 0x03
768+ GPIO_PULLENSET = 0x0B
769+ GPIO_BULK_SET = 0x05
770+ i2c .writeto (seesaw_dev , bytes ([GPIO_BASE , GPIO_DIRCLR_BULK ]) + mask_bytes )
771+ i2c .writeto (seesaw_dev , bytes ([GPIO_BASE , GPIO_PULLENSET ]) + mask_bytes )
772+ i2c .writeto (seesaw_dev , bytes ([GPIO_BASE , GPIO_BULK_SET ]) + mask_bytes )
773+ except OSError :
774+ _seesaw_missing .add (seesaw_dev )
749775
750776def read_buttons (pins = (12 , 14 , 17 , 9 ), seesaw_dev = 0x49 , delay = 0.008 ):
751- """Read the 4 seesaw encoder push buttons."""
777+ """Read the 4 seesaw encoder push buttons.
778+
779+ Returns [False, False, ...] (one entry per pin) if the seesaw device
780+ isn't on the I2C bus."""
752781 if web ():
753782 return [_web_encoder_button ] * len (pins )
754- i2c = get_i2c ()
755- GPIO_BASE = 0x01
756- GPIO_BULK = 0x04
757- i2c .writeto (seesaw_dev , bytes ([GPIO_BASE , GPIO_BULK ]))
758- time .sleep (delay )
759- buffer = bytearray (4 )
760- i2c .readfrom_into (seesaw_dev , buffer )
761- mask = struct .unpack ('>I' , buffer )[0 ]
762- result = []
763- for p in pins :
764- state = True
765- if (mask & (1 << p )):
766- # bit set means button not pressed.
767- state = False
768- result .append (state )
769- return result
783+ if seesaw_dev in _seesaw_missing :
784+ return [False ] * len (pins )
785+ try :
786+ i2c = get_i2c ()
787+ GPIO_BASE = 0x01
788+ GPIO_BULK = 0x04
789+ i2c .writeto (seesaw_dev , bytes ([GPIO_BASE , GPIO_BULK ]))
790+ time .sleep (delay )
791+ buffer = bytearray (4 )
792+ i2c .readfrom_into (seesaw_dev , buffer )
793+ mask = struct .unpack ('>I' , buffer )[0 ]
794+ result = []
795+ for p in pins :
796+ state = True
797+ if (mask & (1 << p )):
798+ # bit set means button not pressed.
799+ state = False
800+ result .append (state )
801+ return result
802+ except OSError :
803+ _seesaw_missing .add (seesaw_dev )
804+ return [False ] * len (pins )
770805
771806def monitor_encoders ():
772807 """Show status of encoders on display."""
0 commit comments