This library provides a complete driver to control the MCP23009E I/O expander on the STeaMi board.
- Full GPIO control: Configure pins as input/output, read/write levels
- Pull-up resistors: Built-in pull-up support
- Interrupt support: Hardware interrupts with callback system
- Pin-compatible API:
MCP23009Pinclass compatible withmachine.Pin - Register access: Low-level register access for advanced usage
The base I²C address is `0x20` (A0=A1=A2=LOW). On the STeaMi board, the address is `0x20`.
from machine import I2C, Pin
from mcp23009e import MCP23009E
from mcp23009e.const import MCP23009_DIR_INPUT, MCP23009_I2C_ADDR, MCP23009_PULLUP
# Initialize I2C and reset pin
bus = I2C(1)
reset = Pin("RST_EXPANDER", Pin.OUT)
# Create driver instance
mcp = MCP23009E(bus, address=MCP23009_I2C_ADDR, reset_pin=reset)
# Configure a GPIO as input with pull-up
mcp.setup(7, MCP23009_DIR_INPUT, pullup=MCP23009_PULLUP)
# Read the level
level = mcp.get_level(7)
print(f"GPIO 7 level: {level}")
# Configure a GPIO as output
mcp.setup(0, MCP23009_DIR_OUTPUT)
mcp.set_level(0, MCP23009_LOGIC_HIGH)The MCP23009Pin class provides a machine.Pin-compatible interface:
from machine import I2C, Pin
from mcp23009e import MCP23009E, MCP23009Pin
from mcp23009e.const import MCP23009_I2C_ADDR
# Initialize
bus = I2C(1)
reset = Pin("RST_EXPANDER", Pin.OUT)
mcp = MCP23009E(bus, address=MCP23009_I2C_ADDR, reset_pin=reset)
# Create a Pin object for a button
btn = MCP23009Pin(mcp, 7, MCP23009Pin.IN, MCP23009Pin.PULL_UP)
print(f"Button state: {btn.value()}")
# Create a Pin object for a LED
led = MCP23009Pin(mcp, 0, MCP23009Pin.OUT)
led.on() # Turn on
led.off() # Turn off
led.toggle() # Toggle statefrom machine import I2C, Pin
from mcp23009e import MCP23009E, MCP23009Pin
from mcp23009e.const import MCP23009_I2C_ADDR
# Initialize with interrupt pin
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)
# Create pin and configure interrupt
btn = MCP23009Pin(mcp, 7, MCP23009Pin.IN, MCP23009Pin.PULL_UP)
def callback(pin):
print(f"Button state changed: {pin.value()}")
btn.irq(handler=callback, trigger=MCP23009Pin.IRQ_FALLING | MCP23009Pin.IRQ_RISING)MCP23009E(i2c, address, reset_pin, interrupt_pin=None)setup(gpx, direction, pullup, polarity)- Configure a GPIOset_level(gpx, level)- Set output levelget_level(gpx)- Read input levelinterrupt_on_change(gpx, callback)- Register change callbackinterrupt_on_falling(gpx, callback)- Register falling edge callbackinterrupt_on_raising(gpx, callback)- Register rising edge callbackdisable_interrupt(gpx)- Disable interrupts on a GPIO
MCP23009Pin(mcp, pin_number, mode=-1, pull=-1, value=None)init(mode, pull, value)- (Re)configure the pinvalue(x=None)- Get or set pin valueon()- Set pin highoff()- Set pin lowtoggle()- Toggle pin stateirq(handler, trigger)- Configure interruptmode(mode=None)- Get or set modepull(pull=None)- Get or set pull configuration
MCP23009Pin.IN/MCP23009Pin.OUT- Pin modesMCP23009Pin.PULL_UP- Pull-up configurationMCP23009Pin.IRQ_FALLING/MCP23009Pin.IRQ_RISING- Interrupt triggers
Special pin class for active-low configurations (LEDs connected between VCC and GPIO).
The MCP23009E can sink more current (25mA) than it can source (~1mA). For LEDs and similar loads, use this configuration:
3.3V → [LED] → [Resistor 220-330Ω] → GPIO
With MCP23009ActiveLowPin, the logic is automatically inverted:
led.on()→ GPIO LOW → LED lights upled.off()→ GPIO HIGH → LED turns off
from mcp23009e import MCP23009E, MCP23009ActiveLowPin
mcp = MCP23009E(bus, address=MCP23009_I2C_ADDR, reset_pin=reset)
# Create an active-low LED
led = MCP23009ActiveLowPin(mcp, 0)
led.on() # LED lights up (GPIO goes LOW)
led.off() # LED turns off (GPIO goes HIGH)
led.toggle() # Toggle LED stateThe API is identical to MCP23009Pin - just use MCP23009ActiveLowPin instead.
The driver provides simple hardware power management helpers through the reset pin:
mcp.power_off()Holds the reset pin low.
mcp.power_on()Releases the reset pin.
mcp.reset()Toggles the reset pin to perform a hardware reset.
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 |
mpremote mount lib/mcp23009e run lib/mcp23009e/examples/buttons.py