|
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 |
12 | 5 |
|
13 | 6 | # https://www.microchip.com/en-us/product/mcp23009
|
14 | 7 | # https://ww1.microchip.com/downloads/en/DeviceDoc/20002121C.pdf
|
15 | 8 |
|
16 |
| -### MCP23009 Register definitions ### |
17 |
| -IODIR = 0x00 |
18 |
| -GPIO = 0x09 |
19 |
| -GPPU = 0x06 |
20 | 9 |
|
| 10 | +# NOTE: no need to set TEST and RESET address and value, due to adafruit_mcp230xx library handling it. |
| 11 | +class MCP23009(ButtonIOExpander): |
21 | 12 |
|
22 |
| -class MCP23009(i2c.i2c): |
23 | 13 | # address
|
24 | 14 | SENSOR_ADDRESS = 0x27
|
25 | 15 |
|
26 |
| - RESET_ADDRESS = IODIR |
27 |
| - RESET_VALUE = 0xFF |
28 |
| - |
29 |
| - TEST_ADDRESS = IODIR |
30 |
| - TEST_VALUE = (0xFF,) |
31 |
| - |
32 | 16 | # The amount of available channels (8 for MCP23009)
|
33 | 17 | CHANNELS = 8
|
34 | 18 |
|
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 |
| - |
62 | 19 | 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) |
123 | 22 |
|
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