diff --git a/lib/mcp23009e/README.md b/lib/mcp23009e/README.md index 747e0784..824c14bc 100644 --- a/lib/mcp23009e/README.md +++ b/lib/mcp23009e/README.md @@ -194,21 +194,24 @@ Toggles the reset pin to perform a hardware reset. ## Examples -The library includes several examples: - -* `buttons.py` - Simple button reading with polling -* `i2c_scan.py` - Scan I2C buses for connected devices -* `test_basic.py` - Basic driver functionality tests -* `test_interrupts.py` - Interrupt system demonstration -* `test_led_simple.py` - Basic active-low LED control example -* `test_output_active_low.py` - Active-low output tests with inverted logic -* `test_output.py` - GPIO output tests using low-level and Pin APIs -* `test_pin.py` - MCP23009Pin class usage examples -* `test_pin_irq.py` - Pin-compatible interrupt examples - +The library includes several practical examples: + +| Example | Description | +|-----------------------|------------| +| `buttons.py` | Simple D-PAD button reading using polling (no interrupts) | +| `i2c_scan.py` | Scan the I2C bus to detect connected devices | +| `reaction_timer.py` | Reaction time game using D-PAD buttons and interrupts (best of 5 rounds) | +| `simon.py` | Simon Says memory game using the D-PAD | +| `combination_lock.py` | Digital lock using a secret D-PAD sequence | +| `dpad_counter.py` | Simple state machine: increment/decrement/reset/print using D-PAD | +| `morse_code.py` | Morse code input using button press duration (dot/dash detection) | +| `binary_counter.py` | 4-bit binary counter displayed on GPIO outputs (GPIO1–GPIO4 / pins 0–3) | +| `dpad_piano.py` | Play musical notes with the D-PAD and buzzer (multi-press = higher octave) | +| `menu_navigation.py` | Minimal centered UI for navigating a menu on the SSD1327 OLED display | +| `sleep_on_button.py` | Low-power example: wake the board from sleep using MCP23009E interrupts | ### How to run ```python -mpremote mount lib/mcp23009e run lib/mcp23009e/examples/test_basic.py +mpremote mount lib/mcp23009e run lib/mcp23009e/examples/buttons.py ``` diff --git a/lib/mcp23009e/examples/binary_counter.py b/lib/mcp23009e/examples/binary_counter.py new file mode 100644 index 00000000..e16dae9c --- /dev/null +++ b/lib/mcp23009e/examples/binary_counter.py @@ -0,0 +1,87 @@ +""" +Binary counter using D-PAD inputs and MCP23009E GPIO outputs. + +UP -> increment +DOWN -> decrement +LEFT -> reset +RIGHT -> print current value + +The value is displayed on GPIO1..GPIO4 (expander pins 0..3) +as a 4-bit binary number. +""" + +from time import sleep_ms + +from machine import I2C, Pin +from mcp23009e import MCP23009E +from mcp23009e.const import * + +bus = I2C(1) +reset = Pin("RST_EXPANDER", Pin.OUT) + +mcp = MCP23009E(bus, address=MCP23009_I2C_ADDR, reset_pin=reset) + +BUTTONS = { + MCP23009_BTN_UP: "UP", + MCP23009_BTN_DOWN: "DOWN", + MCP23009_BTN_LEFT: "LEFT", + MCP23009_BTN_RIGHT: "RIGHT", +} + +OUTPUT_PINS = [MCP23009_GPIO1, MCP23009_GPIO2, MCP23009_GPIO3, MCP23009_GPIO4] + + +def wait_all_released(): + while True: + if all(mcp.get_level(pin_number) == MCP23009_LOGIC_HIGH for pin_number in BUTTONS): + return + sleep_ms(20) + + +def wait_for_button(): + wait_all_released() + while True: + for pin_number, name in BUTTONS.items(): + if mcp.get_level(pin_number) == MCP23009_LOGIC_LOW: + while mcp.get_level(pin_number) == MCP23009_LOGIC_LOW: + sleep_ms(20) + return name + sleep_ms(20) + + +def update_outputs(value): + for bit_index, pin_number in enumerate(OUTPUT_PINS): + bit_value = (value >> bit_index) & 0x01 + mcp.set_level(pin_number, bit_value) + + +for pin_number in BUTTONS: + mcp.setup(pin_number, MCP23009_DIR_INPUT, pullup=MCP23009_PULLUP) + +for pin_number in OUTPUT_PINS: + mcp.setup(pin_number, MCP23009_DIR_OUTPUT) + mcp.set_level(pin_number, MCP23009_LOGIC_LOW) + +counter = 0 +update_outputs(counter) + +print("=================================") +print("Binary Counter") +print("=================================\n") +print("Displaying the 4-bit value on GP0..GP3") +print("UP=+1 DOWN=-1 LEFT=reset RIGHT=print\n") + +while True: + button = wait_for_button() + + if button == "UP": + counter = (counter + 1) & 0x0F + elif button == "DOWN": + counter = (counter - 1) & 0x0F + elif button == "LEFT": + counter = 0 + elif button == "RIGHT": + pass + + update_outputs(counter) + print("Value:", counter, " Binary:", "{:04b}".format(counter)) diff --git a/lib/mcp23009e/examples/combination_lock.py b/lib/mcp23009e/examples/combination_lock.py new file mode 100644 index 00000000..5e96cb02 --- /dev/null +++ b/lib/mcp23009e/examples/combination_lock.py @@ -0,0 +1,73 @@ +""" +Digital combination lock using the MCP23009E D-PAD. + +Enter the secret button sequence to unlock. +A wrong input resets the attempt. +""" + +from time import sleep_ms + +from machine import I2C, Pin +from mcp23009e import MCP23009E +from mcp23009e.const import * + +bus = I2C(1) +reset = Pin("RST_EXPANDER", Pin.OUT) + +mcp = MCP23009E(bus, address=MCP23009_I2C_ADDR, reset_pin=reset) + +BUTTONS = { + MCP23009_BTN_UP: "UP", + MCP23009_BTN_DOWN: "DOWN", + MCP23009_BTN_LEFT: "LEFT", + MCP23009_BTN_RIGHT: "RIGHT", +} + +SECRET = ["UP", "UP", "DOWN", "LEFT", "RIGHT"] + + +def wait_all_released(): + while True: + if all(mcp.get_level(pin_number) == MCP23009_LOGIC_HIGH for pin_number in BUTTONS): + return + sleep_ms(20) + + +def wait_for_button(): + wait_all_released() + while True: + for pin_number, name in BUTTONS.items(): + if mcp.get_level(pin_number) == MCP23009_LOGIC_LOW: + while mcp.get_level(pin_number) == MCP23009_LOGIC_LOW: + sleep_ms(20) + return name + sleep_ms(20) + + +for pin_number in BUTTONS: + mcp.setup(pin_number, MCP23009_DIR_INPUT, pullup=MCP23009_PULLUP) + +print("=================================") +print("Combination Lock") +print("=================================\n") +print("Enter the secret D-PAD sequence.\n") + +entered = [] + +while True: + button = wait_for_button() + entered.append(button) + + print("Input:", "-".join(entered)) + + expected_prefix = SECRET[: len(entered)] + if entered != expected_prefix: + print("WRONG") + entered = [] + print() + continue + + if len(entered) == len(SECRET): + print("UNLOCKED") + entered = [] + print() diff --git a/lib/mcp23009e/examples/dpad_counter.py b/lib/mcp23009e/examples/dpad_counter.py new file mode 100644 index 00000000..194c5329 --- /dev/null +++ b/lib/mcp23009e/examples/dpad_counter.py @@ -0,0 +1,70 @@ +""" +Simple D-PAD counter. + +UP -> increment +DOWN -> decrement +LEFT -> reset +RIGHT -> print current value +""" + +from time import sleep_ms + +from machine import I2C, Pin +from mcp23009e import MCP23009E +from mcp23009e.const import * + +bus = I2C(1) +reset = Pin("RST_EXPANDER", Pin.OUT) + +mcp = MCP23009E(bus, address=MCP23009_I2C_ADDR, reset_pin=reset) + +BUTTONS = { + MCP23009_BTN_UP: "UP", + MCP23009_BTN_DOWN: "DOWN", + MCP23009_BTN_LEFT: "LEFT", + MCP23009_BTN_RIGHT: "RIGHT", +} + + +def wait_all_released(): + while True: + if all(mcp.get_level(pin_number) == MCP23009_LOGIC_HIGH for pin_number in BUTTONS): + return + sleep_ms(20) + + +def wait_for_button(): + wait_all_released() + while True: + for pin_number, name in BUTTONS.items(): + if mcp.get_level(pin_number) == MCP23009_LOGIC_LOW: + while mcp.get_level(pin_number) == MCP23009_LOGIC_LOW: + sleep_ms(20) + return name + sleep_ms(20) + + +for pin_number in BUTTONS: + mcp.setup(pin_number, MCP23009_DIR_INPUT, pullup=MCP23009_PULLUP) + +value = 0 + +print("=================================") +print("D-PAD Counter") +print("=================================\n") +print("UP=+1 DOWN=-1 LEFT=reset RIGHT=print\n") + +while True: + button = wait_for_button() + + if button == "UP": + value += 1 + print("Increment ->", value) + elif button == "DOWN": + value -= 1 + print("Decrement ->", value) + elif button == "LEFT": + value = 0 + print("Reset ->", value) + elif button == "RIGHT": + print("Current value ->", value) diff --git a/lib/mcp23009e/examples/dpad_piano.py b/lib/mcp23009e/examples/dpad_piano.py new file mode 100644 index 00000000..558b83eb --- /dev/null +++ b/lib/mcp23009e/examples/dpad_piano.py @@ -0,0 +1,160 @@ +""" +D-PAD piano example using the MCP23009E and the board buzzer. + +UP -> C +RIGHT -> D +DOWN -> E +LEFT -> F + +If two or more buttons are pressed at the same time, the higher octave is used. +MENU exits the piano mode. +""" + +from time import sleep_ms, sleep_us, ticks_diff, ticks_ms + +from machine import I2C, Pin +from mcp23009e import MCP23009E +from mcp23009e.const import * + +# Board pins +SPEAKER = Pin("SPEAKER", Pin.OUT) +MENU_BUTTON = Pin("MENU_BUTTON", Pin.IN, Pin.PULL_UP) + +# I2C and expander setup +i2c = I2C(1) +reset_expander = Pin("RST_EXPANDER", Pin.OUT) +mcp = MCP23009E(i2c, address=MCP23009_I2C_ADDR, reset_pin=reset_expander) + +# D-PAD mapping on the MCP23009E +BUTTONS = { + MCP23009_BTN_UP: "UP", + MCP23009_BTN_RIGHT: "RIGHT", + MCP23009_BTN_DOWN: "DOWN", + MCP23009_BTN_LEFT: "LEFT", +} + +# Low octave notes +NOTES_LOW = { + "UP": 262, # C4 + "RIGHT": 294, # D4 + "DOWN": 330, # E4 + "LEFT": 349, # F4 +} + +# High octave notes +NOTES_HIGH = { + "UP": 523, # C5 + "RIGHT": 587, # D5 + "DOWN": 659, # E5 + "LEFT": 698, # F5 +} + + +def tone(pin, freq, duration_ms): + """Generate a square wave on the buzzer pin.""" + if freq == 0: + sleep_ms(duration_ms) + return + + period_us = int(1_000_000 / freq) + half_period_us = period_us // 2 + end_time = ticks_ms() + duration_ms + + while ticks_diff(end_time, ticks_ms()) > 0: + pin.on() + sleep_us(half_period_us) + pin.off() + sleep_us(half_period_us) + + +def setup_buttons(): + """Configure D-PAD buttons as MCP23009E inputs with pull-ups.""" + for pin_number in BUTTONS: + mcp.setup(pin_number, MCP23009_DIR_INPUT, pullup=MCP23009_PULLUP) + + +def get_pressed_buttons(): + """Return a list of currently pressed D-PAD button names.""" + pressed = [] + + for pin_number, name in BUTTONS.items(): + if mcp.get_level(pin_number) == MCP23009_LOGIC_LOW: + pressed.append(name) + + return pressed + + +def select_frequency(pressed): + """Select the note frequency from the current pressed buttons.""" + if not pressed: + return 0, None, None + + note_name = pressed[0] + + if len(pressed) >= 2: + return NOTES_HIGH[note_name], note_name, "high" + + return NOTES_LOW[note_name], note_name, "low" + + +def wait_menu_release(): + """Wait until the MENU button is released.""" + while MENU_BUTTON.value() == 0: + sleep_ms(20) + + +def dpad_piano(): + """Main piano loop.""" + setup_buttons() + + print("=================================") + print("D-PAD Piano") + print("=================================") + print() + print("UP -> C") + print("RIGHT -> D") + print("DOWN -> E") + print("LEFT -> F") + print("Two or more buttons -> higher octave") + print("MENU -> exit") + print() + + last_note = None + last_octave = None + + try: + while True: + # MENU button is a direct MCU pin, not an MCP23009E pin + if MENU_BUTTON.value() == 0: + print("Menu button pressed, exiting piano.") + wait_menu_release() + break + + pressed = get_pressed_buttons() + + if pressed: + frequency, note_name, octave = select_frequency(pressed) + + if note_name != last_note or octave != last_octave: + print( + "Playing:", + note_name, + "-", + frequency, + "Hz", + "(" + octave + " octave)", + ) + last_note = note_name + last_octave = octave + + tone(SPEAKER, frequency, 60) + else: + last_note = None + last_octave = None + sleep_ms(20) + + except KeyboardInterrupt: + print("\nPiano stopped.") + + +dpad_piano() diff --git a/lib/mcp23009e/examples/menu_navigation.py b/lib/mcp23009e/examples/menu_navigation.py new file mode 100644 index 00000000..724de6a6 --- /dev/null +++ b/lib/mcp23009e/examples/menu_navigation.py @@ -0,0 +1,144 @@ +""" +Minimal menu navigation example using the MCP23009E D-PAD and SSD1327 OLED display. + +UP/DOWN -> move in the menu +RIGHT -> select +LEFT -> go back + +The UI stays in the center of the round display to avoid cropped corners. +""" + +from time import sleep_ms + +import ssd1327 +from machine import I2C, SPI, Pin +from mcp23009e import MCP23009E +from mcp23009e.const import * + +# Setup MCP23009E on I2C bus +i2c = I2C(1) +reset_expander = Pin("RST_EXPANDER", Pin.OUT) +mcp = MCP23009E(i2c, address=MCP23009_I2C_ADDR, reset_pin=reset_expander) + +# Setup SSD1327 display on SPI bus +spi = SPI(1) +dc = Pin("DATA_COMMAND_DISPLAY") +res = Pin("RST_DISPLAY") +cs = Pin("CS_DISPLAY") +display = ssd1327.WS_OLED_128X128_SPI(spi, dc, res, cs) + +# D-PAD button mapping +BUTTONS = { + MCP23009_BTN_UP: "UP", + MCP23009_BTN_DOWN: "DOWN", + MCP23009_BTN_LEFT: "LEFT", + MCP23009_BTN_RIGHT: "RIGHT", +} + +# Keep short labels for a compact centered UI +MENU_ITEMS = [ + ("Battery", "4.01 V"), + ("Press", "1013 hPa"), + ("Hum", "48.6 %"), +] + +# Central safe area for the round display +X0 = 28 +TITLE_Y = 28 +ITEM_Y = 46 +ITEM_SPACING = 14 +FOOTER_Y = 92 + + +def setup_buttons(): + """Configure all D-PAD buttons as inputs with pull-ups.""" + for pin_number in BUTTONS: + mcp.setup(pin_number, MCP23009_DIR_INPUT, pullup=MCP23009_PULLUP) + + +def wait_all_released(): + """Wait until all buttons are released.""" + while True: + all_released = True + for pin_number in BUTTONS: + if mcp.get_level(pin_number) == MCP23009_LOGIC_LOW: + all_released = False + break + if all_released: + return + sleep_ms(20) + + +def wait_for_button(): + """Wait for a button press and return its name.""" + wait_all_released() + + while True: + for pin_number, name in BUTTONS.items(): + if mcp.get_level(pin_number) == MCP23009_LOGIC_LOW: + while mcp.get_level(pin_number) == MCP23009_LOGIC_LOW: + sleep_ms(20) + return name + sleep_ms(20) + + +def draw_menu(selected_index): + """Draw the centered menu.""" + display.fill(0) + + display.text("MENU", 48, TITLE_Y, 15) + + for index, (label, _) in enumerate(MENU_ITEMS): + y = ITEM_Y + index * ITEM_SPACING + prefix = ">" if index == selected_index else " " + display.text(prefix + label, X0, y, 15) + + display.text("R:OK L:BACK", 24, FOOTER_Y, 8) + display.show() + + +def draw_detail(selected_index): + """Draw the centered detail screen.""" + name, value = MENU_ITEMS[selected_index] + + display.fill(0) + + display.text(name, 44, 34, 15) + display.text(value, 34, 56, 15) + display.text("LEFT BACK", 34, 88, 8) + + display.show() + + +def main(): + """Main UI loop.""" + setup_buttons() + + selected_index = 0 + in_detail = False + + draw_menu(selected_index) + + while True: + button = wait_for_button() + + if not in_detail: + if button == "UP": + selected_index = (selected_index - 1) % len(MENU_ITEMS) + draw_menu(selected_index) + + elif button == "DOWN": + selected_index = (selected_index + 1) % len(MENU_ITEMS) + draw_menu(selected_index) + + elif button == "RIGHT": + draw_detail(selected_index) + in_detail = True + + else: + if button == "LEFT": + draw_menu(selected_index) + in_detail = False + + +main() diff --git a/lib/mcp23009e/examples/morse_code.py b/lib/mcp23009e/examples/morse_code.py new file mode 100644 index 00000000..f4c6237a --- /dev/null +++ b/lib/mcp23009e/examples/morse_code.py @@ -0,0 +1,116 @@ +""" +Morse code input using the MCP23009E D-PAD. + +RIGHT short press -> dot +RIGHT long press -> dash +LEFT -> validate current letter +UP -> add space / start new word +DOWN -> clear current symbol buffer +""" + +from time import sleep_ms, ticks_diff, ticks_ms + +from machine import I2C, Pin +from mcp23009e import MCP23009E +from mcp23009e.const import * + +bus = I2C(1) +reset = Pin("RST_EXPANDER", Pin.OUT) + +mcp = MCP23009E(bus, address=MCP23009_I2C_ADDR, reset_pin=reset) + +DOT_DASH_PIN = MCP23009_BTN_RIGHT +VALIDATE_PIN = MCP23009_BTN_LEFT +SPACE_PIN = MCP23009_BTN_UP +CLEAR_PIN = MCP23009_BTN_DOWN + +LONG_PRESS_MS = 400 + +MORSE_TABLE = { + ".-": "A", + "-...": "B", + "-.-.": "C", + "-..": "D", + ".": "E", + "..-.": "F", + "--.": "G", + "....": "H", + "..": "I", + ".---": "J", + "-.-": "K", + ".-..": "L", + "--": "M", + "-.": "N", + "---": "O", + ".--.": "P", + "--.-": "Q", + ".-.": "R", + "...": "S", + "-": "T", + "..-": "U", + "...-": "V", + ".--": "W", + "-..-": "X", + "-.--": "Y", + "--..": "Z", +} + +for pin_number in (DOT_DASH_PIN, VALIDATE_PIN, SPACE_PIN, CLEAR_PIN): + mcp.setup(pin_number, MCP23009_DIR_INPUT, pullup=MCP23009_PULLUP) + +current_symbol = "" +decoded_text = "" + + +def wait_press_and_release(pin_number): + while mcp.get_level(pin_number) == MCP23009_LOGIC_HIGH: + sleep_ms(10) + + start = ticks_ms() + + while mcp.get_level(pin_number) == MCP23009_LOGIC_LOW: + sleep_ms(10) + + end = ticks_ms() + return ticks_diff(end, start) + + +print("=================================") +print("Morse Code Input") +print("=================================\n") +print("RIGHT short = dot") +print("RIGHT long = dash") +print("LEFT = validate letter") +print("UP = space") +print("DOWN = clear current symbol") +print() + +while True: + if mcp.get_level(DOT_DASH_PIN) == MCP23009_LOGIC_LOW: + duration = wait_press_and_release(DOT_DASH_PIN) + if duration >= LONG_PRESS_MS: + current_symbol += "-" + print("Dash ->", current_symbol) + else: + current_symbol += "." + print("Dot ->", current_symbol) + + elif mcp.get_level(VALIDATE_PIN) == MCP23009_LOGIC_LOW: + wait_press_and_release(VALIDATE_PIN) + letter = MORSE_TABLE.get(current_symbol, "?") + decoded_text += letter + print("Letter:", letter) + print("Text :", decoded_text) + current_symbol = "" + + elif mcp.get_level(SPACE_PIN) == MCP23009_LOGIC_LOW: + wait_press_and_release(SPACE_PIN) + decoded_text += " " + print("Text :", decoded_text) + + elif mcp.get_level(CLEAR_PIN) == MCP23009_LOGIC_LOW: + wait_press_and_release(CLEAR_PIN) + current_symbol = "" + print("Current symbol cleared") + + sleep_ms(10) diff --git a/lib/mcp23009e/examples/reaction_timer.py b/lib/mcp23009e/examples/reaction_timer.py new file mode 100644 index 00000000..bae9feeb --- /dev/null +++ b/lib/mcp23009e/examples/reaction_timer.py @@ -0,0 +1,119 @@ +""" +Reaction timer game using the MCP23009E D-PAD. + +Wait for "GO!", then press any D-PAD button as fast as possible. +The script measures the reaction time in milliseconds. +Best score over 5 rounds. +""" + +from time import sleep_ms, ticks_diff, ticks_ms + +import urandom +from machine import I2C, Pin +from mcp23009e import MCP23009E +from mcp23009e.const import * + +bus = I2C(1) +reset = Pin("RST_EXPANDER", Pin.OUT) +interrupt = Pin("INT_EXPANDER", Pin.IN) + +mcp = MCP23009E( + bus, + address=MCP23009_I2C_ADDR, + reset_pin=reset, + interrupt_pin=interrupt, +) + +BUTTONS = { + MCP23009_BTN_UP: "UP", + MCP23009_BTN_DOWN: "DOWN", + MCP23009_BTN_LEFT: "LEFT", + MCP23009_BTN_RIGHT: "RIGHT", +} + +pressed_button = None +press_time = None +waiting_for_press = False + + +def make_callback(pin_number, name): + def callback(): + global pressed_button, press_time, waiting_for_press + if waiting_for_press and pressed_button is None: + pressed_button = name + press_time = ticks_ms() + + return callback + + +for pin_number, name in BUTTONS.items(): + mcp.setup(pin_number, MCP23009_DIR_INPUT, pullup=MCP23009_PULLUP) + mcp.interrupt_on_falling(pin_number, make_callback(pin_number, name)) + +print("=================================") +print("Reaction Timer") +print("=================================\n") +print("Press any D-PAD button when GO! appears.") +print("Do not press too early.\n") + +scores = [] + +for round_index in range(5): + print("Round", round_index + 1) + + # Wait until all buttons are released + while True: + all_released = True + for pin_number in BUTTONS: + if mcp.get_level(pin_number) == MCP23009_LOGIC_LOW: + all_released = False + break + if all_released: + break + sleep_ms(20) + + delay_ms = 1000 + (urandom.getrandbits(12) % 4001) + elapsed = 0 + false_start = False + + while elapsed < delay_ms: + for pin_number, name in BUTTONS.items(): + if mcp.get_level(pin_number) == MCP23009_LOGIC_LOW: + print("Too early! False start on", name) + false_start = True + break + if false_start: + break + sleep_ms(10) + elapsed += 10 + + if false_start: + print() + continue + + pressed_button = None + press_time = None + waiting_for_press = True + go_time = ticks_ms() + + print("GO!") + + while pressed_button is None: + sleep_ms(1) + + waiting_for_press = False + reaction_ms = ticks_diff(press_time, go_time) + scores.append(reaction_ms) + + print("Button:", pressed_button) + print("Reaction time:", reaction_ms, "ms\n") + +if scores: + best = min(scores) + average = sum(scores) // len(scores) + print("Finished!") + print("Valid rounds:", len(scores), "/ 5") + print("Best:", best, "ms") + print("Average:", average, "ms") +else: + print("No valid round recorded.") diff --git a/lib/mcp23009e/examples/simon.py b/lib/mcp23009e/examples/simon.py new file mode 100644 index 00000000..d2c7bf08 --- /dev/null +++ b/lib/mcp23009e/examples/simon.py @@ -0,0 +1,91 @@ +""" +Simon Says memory game using the MCP23009E D-PAD. + +Watch the sequence printed on the console, then replay it on the D-PAD. +The sequence grows by one direction every round. +""" + +from time import sleep_ms + +import urandom +from machine import I2C, Pin +from mcp23009e import MCP23009E +from mcp23009e.const import * + +bus = I2C(1) +reset = Pin("RST_EXPANDER", Pin.OUT) + +mcp = MCP23009E(bus, address=MCP23009_I2C_ADDR, reset_pin=reset) + +BUTTONS = { + MCP23009_BTN_UP: "UP", + MCP23009_BTN_DOWN: "DOWN", + MCP23009_BTN_LEFT: "LEFT", + MCP23009_BTN_RIGHT: "RIGHT", +} + +PINS = list(BUTTONS.keys()) +sequence = [] + + +def wait_all_released(): + while True: + released = True + for pin_number in PINS: + if mcp.get_level(pin_number) == MCP23009_LOGIC_LOW: + released = False + break + if released: + return + sleep_ms(20) + + +def wait_for_button(): + wait_all_released() + while True: + for pin_number, name in BUTTONS.items(): + if mcp.get_level(pin_number) == MCP23009_LOGIC_LOW: + while mcp.get_level(pin_number) == MCP23009_LOGIC_LOW: + sleep_ms(20) + return name + sleep_ms(20) + + +for pin_number in PINS: + mcp.setup(pin_number, MCP23009_DIR_INPUT, pullup=MCP23009_PULLUP) + +print("=================================") +print("Simon Says") +print("=================================\n") +print("Repeat the sequence using the D-PAD.\n") + +score = 0 + +while True: + next_name = BUTTONS[PINS[urandom.getrandbits(8) % len(PINS)]] + sequence.append(next_name) + + print("Sequence:") + for item in sequence: + print(" ", item) + sleep_ms(500) + + print("\nYour turn:") + success = True + + for expected in sequence: + received = wait_for_button() + print("You pressed:", received) + if received != expected: + success = False + break + + if not success: + print("\nWrong sequence!") + print("Final score:", score) + break + + score += 1 + print("Correct! Score:", score) + print("-" * 30) + sleep_ms(800) diff --git a/lib/mcp23009e/examples/sleep_on_button.py b/lib/mcp23009e/examples/sleep_on_button.py new file mode 100644 index 00000000..71d6514d --- /dev/null +++ b/lib/mcp23009e/examples/sleep_on_button.py @@ -0,0 +1,88 @@ +""" +Low-power wake-on-button demo using the MCP23009E interrupt output. + +The board enters light sleep and wakes up when any D-PAD button is pressed. +After wake-up, the script prints which button caused the wake event and how long +the board slept. + +This example assumes the expander interrupt line is connected to a wake-capable +MCU pin named "INT_EXPANDER". +""" + +from time import sleep_ms, ticks_diff, ticks_ms + +from machine import I2C, Pin, lightsleep +from mcp23009e import MCP23009E +from mcp23009e.const import * + +bus = I2C(1) +reset = Pin("RST_EXPANDER", Pin.OUT) +interrupt = Pin("INT_EXPANDER", Pin.IN) + +mcp = MCP23009E( + bus, + address=MCP23009_I2C_ADDR, + reset_pin=reset, + interrupt_pin=interrupt, +) + +BUTTONS = { + MCP23009_BTN_UP: "UP", + MCP23009_BTN_DOWN: "DOWN", + MCP23009_BTN_LEFT: "LEFT", + MCP23009_BTN_RIGHT: "RIGHT", +} + +wake_button = None + + +def make_callback(name): + def callback(): + global wake_button + wake_button = name + + return callback + + +for pin_number, name in BUTTONS.items(): + mcp.setup(pin_number, MCP23009_DIR_INPUT, pullup=MCP23009_PULLUP) + mcp.interrupt_on_falling(pin_number, make_callback(name)) + +print("=================================") +print("Sleep on Button") +print("=================================\n") +print("Press any D-PAD button to wake the board.\n") + +while True: + wake_button = None + start = ticks_ms() + + # Wait until all buttons are released before sleeping + while True: + if all(mcp.get_level(pin_number) == MCP23009_LOGIC_HIGH for pin_number in BUTTONS): + break + sleep_ms(20) + + print("Going to light sleep...") + + # Duration can be removed if your platform supports external wake-up only. + lightsleep(60000) + + slept_ms = ticks_diff(ticks_ms(), start) + + # Give the expander interrupt logic a short time to settle + sleep_ms(20) + + if wake_button is None: + intf = mcp.get_intf() # which pins triggered + intcap = mcp.get_intcap() # latched GPIO state at interrupt time + + for pin_number, name in BUTTONS.items(): + mask = 1 << pin_number + if (intf & mask) and not (intcap & mask): + wake_button = name + break + + print("Woke up after", slept_ms, "ms") + print("Wake source:", wake_button if wake_button else "unknown") + print() diff --git a/lib/mcp23009e/examples/test_basic.py b/lib/mcp23009e/examples/test_basic.py deleted file mode 100644 index abb62032..00000000 --- a/lib/mcp23009e/examples/test_basic.py +++ /dev/null @@ -1,91 +0,0 @@ -""" -Exemple de test basique pour le driver MCP23009E -Ce script teste les fonctionnalités de base : configuration GPIO, lecture/écriture -""" - -from time import sleep - -from machine import I2C, Pin -from mcp23009e import MCP23009E -from mcp23009e.const import * - -# Configuration I2C (à adapter selon votre carte) -# Sur STeaMi, l'I2C1 est généralement utilisé -bus = I2C(1) - -# IMPORTANT : Le pin RST_EXPANDER n'a pas de pull-up, il DOIT être initialisé -# sinon le MCP23009E reste en état reset et ne répond pas sur I2C -reset = Pin("RST_EXPANDER", Pin.OUT) - -mcp = MCP23009E(bus, address=MCP23009_I2C_ADDR, reset_pin=reset) - -print("=================================") -print("Test du driver MCP23009E") -print("=================================\n") - -# Test 1 : Lecture des registres par défaut -print("Test 1 : Lecture des registres initiaux") -print(f" IODIR : 0x{mcp.get_iodir():02X} (devrait être 0xFF - toutes entrées)") -print(f" GPPU : 0x{mcp.get_gppu():02X} (devrait être 0x00 - pas de pull-up)") -print(f" GPINTEN: 0x{mcp.get_gpinten():02X} (devrait être 0x00 - pas d'interruptions)") -print(f" IOCON : 0x{mcp.get_iocon().get_register_value():02X} (devrait être 0x00)") - -print() - -# Test 2 : Configuration d'un GPIO en entrée avec pull-up -print("Test 2 : Configuration GPIO 7 en entrée avec pull-up") -mcp.setup(7, MCP23009_DIR_INPUT, pullup=MCP23009_PULLUP) -print(f" IODIR après setup: 0x{mcp.get_iodir():02X}") -print(f" GPPU après setup : 0x{mcp.get_gppu():02X}") -print() - -# Test 3 : Test d'un GPIO en sortie -print("\nTest 3 : Configuration GPIO en sortie") -for i in MCP23009_GPIOS: - mcp.setup(i, MCP23009_DIR_OUTPUT) - print(f" LED {i}: HIGH") - mcp.set_level(i, MCP23009_LOGIC_HIGH) - sleep(0.5) - print(f" LED {i}: LOW") - mcp.set_level(i, MCP23009_LOGIC_LOW) - sleep(0.5) -print() - -# Test 4 : Lecture du niveau logique des boutons du D-PAD -print("Test 4 : Lecture des boutons du D-PAD") -print(" (Les boutons sont normalement HIGH, appuyés = LOW)") -btn_names = { - MCP23009_BTN_UP: "UP", - MCP23009_BTN_DOWN: "DOWN", - MCP23009_BTN_LEFT: "LEFT", - MCP23009_BTN_RIGHT: "RIGHT", -} - -for btn_pin, btn_name in btn_names.items(): - # Configurer tous les boutons en entrée avec pull-up - mcp.setup(btn_pin, MCP23009_DIR_INPUT, pullup=MCP23009_PULLUP) - -print("\nAppuyez sur les boutons du D-PAD (Ctrl+C pour arrêter)...") -print("=" * 50) - -try: - while True: - # Lire l'état de tous les boutons - states = [] - for btn_pin, btn_name in btn_names.items(): - level = mcp.get_level(btn_pin) - if level == MCP23009_LOGIC_LOW: # Bouton appuyé - states.append(btn_name) - - if states: - print(f"Boutons appuyés: {', '.join(states)}") - - sleep(0.1) - -except KeyboardInterrupt: - print("\n\nTest terminé!") - - -print("\n=================================") -print("Tests terminés avec succès!") -print("=================================") diff --git a/lib/mcp23009e/examples/test_interrupts.py b/lib/mcp23009e/examples/test_interrupts.py deleted file mode 100644 index b2bd1f68..00000000 --- a/lib/mcp23009e/examples/test_interrupts.py +++ /dev/null @@ -1,96 +0,0 @@ -""" -Exemple de test des interruptions pour le driver MCP23009E -Ce script teste les fonctionnalités d'interruption sur les boutons du D-PAD -""" - -from time import sleep - -from machine import I2C, Pin -from mcp23009e import MCP23009E -from mcp23009e.const import * - -# Configuration I2C (à adapter selon votre carte) -bus = I2C(1) - -# IMPORTANT : Le pin RST_EXPANDER n'a pas de pull-up, il DOIT être initialisé -reset = Pin("RST_EXPANDER", Pin.OUT) - -# Pin d'interruption du MCP23009E -interrupt = Pin("INT_EXPANDER", Pin.IN) - -# Créer l'instance avec le pin d'interruption -mcp = MCP23009E(bus, address=MCP23009_I2C_ADDR, reset_pin=reset, interrupt_pin=interrupt) - -print("=================================") -print("Test des interruptions MCP23009E") -print("=================================\n") - -# Variables pour compter les événements -btn_counters = { - MCP23009_BTN_UP: {"name": "UP", "press": 0, "release": 0}, - MCP23009_BTN_DOWN: {"name": "DOWN", "press": 0, "release": 0}, - MCP23009_BTN_LEFT: {"name": "LEFT", "press": 0, "release": 0}, - MCP23009_BTN_RIGHT: {"name": "RIGHT", "press": 0, "release": 0}, -} - -# Callbacks pour les événements -def on_button_press(btn_pin): - """Callback appelé lors d'un appui sur un bouton""" - def callback(): - btn_counters[btn_pin]["press"] += 1 - btn_name = btn_counters[btn_pin]["name"] - print(f"[INTERRUPT] Bouton {btn_name} APPUYÉ") - return callback - -def on_button_release(btn_pin): - """Callback appelé lors du relâchement d'un bouton""" - def callback(): - btn_counters[btn_pin]["release"] += 1 - btn_name = btn_counters[btn_pin]["name"] - print(f"[INTERRUPT] Bouton {btn_name} RELÂCHÉ") - return callback - -def on_button_change(btn_pin): - """Callback appelé lors d'un changement d'état d'un bouton""" - def callback(level): - btn_name = btn_counters[btn_pin]["name"] - state = "RELÂCHÉ" if level == MCP23009_LOGIC_HIGH else "APPUYÉ" - print(f"[CHANGE] Bouton {btn_name} -> {state}") - return callback - -# Configuration des boutons avec interruptions -print("Configuration des interruptions sur les boutons...") -for btn_pin, info in btn_counters.items(): - # Configurer le GPIO en entrée avec pull-up - mcp.setup(btn_pin, MCP23009_DIR_INPUT, pullup=MCP23009_PULLUP) - - # Enregistrer les callbacks d'interruption - # Les boutons sont actifs à LOW (appuyés = LOW, relâchés = HIGH) - mcp.interrupt_on_falling(btn_pin, on_button_press(btn_pin)) # Appui - mcp.interrupt_on_raising(btn_pin, on_button_release(btn_pin)) # Relâchement - mcp.interrupt_on_change(btn_pin, on_button_change(btn_pin)) # Changement - -print("\n✓ Configuration terminée") -print("\nAppuyez sur les boutons du D-PAD (Ctrl+C pour arrêter)...") -print("Les interruptions seront affichées automatiquement") -print("=" * 50) - -try: - while True: - # Attendre les interruptions - # Le handler s'exécute automatiquement - sleep(1) - - # Afficher un résumé toutes les secondes - # (Optionnel, commentez si vous ne voulez que les événements) - # total = sum(c["press"] + c["release"] for c in btn_counters.values()) - # if total > 0: - # print(f"\n[Résumé] Total événements: {total}") - -except KeyboardInterrupt: - print("\n\n=================================") - print("Résumé des événements:") - print("=================================") - for btn_pin, info in btn_counters.items(): - print(f"{info['name']:6} - Appuis: {info['press']:3}, Relâchements: {info['release']:3}") - print("\nTest terminé!") diff --git a/lib/mcp23009e/examples/test_led_simple.py b/lib/mcp23009e/examples/test_led_simple.py deleted file mode 100644 index 8c9f691b..00000000 --- a/lib/mcp23009e/examples/test_led_simple.py +++ /dev/null @@ -1,108 +0,0 @@ -""" -Exemple simple d'utilisation des LEDs en configuration active-low - -Montage requis : 3.3V → [LED] → [Résistance 220-330Ω] → GPIO - -Cet exemple utilise MCP23009ActiveLowPin qui gère automatiquement -l'inversion de logique nécessaire pour ce type de montage. -""" - -from time import sleep - -from machine import I2C, Pin -from mcp23009e import MCP23009E, MCP23009ActiveLowPin -from mcp23009e.const import * - -# Configuration I2C -bus = I2C(1) -reset = Pin("RST_EXPANDER", Pin.OUT) - -# Créer l'instance du driver -mcp = MCP23009E(bus, address=MCP23009_I2C_ADDR, reset_pin=reset) - -print("=" * 50) -print("Test simple de LEDs - Configuration active-low") -print("=" * 50) -print("\nMontage: 3.3V → [LED] → [R] → GPIO\n") - -# Créer des objets LED (la logique est automatiquement inversée) -led0 = MCP23009ActiveLowPin(mcp, 0) -led1 = MCP23009ActiveLowPin(mcp, 1) -led2 = MCP23009ActiveLowPin(mcp, 2) -led3 = MCP23009ActiveLowPin(mcp, 3) - -leds = [led0, led1, led2, led3] - -print("✓ 4 LEDs configurées sur GPIO 0-3\n") - -# Test 1 : Allumer/éteindre chaque LED -print("Test 1 : Allumer/éteindre chaque LED") -print("-" * 50) -for i, led in enumerate(leds): - print(f"LED {i}: ON") - led.on() - sleep(0.5) - print(f"LED {i}: OFF") - led.off() - sleep(0.3) - -# Test 2 : Chenillard -print("\nTest 2 : Effet chenillard (3 cycles)") -print("-" * 50) -for cycle in range(3): - # Aller - for led in leds: - led.on() - sleep(0.15) - led.off() - # Retour - for led in reversed(leds): - led.on() - sleep(0.15) - led.off() - -# Test 3 : Clignotement synchrone -print("\nTest 3 : Clignotement synchrone (5 fois)") -print("-" * 50) -for i in range(5): - # Allumer toutes les LEDs - for led in leds: - led.on() - print("Toutes ON") - sleep(0.3) - - # Éteindre toutes les LEDs - for led in leds: - led.off() - print("Toutes OFF") - sleep(0.3) - -# Test 4 : Pattern alterné -print("\nTest 4 : Pattern alterné (5 fois)") -print("-" * 50) -for i in range(5): - # Pattern 1 : LED 0 et 2 allumées - led0.on() - led1.off() - led2.on() - led3.off() - print("Pattern: ON OFF ON OFF") - sleep(0.3) - - # Pattern 2 : LED 1 et 3 allumées - led0.off() - led1.on() - led2.off() - led3.on() - print("Pattern: OFF ON OFF ON") - sleep(0.3) - -# Éteindre toutes les LEDs -for led in leds: - led.off() - -print("\n" + "=" * 50) -print("Test terminé!") -print("=" * 50) -print("\nNotez que vous utilisez led.on() et led.off()") -print("normalement, sans vous soucier de la logique inversée!") diff --git a/lib/mcp23009e/examples/test_output.py b/lib/mcp23009e/examples/test_output.py deleted file mode 100644 index 8c97d571..00000000 --- a/lib/mcp23009e/examples/test_output.py +++ /dev/null @@ -1,219 +0,0 @@ -""" -Exemple de test des sorties pour le driver MCP23009E -Ce script teste la configuration des GPIO en sortie et le contrôle de leur niveau -""" -import time - -from machine import I2C, Pin -from mcp23009e import MCP23009E, MCP23009Pin -from mcp23009e.const import * - -# Configuration I2C -bus = I2C(1) - -# Configuration du pin de reset -reset = Pin("RST_EXPANDER", Pin.OUT) - -# Créer l'instance du driver -mcp = MCP23009E(bus, address=MCP23009_I2C_ADDR, reset_pin=reset) - -print("=================================") -print("Test des sorties MCP23009E") -print("=================================\n") - -# ===== Test 1 : Configuration de sorties avec l'API bas niveau ===== -print("Test 1 : API bas niveau (MCP23009E)") -print("-" * 50) - -# Configurer les GPIO 0-3 en sortie -print("Configuration des GPIO 0-3 en sortie...") -for gpio in range(4): - mcp.setup(gpio, MCP23009_DIR_OUTPUT) -print("✓ Configuration terminée\n") - -# Test séquentiel : allumer chaque GPIO un par un -print("Test séquentiel : allumer chaque GPIO pendant 0.5s") -for gpio in range(4): - print(f" GPIO {gpio}: HIGH") - mcp.set_level(gpio, MCP23009_LOGIC_HIGH) - sleep(0.5) - print(f" GPIO {gpio}: LOW") - mcp.set_level(gpio, MCP23009_LOGIC_LOW) - sleep(0.3) - -print() - -# Test simultané : allumer toutes les sorties ensemble -print("Test simultané : allumer toutes les sorties") -for gpio in range(4): - mcp.set_level(gpio, MCP23009_LOGIC_HIGH) -print(" Toutes les sorties: HIGH") -sleep(1) - -for gpio in range(4): - mcp.set_level(gpio, MCP23009_LOGIC_LOW) -print(" Toutes les sorties: LOW") -sleep(0.5) - -# ===== Test 2 : Configuration de sorties avec l'API Pin ===== -print("\n\nTest 2 : API Pin (MCP23009Pin)") -print("-" * 50) - -# Créer des objets Pin pour les GPIO 0-3 -pins = [] -print("Création des objets Pin...") -for i in range(4): - pin = MCP23009Pin(mcp, i, MCP23009Pin.OUT) - pins.append(pin) - print(f" Pin {i} créée: {pin}") -print() - -# Test des méthodes on()/off() -print("Test des méthodes on()/off()") -for i, pin in enumerate(pins): - print(f" Pin {i}: ON") - pin.on() - sleep(0.3) - print(f" Pin {i}: OFF") - pin.off() - sleep(0.3) - -print() - -# Test de la méthode toggle() -print("Test de la méthode toggle() - 5 cycles") -for cycle in range(5): - print(f" Cycle {cycle + 1}:") - for i, pin in enumerate(pins): - pin.toggle() - state = "ON" if pin.value() else "OFF" - print(f" Pin {i}: {state}") - sleep(0.5) - -# Éteindre toutes les pins -for pin in pins: - pin.off() - -# ===== Test 3 : Chenillard (Knight Rider effect) ===== -print("\n\nTest 3 : Effet chenillard (Knight Rider)") -print("-" * 50) -print("3 cycles d'aller-retour...") - -for cycle in range(3): - # Aller (0 -> 3) - for i in range(4): - pins[i].on() - sleep(0.15) - pins[i].off() - - # Retour (3 -> 0) - for i in range(3, -1, -1): - pins[i].on() - sleep(0.15) - pins[i].off() - -print("✓ Chenillard terminé") - -# ===== Test 4 : Compteur binaire ===== -print("\n\nTest 4 : Compteur binaire sur 4 bits (0-15)") -print("-" * 50) - -for count in range(16): - # Afficher le nombre en binaire sur les 4 GPIO - binary = f"{count:04b}" - print(f" Compteur: {count:2d} = {binary}") - - for i in range(4): - bit = int(binary[3 - i]) # LSB en premier (GPIO 0) - pins[i].value(bit) - - sleep(0.4) - -# Tout éteindre -for pin in pins: - pin.off() - -# ===== Test 5 : Contrôle direct du port (tous les GPIO en même temps) ===== -print("\n\nTest 5 : Contrôle du port complet (8 bits)") -print("-" * 50) - -# Configurer tous les GPIO en sortie -print("Configuration de tous les GPIO en sortie...") -for gpio in range(8): - mcp.setup(gpio, MCP23009_DIR_OUTPUT) - -# Test de patterns -patterns = [ - (0b00000000, "Tout éteint"), - (0b11111111, "Tout allumé"), - (0b10101010, "Pattern alterné 1"), - (0b01010101, "Pattern alterné 2"), - (0b11110000, "4 premiers allumés"), - (0b00001111, "4 derniers allumés"), -] - -print("\nTest de différents patterns:") -for pattern, description in patterns: - print(f" {description}: {pattern:08b}") - mcp.set_gpio(pattern) - sleep(0.5) - -# Tout éteindre -mcp.set_gpio(0x00) - -# ===== Test 6 : PWM logiciel (simulation) ===== -print("\n\nTest 6 : Simulation PWM logiciel sur GPIO 0") -print("-" * 50) -print("Variation de luminosité (si LED connectée)...") - -led = MCP23009Pin(mcp, 0, MCP23009Pin.OUT) - -# Augmenter progressivement -print(" Augmentation de la luminosité...") -for duty in range(0, 101, 10): - # Simuler le PWM avec on/off rapide - for _ in range(20): - led.on() - sleep(duty / 10000) # Temps ON - led.off() - sleep((100 - duty) / 10000) # Temps OFF - -# Diminuer progressivement -print(" Diminution de la luminosité...") -for duty in range(100, -1, -10): - for _ in range(20): - led.on() - sleep(duty / 10000) - led.off() - sleep((100 - duty) / 10000) - -led.off() - -# ===== Test 7 : Test de performance ===== -print("\n\nTest 7 : Test de performance") -print("-" * 50) - - -# Mesurer la vitesse de commutation -test_pin = MCP23009Pin(mcp, 0, MCP23009Pin.OUT) -cycles = 100 - -start = time.ticks_ms() -for _ in range(cycles): - test_pin.on() - test_pin.off() -end = time.ticks_ms() - -elapsed = time.ticks_diff(end, start) -freq = cycles / (elapsed / 1000) if elapsed > 0 else 0 - -print(f" {cycles} cycles en {elapsed} ms") -print(f" Vitesse de commutation: ~{freq:.1f} Hz") -print(f" Temps par commutation: ~{elapsed/cycles:.2f} ms") - -print("\n=================================") -print("Tous les tests terminés avec succès!") -print("=================================") -print("\nNote: Ces tests fonctionnent sans matériel externe.") -print("Pour voir les résultats visuellement, connectez des LEDs") -print("sur les GPIO avec des résistances appropriées (220-330Ω).") diff --git a/lib/mcp23009e/examples/test_output_active_low.py b/lib/mcp23009e/examples/test_output_active_low.py deleted file mode 100644 index 5202e831..00000000 --- a/lib/mcp23009e/examples/test_output_active_low.py +++ /dev/null @@ -1,246 +0,0 @@ -""" -Exemple de test des sorties pour le driver MCP23009E -CONFIGURATION ACTIVE-LOW : LED entre 3.3V et GPIO (cathode sur GPIO) - -Le MCP23009E peut absorber (sink) plus de courant qu'il ne peut en fournir (source). -Pour cette raison, les LEDs doivent être connectées ainsi : - 3.3V ---[LED]---[Résistance 220-330Ω]--- GPIO - -Avec ce montage : - - GPIO LOW → LED allumée (le MCP absorbe le courant) - - GPIO HIGH → LED éteinte (pas de courant) - -La logique est donc INVERSÉE par rapport à un montage classique. -""" - -from time import sleep - -from machine import I2C, Pin -from mcp23009e import MCP23009E, MCP23009Pin -from mcp23009e.const import * - -# Configuration I2C -bus = I2C(1) - -# Configuration du pin de reset -reset = Pin("RST_EXPANDER", Pin.OUT) - -# Créer l'instance du driver -mcp = MCP23009E(bus, address=MCP23009_I2C_ADDR, reset_pin=reset) - -print("=" * 60) -print("Test des sorties MCP23009E - Configuration ACTIVE-LOW") -print("=" * 60) -print("\nMontage: 3.3V → [LED] → [Résistance] → GPIO") -print("Logique: GPIO LOW = LED ON, GPIO HIGH = LED OFF") -print("=" * 60) -print() - -# ===== Classe helper pour inverser la logique ===== -class ActiveLowLED: - """ - Classe wrapper pour gérer une LED en configuration active-low - La logique est inversée : on() met le GPIO à LOW, off() le met à HIGH - """ - def __init__(self, mcp, gpio_num): - self.pin = MCP23009Pin(mcp, gpio_num, MCP23009Pin.OUT) - # Initialiser à HIGH (LED éteinte) - self.off() - - def on(self): - """Allume la LED (GPIO LOW)""" - self.pin.value(0) - - def off(self): - """Éteint la LED (GPIO HIGH)""" - self.pin.value(1) - - def toggle(self): - """Inverse l'état de la LED""" - self.pin.toggle() - - def value(self, x=None): - """ - Obtient ou définit l'état de la LED (logique inversée) - 1 = allumée, 0 = éteinte - """ - if x is None: - # Retourner l'état logique de la LED (inversé par rapport au GPIO) - return 1 - self.pin.value() - else: - # Écrire l'état (inversé) - self.pin.value(1 - x) - -# ===== Test 1 : Test basique avec logique inversée ===== -print("Test 1 : API avec logique inversée") -print("-" * 60) - -# Créer des LEDs avec logique inversée -leds = [] -print("Configuration des LED 0-3...") -for i in range(4): - led = ActiveLowLED(mcp, i) - leds.append(led) - print(f" LED {i} créée et initialisée (éteinte)") -print() - -# Test séquentiel -print("Test séquentiel : allumer chaque LED pendant 0.5s") -for i, led in enumerate(leds): - print(f" LED {i}: ON") - led.on() - sleep(0.5) - print(f" LED {i}: OFF") - led.off() - sleep(0.3) - -print() - -# Test simultané -print("Test simultané : allumer toutes les LEDs") -for led in leds: - led.on() -print(" Toutes les LEDs: ON") -sleep(1) - -for led in leds: - led.off() -print(" Toutes les LEDs: OFF") -sleep(0.5) - -# ===== Test 2 : Effet chenillard ===== -print("\n\nTest 2 : Effet chenillard (Knight Rider)") -print("-" * 60) -print("3 cycles d'aller-retour...") - -for cycle in range(3): - # Aller (0 -> 3) - for i in range(4): - leds[i].on() - sleep(0.15) - leds[i].off() - - # Retour (3 -> 0) - for i in range(3, -1, -1): - leds[i].on() - sleep(0.15) - leds[i].off() - -print("✓ Chenillard terminé") - -# ===== Test 3 : Compteur binaire ===== -print("\n\nTest 3 : Compteur binaire sur 4 bits (0-15)") -print("-" * 60) - -for count in range(16): - binary = f"{count:04b}" - print(f" Compteur: {count:2d} = {binary}") - - for i in range(4): - bit = int(binary[3 - i]) # LSB en premier (LED 0) - leds[i].value(bit) - - sleep(0.4) - -# Tout éteindre -for led in leds: - led.off() - -# ===== Test 4 : Test de toggle ===== -print("\n\nTest 4 : Test de toggle() - 5 cycles") -print("-" * 60) - -for cycle in range(5): - print(f" Cycle {cycle + 1}:") - for i, led in enumerate(leds): - led.toggle() - state = "ON" if led.value() else "OFF" - print(f" LED {i}: {state}") - sleep(0.5) - -# Éteindre toutes les LEDs -for led in leds: - led.off() - -# ===== Test 5 : Patterns sur 8 LEDs ===== -print("\n\nTest 5 : Patterns sur 8 LEDs (si disponibles)") -print("-" * 60) - -# Créer 4 LEDs supplémentaires -print("Configuration des LED 4-7...") -for i in range(4, 8): - led = ActiveLowLED(mcp, i) - leds.append(led) - -print() - -# Patterns intéressants (ATTENTION: logique inversée pour set_gpio) -patterns = [ - (0b11111111, "Tout éteint (GPIO HIGH)"), - (0b00000000, "Tout allumé (GPIO LOW)"), - (0b01010101, "Pattern alterné 1"), - (0b10101010, "Pattern alterné 2"), - (0b00001111, "4 premiers allumés"), - (0b11110000, "4 derniers allumés"), -] - -print("Test de différents patterns:") -for pattern, description in patterns: - print(f" {description}: {pattern:08b}") - # Attention: avec set_gpio, on écrit directement, donc la logique est inversée - mcp.set_gpio(pattern) - sleep(0.5) - -# Tout éteindre (set_gpio avec 0xFF = tous HIGH = toutes LEDs éteintes) -mcp.set_gpio(0xFF) - -# ===== Test 6 : PWM logiciel ===== -print("\n\nTest 6 : Simulation PWM logiciel sur LED 0") -print("-" * 60) -print("Variation de luminosité...") - -led0 = leds[0] - -# Augmenter progressivement -print(" Augmentation de la luminosité...") -for duty in range(0, 101, 10): - for _ in range(20): - led0.on() - sleep(duty / 10000) - led0.off() - sleep((100 - duty) / 10000) - -# Diminuer progressivement -print(" Diminution de la luminosité...") -for duty in range(100, -1, -10): - for _ in range(20): - led0.on() - sleep(duty / 10000) - led0.off() - sleep((100 - duty) / 10000) - -led0.off() - -# ===== Test 7 : Explication du fonctionnement ===== -print("\n\n" + "=" * 60) -print("EXPLICATION TECHNIQUE") -print("=" * 60) -print() -print("Le MCP23009E peut absorber (sink) plus de courant qu'il ne peut") -print("en fournir (source). Les spécifications typiques sont:") -print(" - Sink current (IOL): 25 mA max par pin") -print(" - Source current (IOH): ~1 mA par pin") -print() -print("C'est pourquoi le montage Active-Low est préféré:") -print() -print(" Montage Active-Low (recommandé):") -print(" 3.3V → [LED] → [R] → GPIO") -print(" LED ON quand GPIO = LOW (le MCP absorbe le courant)") -print() -print(" Montage Active-High (déconseillé):") -print(" GPIO → [R] → [LED] → GND") -print(" Courant trop faible pour allumer correctement la LED") -print() -print("=" * 60) -print("Tous les tests terminés avec succès!") -print("=" * 60) diff --git a/lib/mcp23009e/examples/test_pin.py b/lib/mcp23009e/examples/test_pin.py deleted file mode 100644 index 0e988f18..00000000 --- a/lib/mcp23009e/examples/test_pin.py +++ /dev/null @@ -1,131 +0,0 @@ -""" -Exemple d'utilisation de la classe MCP23009Pin -Cet exemple montre comment utiliser les GPIO du MCP23009E comme des pins normales -""" - -from time import sleep - -from machine import I2C, Pin -from mcp23009e import MCP23009E, MCP23009Pin -from mcp23009e.const import * - -# Configuration I2C -bus = I2C(1) - -# Configuration du pin de reset -reset = Pin("RST_EXPANDER", Pin.OUT) - -# Créer l'instance du driver MCP23009E -mcp = MCP23009E(bus, address=MCP23009_I2C_ADDR, reset_pin=reset) - -print("=================================") -print("Test de la classe MCP23009Pin") -print("=================================\n") - -# ===== Test 1 : Utilisation basique comme Pin ===== -print("Test 1 : Configuration et lecture/écriture") -print("-" * 50) - -# Créer des objets Pin pour les boutons du D-PAD -btn_up = MCP23009Pin(mcp, MCP23009_BTN_UP, MCP23009Pin.IN, MCP23009Pin.PULL_UP) -btn_down = MCP23009Pin(mcp, MCP23009_BTN_DOWN, MCP23009Pin.IN, MCP23009Pin.PULL_UP) -btn_left = MCP23009Pin(mcp, MCP23009_BTN_LEFT, MCP23009Pin.IN, MCP23009Pin.PULL_UP) -btn_right = MCP23009Pin(mcp, MCP23009_BTN_RIGHT, MCP23009Pin.IN, MCP23009Pin.PULL_UP) - -print("✓ Boutons configurés en entrée avec pull-up") - -# Afficher l'état initial -print("\nÉtat initial des boutons:") -print(f" UP: {btn_up.value()}") -print(f" DOWN: {btn_down.value()}") -print(f" LEFT: {btn_left.value()}") -print(f" RIGHT: {btn_right.value()}") - -# ===== Test 2 : Utilisation avec une LED (GPIO 0 par exemple) ===== -print("\n\nTest 2 : Contrôle d'une LED (optionnel)") -print("-" * 50) - -# Créer une pin en mode sortie pour une LED -led = MCP23009Pin(mcp, 0, MCP23009Pin.OUT) -print("✓ LED configurée sur GPIO 0") - -print("\nTest des méthodes on/off/toggle:") -for i in range(3): - print(f" Cycle {i+1}: ON") - led.on() - sleep(0.3) - print(f" Cycle {i+1}: OFF") - led.off() - sleep(0.3) - -print("\nTest de toggle:") -for i in range(4): - led.toggle() - state = "ON" if led.value() else "OFF" - print(f" Toggle {i+1}: {state}") - sleep(0.3) - -led.off() - -# ===== Test 3 : Lecture des boutons en continu ===== -print("\n\nTest 3 : Lecture en continu des boutons") -print("-" * 50) -print("Appuyez sur les boutons (Ctrl+C pour arrêter)...") - -buttons = { - "UP": btn_up, - "DOWN": btn_down, - "LEFT": btn_left, - "RIGHT": btn_right, -} - -try: - last_states = dict.fromkeys(buttons.keys()) - - while True: - for name, btn in buttons.items(): - # Les boutons sont actifs à LOW - state = btn.value() - - if last_states[name] != state: - if state == 0: # Bouton appuyé - print(f"[{name}] APPUYÉ") - else: # Bouton relâché - print(f"[{name}] RELÂCHÉ") - last_states[name] = state - - sleep(0.05) - -except KeyboardInterrupt: - print("\n\nTest terminé!") - -# ===== Test 4 : Utilisation avec l'API callable ===== -print("\n\nTest 4 : Utilisation avec l'API callable pin()") -print("-" * 50) - -# On peut aussi appeler directement la pin comme une fonction -test_pin = MCP23009Pin(mcp, 1, MCP23009Pin.OUT) -test_pin(1) # Équivalent à test_pin.value(1) -print(f"Pin 1 via callable: {test_pin()}") # Équivalent à test_pin.value() -test_pin(0) -print(f"Pin 1 via callable: {test_pin()}") - -# ===== Test 5 : Reconfiguration dynamique ===== -print("\n\nTest 5 : Reconfiguration dynamique") -print("-" * 50) - -dynamic_pin = MCP23009Pin(mcp, 2) -print(f"Pin 2 créée sans configuration: {dynamic_pin}") - -dynamic_pin.init(MCP23009Pin.OUT) -print(f"Pin 2 configurée en sortie: {dynamic_pin}") -dynamic_pin.on() -print(f"Pin 2 valeur: {dynamic_pin.value()}") - -dynamic_pin.init(MCP23009Pin.IN, MCP23009Pin.PULL_UP) -print(f"Pin 2 reconfigurée en entrée avec pull-up: {dynamic_pin}") -print(f"Pin 2 valeur: {dynamic_pin.value()}") - -print("\n=================================") -print("Tous les tests terminés avec succès!") -print("=================================") diff --git a/lib/mcp23009e/examples/test_pin_irq.py b/lib/mcp23009e/examples/test_pin_irq.py deleted file mode 100644 index c87854f1..00000000 --- a/lib/mcp23009e/examples/test_pin_irq.py +++ /dev/null @@ -1,97 +0,0 @@ -""" -Exemple d'utilisation de MCP23009Pin avec interruptions -Montre comment utiliser la méthode irq() compatible avec machine.Pin -""" - -from time import sleep - -from machine import I2C, Pin -from mcp23009e import MCP23009E, MCP23009Pin -from mcp23009e.const import * - -# Configuration I2C -bus = I2C(1) - -# Configuration des pins -reset = Pin("RST_EXPANDER", Pin.OUT) -interrupt = Pin("INT_EXPANDER", Pin.IN) - -# Créer l'instance du driver avec support des interruptions -mcp = MCP23009E(bus, address=MCP23009_I2C_ADDR, reset_pin=reset, interrupt_pin=interrupt) - -print("=" * 60) -print("Test des interruptions avec MCP23009Pin") -print("=" * 60) - -# Compteurs d'événements -event_counts = { - "UP": 0, - "DOWN": 0, - "LEFT": 0, - "RIGHT": 0, -} - -# Callback pour les interruptions -def button_callback(pin): - """ - Fonction appelée lors d'une interruption sur un bouton - Compatible avec l'API Pin de MicroPython - """ - # Identifier le bouton - btn_names = { - MCP23009_BTN_UP: "UP", - MCP23009_BTN_DOWN: "DOWN", - MCP23009_BTN_LEFT: "LEFT", - MCP23009_BTN_RIGHT: "RIGHT", - } - - btn_name = btn_names.get(pin._pin_number, "UNKNOWN") - state = "APPUYÉ" if pin.value() == 0 else "RELÂCHÉ" - - event_counts[btn_name] += 1 - print(f"[IRQ] {btn_name:5} {state:8} (événement #{event_counts[btn_name]})") - -# Configuration des boutons avec interruptions -print("\nConfiguration des boutons avec interruptions...") -print("-" * 60) - -# Créer les pins pour les boutons -btn_up = MCP23009Pin(mcp, MCP23009_BTN_UP, MCP23009Pin.IN, MCP23009Pin.PULL_UP) -btn_down = MCP23009Pin(mcp, MCP23009_BTN_DOWN, MCP23009Pin.IN, MCP23009Pin.PULL_UP) -btn_left = MCP23009Pin(mcp, MCP23009_BTN_LEFT, MCP23009Pin.IN, MCP23009Pin.PULL_UP) -btn_right = MCP23009Pin(mcp, MCP23009_BTN_RIGHT, MCP23009Pin.IN, MCP23009Pin.PULL_UP) - -# Configurer les interruptions (appui ET relâchement) -btn_up.irq(handler=button_callback, trigger=MCP23009Pin.IRQ_FALLING | MCP23009Pin.IRQ_RISING) -btn_down.irq(handler=button_callback, trigger=MCP23009Pin.IRQ_FALLING | MCP23009Pin.IRQ_RISING) -btn_left.irq(handler=button_callback, trigger=MCP23009Pin.IRQ_FALLING | MCP23009Pin.IRQ_RISING) -btn_right.irq(handler=button_callback, trigger=MCP23009Pin.IRQ_FALLING | MCP23009Pin.IRQ_RISING) - -print("✓ Interruptions configurées sur tous les boutons") -print(" Trigger: IRQ_FALLING | IRQ_RISING (appui et relâchement)") - -print("\n" + "=" * 60) -print("Appuyez sur les boutons du D-PAD (Ctrl+C pour arrêter)") -print("=" * 60) -print() - -try: - # Boucle principale - les interruptions sont gérées automatiquement - while True: - sleep(1) - -except KeyboardInterrupt: - print("\n") - print("=" * 60) - print("Résumé des événements") - print("=" * 60) - for btn_name, count in event_counts.items(): - print(f" {btn_name:5}: {count:3} événements") - print() - print("Test terminé!") - -print("\n" + "=" * 60) -print("Note: L'API irq() de MCP23009Pin est compatible avec machine.Pin") -print("Vous pouvez remplacer un Pin natif par un MCP23009Pin sans") -print("changer votre code existant!") -print("=" * 60)