IO Expander MicroPython
The Pimoroni IO Expander Breakout is a Nuvoton MS51 microcontroller that gives you 14 extra pins over i2c. Eight of them can read a 12-bit ADC, six can drive a 16-bit PWM output, and four rotary encoders can be counted in hardware.
This driver also underpins the RGB Encoder, RGB Potentiometer and Encoder Wheel breakouts, which are all fundamentally an IO Expander with something wired to its pins.
Installing
Install with mip:
import mip
mip.install("github:pimoroni/ioexpander-micropython/package.json")
Or with Thonny, via Tools -> Manage Packages, searching for ioexpander-micropython.
Getting Started
from machine import I2C, Pin
from ioexpander import IOExpander
i2c = I2C(0, sda=Pin(20), scl=Pin(21))
ioe = IOExpander(i2c)
The breakout ships at address 0x18. Pins are numbered 1 to 14, matching the silkscreen.
Pin Modes
Every pin has to be told what it is before you use it:
from ioexpander import ADC, IN, IN_PU, OD, OUT, PWM
ioe.set_mode(1, OUT) # Push-pull output
ioe.set_mode(2, IN) # Input, high impedance
ioe.set_mode(3, IN_PU) # Input with pull-up
ioe.set_mode(4, OD) # Open-drain output
ioe.set_mode(5, PWM) # PWM output
ioe.set_mode(14, ADC) # Analog input
Not every pin can do everything. Pins 1 to 6 are PWM only, pins 10, 11, 13 and 14 are ADC only,
and pins 7, 8, 9 and 12 can be either. Setting an unsupported mode raises a ValueError.
Two extra settings apply to particular modes:
ioe.set_mode(2, IN, schmitt_trigger=True) # Cleans up a noisy input
ioe.set_mode(5, PWM, invert=True) # For a common anode LED
Reading and Writing
input() returns a digital 0 or 1, or a 12-bit count from a pin in ADC mode:
value = ioe.input(14)
volts = ioe.input_as_voltage(14)
Voltages are scaled against the ADC reference, which defaults to 3.3V:
ioe.set_adc_vref(5.0)
output() writes a digital LOW or HIGH, or a PWM duty cycle:
from ioexpander import HIGH, LOW
ioe.output(1, HIGH)
ioe.output(5, 128)
PWM
All six PWM channels share one counter and the period and clock divider are global. The period sets how many steps of duty cycle you get. The divider sets how fast the counter runs:
ioe.set_pwm_period(255) # 256 steps of duty cycle
ioe.set_pwm_control(2) # Divide the 24MHz clock by 2
Or set a frequency and let the driver pick both:
period = ioe.set_pwm_frequency(1000)
New duty cycles are buffered until a load. output() loads by default, which is one i2c
transaction per channel. To update several channels in step, use load=Talse until the last one:
ioe.output(1, r, load=False)
ioe.output(7, g, load=False)
ioe.output(2, b)
This combines multiple transactions into one, speeding things up slightly.
Rotary Encoders
The expander counts up to four rotary encoders in hardware, so you don't have to poll fast enough to catch every transition:
ioe.setup_rotary_encoder(1, pin_a=12, pin_b=3, pin_c=11)
count = ioe.read_rotary_encoder(1)
ioe.clear_rotary_encoder(1)
pin_c is the encoder's common terminal, if it is wired to a pin rather than to ground. Pass
count_microsteps=True to count every state change rather than every detent.
The expander counts into a signed 8-bit register. read_rotary_encoder() tracks the wraparound
for you and returns a count that keeps growing. You must read it often enough that the count doesn't
move more than 128 between reads.
Interrupts
The expander can drive its INT pin when a watched input changes, so you can wait on a pin
instead of polling over i2c:
from machine import Pin
interrupt = Pin(22, Pin.IN, Pin.PULL_UP)
ioe = IOExpander(i2c, interrupt=interrupt)
ioe.set_pin_interrupt(3, True)
if ioe.get_interrupt_flag():
...
ioe.clear_interrupt_flag()
Without a pin, get_interrupt_flag() reads the flag over i2c instead.
Changing the Address
set_address() writes a new address into the expander's flash, where it persists across power
cycles:
ioe.set_address(0x20)
Changelog
0.0.1
- Initial Release
Release files for ioexpander-micropython 0.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ioexpander_micropython-0.0.1.tar.gz | 16.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ioexpander_micropython-0.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 26.1 kB
Release files / ioexpander_micropython-0.0.1.tar.gz
| Download URL | ioexpander_micropython-0.0.1.tar.gz |
|---|---|
| Size | 16.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
230db6d5434f1b9437737bfd4dbdc9fcf5841e71219723e111eafe64549b402b
|
|
BLAKE2b-256 checksum How to use checksums |
f3fe9fe89356336b4c70ef6b7cc21737493170aefa171faf16c953e8669dc45f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.15 {"installer":{"name":"uv","version":"0.12.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / ioexpander_micropython-0.0.1-py3-none-any.whl
| Download URL | ioexpander_micropython-0.0.1-py3-none-any.whl |
|---|---|
| Size | 9.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
518792f04b94c3004fd0adaf9144b799830304b5eeeee5553d565e9d3aa1759f
|
|
BLAKE2b-256 checksum How to use checksums |
1f883a1bfa76744d45c0871216cf0c8ce4940dffc44c0b0eaae005c30c041f8c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.15 {"installer":{"name":"uv","version":"0.12.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|