Add interrupt handling and debouncing
This commit is contained in:
@@ -14,6 +14,12 @@ BUTTON_SEND = None
|
|||||||
RATE_BUTTONS = []
|
RATE_BUTTONS = []
|
||||||
DISPLAY = None
|
DISPLAY = None
|
||||||
|
|
||||||
|
# Variables for Interrupt (ISR) handling and Debouncing
|
||||||
|
pending_rate_button = -1
|
||||||
|
pending_send_button = False
|
||||||
|
last_interrupt_time = 0
|
||||||
|
DEBOUNCE_MS = 250 # Ignore button presses within 250ms of each other
|
||||||
|
|
||||||
# Helper to update display based on state
|
# Helper to update display based on state
|
||||||
def refresh_display(message=None):
|
def refresh_display(message=None):
|
||||||
if not DISPLAY:
|
if not DISPLAY:
|
||||||
@@ -26,10 +32,9 @@ def refresh_display(message=None):
|
|||||||
DISPLAY.text(message, 10, 30)
|
DISPLAY.text(message, 10, 30)
|
||||||
elif CURRENT_STATE == STATE_USER_ID:
|
elif CURRENT_STATE == STATE_USER_ID:
|
||||||
# 1. User ID Mode
|
# 1. User ID Mode
|
||||||
DISPLAY.text("Inchometer", 25, 0)
|
DISPLAY.text("User ID Mode", 15, 0)
|
||||||
|
|
||||||
# Display binary representation: _ _ _ _ _
|
# Display binary representation: _ _ _ _ _
|
||||||
# Bit 4 is leftmost button, Bit 0 is rightmost
|
|
||||||
binary_str = ""
|
binary_str = ""
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
bit_pos = 4 - i
|
bit_pos = 4 - i
|
||||||
@@ -49,8 +54,6 @@ def refresh_display(message=None):
|
|||||||
else:
|
else:
|
||||||
rating_text = str(RATING)
|
rating_text = str(RATING)
|
||||||
|
|
||||||
# Draw "big" text (simulated with scale if supported, or just centered)
|
|
||||||
# Standard ssd1306 doesn't have big fonts, so we just center it
|
|
||||||
DISPLAY.text(rating_text, 60, 30)
|
DISPLAY.text(rating_text, 60, 30)
|
||||||
|
|
||||||
DISPLAY.show()
|
DISPLAY.show()
|
||||||
@@ -60,30 +63,35 @@ def initialize_pin(pin_number:int):
|
|||||||
button = Pin(pin_number, Pin.IN, Pin.PULL_DOWN)
|
button = Pin(pin_number, Pin.IN, Pin.PULL_DOWN)
|
||||||
return button
|
return button
|
||||||
|
|
||||||
# activates send pin
|
# Hardware Interrupt: rate button is pressed
|
||||||
def activate_sendPin() -> None:
|
|
||||||
BUTTON_SEND.irq(trigger=Pin.IRQ_RISING, handler=sendButton_pressed)
|
|
||||||
|
|
||||||
# deactivates send pin
|
|
||||||
def deactivate_sendPin() -> None:
|
|
||||||
BUTTON_SEND.irq(handler=None)
|
|
||||||
|
|
||||||
# rate button is pressed
|
|
||||||
def rate_button_pressed(pin:Pin) -> None:
|
def rate_button_pressed(pin:Pin) -> None:
|
||||||
global USER_ID, RATING, CURRENT_STATE, RATE_BUTTONS
|
global pending_rate_button, last_interrupt_time
|
||||||
|
|
||||||
# Find which button index was pressed
|
current_time = time.ticks_ms()
|
||||||
button_index = -1
|
# Simple software debounce
|
||||||
for i, button in enumerate(RATE_BUTTONS):
|
if time.ticks_diff(current_time, last_interrupt_time) > DEBOUNCE_MS:
|
||||||
if button == pin:
|
# Find which button index was pressed
|
||||||
button_index = i
|
for i, button in enumerate(RATE_BUTTONS):
|
||||||
break
|
if button == pin:
|
||||||
|
pending_rate_button = i
|
||||||
|
break
|
||||||
|
last_interrupt_time = current_time
|
||||||
|
|
||||||
if button_index == -1:
|
# Hardware Interrupt: Send/Confirm button is pressed
|
||||||
return
|
def sendButton_pressed(pin:Pin) -> None:
|
||||||
|
global pending_send_button, last_interrupt_time
|
||||||
|
|
||||||
|
current_time = time.ticks_ms()
|
||||||
|
if time.ticks_diff(current_time, last_interrupt_time) > DEBOUNCE_MS:
|
||||||
|
pending_send_button = True
|
||||||
|
last_interrupt_time = current_time
|
||||||
|
|
||||||
|
# Process the rate button press (Called from the main loop)
|
||||||
|
def process_rate_button(button_index: int):
|
||||||
|
global USER_ID, RATING, CURRENT_STATE
|
||||||
|
|
||||||
if CURRENT_STATE == STATE_USER_ID:
|
if CURRENT_STATE == STATE_USER_ID:
|
||||||
bit_pos = len(RATE_BUTTONS) - 1 - button_index
|
bit_pos = 4 - button_index
|
||||||
USER_ID ^= (1 << bit_pos)
|
USER_ID ^= (1 << bit_pos)
|
||||||
print(f"Current User ID: {USER_ID}")
|
print(f"Current User ID: {USER_ID}")
|
||||||
|
|
||||||
@@ -92,12 +100,10 @@ def rate_button_pressed(pin:Pin) -> None:
|
|||||||
print(f"Current Rating selected: {RATING}")
|
print(f"Current Rating selected: {RATING}")
|
||||||
|
|
||||||
refresh_display()
|
refresh_display()
|
||||||
activate_sendPin()
|
|
||||||
|
|
||||||
# Send/Confirm button is pressed
|
# Process the send button press (Called from the main loop)
|
||||||
def sendButton_pressed(pin) -> None:
|
def process_send_button():
|
||||||
global CURRENT_STATE, USER_ID, RATING
|
global CURRENT_STATE, USER_ID, RATING
|
||||||
deactivate_sendPin()
|
|
||||||
|
|
||||||
if CURRENT_STATE == STATE_USER_ID:
|
if CURRENT_STATE == STATE_USER_ID:
|
||||||
# 2. Confirmed ID display
|
# 2. Confirmed ID display
|
||||||
@@ -109,8 +115,7 @@ def sendButton_pressed(pin) -> None:
|
|||||||
|
|
||||||
elif CURRENT_STATE == STATE_RATING:
|
elif CURRENT_STATE == STATE_RATING:
|
||||||
if RATING == 0:
|
if RATING == 0:
|
||||||
activate_sendPin()
|
return # Don't send if no rating is selected yet
|
||||||
return
|
|
||||||
|
|
||||||
# 4. Sending / Success / Error
|
# 4. Sending / Success / Error
|
||||||
refresh_display("Sending...")
|
refresh_display("Sending...")
|
||||||
@@ -122,7 +127,7 @@ def sendButton_pressed(pin) -> None:
|
|||||||
else:
|
else:
|
||||||
refresh_display("Error")
|
refresh_display("Error")
|
||||||
|
|
||||||
time.sleep(5) # Display status for 5 seconds
|
time.sleep(3) # Display status for 3 seconds
|
||||||
|
|
||||||
# Reset for next rating
|
# Reset for next rating
|
||||||
USER_ID = 0
|
USER_ID = 0
|
||||||
@@ -133,21 +138,32 @@ def sendButton_pressed(pin) -> None:
|
|||||||
# start the rating program
|
# start the rating program
|
||||||
def start_rating(rate_button_pins:list, button_send_pin:int, oled_display=None) -> None:
|
def start_rating(rate_button_pins:list, button_send_pin:int, oled_display=None) -> None:
|
||||||
global RATE_BUTTONS, BUTTON_SEND, DISPLAY
|
global RATE_BUTTONS, BUTTON_SEND, DISPLAY
|
||||||
|
global pending_rate_button, pending_send_button
|
||||||
|
|
||||||
DISPLAY = oled_display
|
DISPLAY = oled_display
|
||||||
|
|
||||||
# initialize all pins as buttons and add them to the list
|
# initialize all pins as buttons and attach interrupts
|
||||||
RATE_BUTTONS = []
|
RATE_BUTTONS = []
|
||||||
for pin_num in rate_button_pins:
|
for pin_num in rate_button_pins:
|
||||||
button = initialize_pin(pin_num)
|
button = initialize_pin(pin_num)
|
||||||
button.irq(trigger=Pin.IRQ_RISING, handler=rate_button_pressed)
|
button.irq(trigger=Pin.IRQ_RISING, handler=rate_button_pressed)
|
||||||
RATE_BUTTONS.append(button)
|
RATE_BUTTONS.append(button)
|
||||||
|
|
||||||
# initialize send button
|
# initialize send button and attach interrupt
|
||||||
BUTTON_SEND = initialize_pin(button_send_pin)
|
BUTTON_SEND = initialize_pin(button_send_pin)
|
||||||
|
BUTTON_SEND.irq(trigger=Pin.IRQ_RISING, handler=sendButton_pressed)
|
||||||
|
|
||||||
print("System started.")
|
print("System started.")
|
||||||
refresh_display()
|
refresh_display()
|
||||||
|
|
||||||
|
# The Main Event Loop - This safely handles the screen updates
|
||||||
while True:
|
while True:
|
||||||
time.sleep(1)
|
if pending_rate_button != -1:
|
||||||
|
process_rate_button(pending_rate_button)
|
||||||
|
pending_rate_button = -1 # Reset the flag
|
||||||
|
|
||||||
|
if pending_send_button:
|
||||||
|
process_send_button()
|
||||||
|
pending_send_button = False # Reset the flag
|
||||||
|
|
||||||
|
time.sleep_ms(50) # Short delay to save power and avoid choking the CPU
|
||||||
|
|||||||
Reference in New Issue
Block a user