"""CircuitPython polled and debounced Button driver. Replaces the MicroPython Pin IRQ implementation with active loop polling. """ import time import digitalio class Button: def __init__(self, pin_obj, name="Button", debounce_ms=50, long_press_ms=800): self.pin = digitalio.DigitalInOut(pin_obj) self.pin.switch_to_input(pull=digitalio.Pull.UP) self.name = name self.debounce_s = debounce_ms / 1000.0 self.long_press_s = long_press_ms / 1000.0 self.last_state = True self.press_time = 0.0 self.last_debounce_time = 0.0 self.click_callback = None self.long_press_callback = None def is_pressed(self): return not self.pin.value def on_click(self, callback): self.click_callback = callback return callback def on_long_press(self, callback): self.long_press_callback = callback return callback def update(self): now = time.monotonic() val = self.pin.value if (now - self.last_debounce_time) < self.debounce_s: return if val != self.last_state: self.last_debounce_time = now self.last_state = val if not val: # Pressed (Active Low) self.press_time = now else: # Released if self.press_time > 0.0: duration = now - self.press_time self.press_time = 0.0 if duration >= self.long_press_s: if self.long_press_callback: self.long_press_callback() else: if self.click_callback: self.click_callback() class BoardButtons: def __init__(self, boot_pin, key_pin=None): self.boot = Button(boot_pin, "BOOT") if key_pin is not None: self.key = Button(key_pin, "KEY") else: self.key = None def update(self): self.boot.update() if self.key is not None: self.key.update()