Skip to content

Commit 55bac51

Browse files
committed
feat: make MCP23009 inherit from ButtonIOExpander
1 parent 6dd1ebe commit 55bac51

File tree

2 files changed

+10
-113
lines changed

2 files changed

+10
-113
lines changed

modules/button_config.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ class Button_Config:
178178
},
179179
"Pirate_Audio_old": {},
180180
"Display_HAT_Mini": {},
181-
"MCP23009": {
181+
"IOExpander": {
182182
"MAIN": {
183183
"GP3": ("scroll_prev", ""),
184184
"GP4": ("count_laps", "reset_count"),

modules/sensor/i2c/MCP23009.py

+9-112
Original file line numberDiff line numberDiff line change
@@ -1,126 +1,23 @@
1-
import time
2-
from threading import Thread
3-
from logger import app_logger
4-
5-
6-
try:
7-
# run from top directory (pizero_bikecomputer)
8-
from . import i2c
9-
except:
10-
# directly run this program
11-
import i2c
1+
from .base.button_io_expander import ButtonIOExpander
2+
from adafruit_mcp230xx.mcp23008 import MCP23008 as MCP
3+
import board
4+
import busio
125

136
# https://www.microchip.com/en-us/product/mcp23009
147
# https://ww1.microchip.com/downloads/en/DeviceDoc/20002121C.pdf
158

16-
### MCP23009 Register definitions ###
17-
IODIR = 0x00
18-
GPIO = 0x09
19-
GPPU = 0x06
209

10+
# NOTE: no need to set TEST and RESET address and value, due to adafruit_mcp230xx library handling it.
11+
class MCP23009(ButtonIOExpander):
2112

22-
class MCP23009(i2c.i2c):
2313
# address
2414
SENSOR_ADDRESS = 0x27
2515

26-
RESET_ADDRESS = IODIR
27-
RESET_VALUE = 0xFF
28-
29-
TEST_ADDRESS = IODIR
30-
TEST_VALUE = (0xFF,)
31-
3216
# The amount of available channels (8 for MCP23009)
3317
CHANNELS = 8
3418

35-
# A button press counts as long press after this amount of milliseconds
36-
LONG_PRESS_DURATION_MS = 1000
37-
38-
# After the button is pressed, it is disabled for this amount of milliseconds to prevent bouncing
39-
DEBOUNCE_DURATION_MS = 80
40-
41-
# Button reads per second. Should be at least 20 for precise results
42-
FPS = 30
43-
44-
_ms_per_frame = 1000 / FPS
45-
46-
# How many frames it takes to reach the LONG_PRESS_DURATION_MS
47-
_long_press_frames = int(LONG_PRESS_DURATION_MS // _ms_per_frame)
48-
49-
# How many frames a button is disabled after release
50-
_debounce_frames = int(DEBOUNCE_DURATION_MS // _ms_per_frame)
51-
52-
_thread = None
53-
54-
# The amount of frames a button is held
55-
_counter = [0] * CHANNELS
56-
# Previous button state
57-
_previous = [0] * CHANNELS
58-
# Saves the lock state of a Button
59-
# NOTE: A button is getting locked to debounce or after long press to prevent that the short and long press event gets triggered simultaneously.
60-
_locked = [False] * CHANNELS
61-
6219
def __init__(self, config):
63-
self.config = config
64-
super().__init__()
65-
66-
def _run(self):
67-
sleep_time = 1.0 / self.FPS
68-
69-
while True:
70-
try:
71-
self.read()
72-
except:
73-
app_logger.error(f"MCP23009 connection issue! Resetting all buttons...")
74-
# if an I2C error occurs due to e.g. connection issues, reset all buttons
75-
for index in range(self.CHANNELS):
76-
self._reset_button(index)
77-
time.sleep(sleep_time)
78-
79-
def press_button(self, button, index):
80-
try:
81-
self.config.button_config.press_button("MCP23009", button, index)
82-
except:
83-
app_logger.warning(f"No button_config for button '{button}'")
84-
85-
def init_sensor(self):
86-
# Set GP0 to GP7 as inputs (1)
87-
self.bus.write_byte_data(self.SENSOR_ADDRESS, IODIR, 0xFF)
88-
# Enable pull-up resistors for GP0 to GP7
89-
self.bus.write_byte_data(self.SENSOR_ADDRESS, GPPU, 0xFF)
90-
91-
self._thread = Thread(target=self._run)
92-
self._thread.daemon = True
93-
self._thread.start()
94-
95-
def read(self):
96-
gpio_state = self.bus.read_byte_data(self.SENSOR_ADDRESS, GPIO)
97-
buttons_pressed = [
98-
not bool(gpio_state & (1 << i)) for i in range(self.CHANNELS)
99-
]
100-
101-
for i, pressed in enumerate(buttons_pressed):
102-
if pressed:
103-
if not self._locked[i]:
104-
self._counter[i] += 1
105-
106-
if self._counter[i] >= self._long_press_frames and not self._locked[i]:
107-
self._on_button_pressed(i, True)
108-
else:
109-
if self._locked[i]:
110-
if self._counter[i] <= self._debounce_frames:
111-
self._counter[i] += 1
112-
continue
113-
else:
114-
self._reset_button(i)
115-
elif not self._locked[i] and self._previous[i] != 0:
116-
self._on_button_pressed(i, False)
117-
self._previous = buttons_pressed
118-
119-
def _on_button_pressed(self, button_index, long_press):
120-
self._locked[button_index] = True
121-
self._counter[button_index] = 0
122-
self.press_button(f"GP{button_index}", int(long_press))
20+
i2c = busio.I2C(board.SCL, board.SDA)
21+
self.mcp = MCP(i2c, address=self.SENSOR_ADDRESS)
12322

124-
def _reset_button(self, button_index):
125-
self._locked[button_index] = False
126-
self._counter[button_index] = 0
23+
super().__init__(config, self.mcp)

0 commit comments

Comments
 (0)