Installing Library

To use the MCP230xx you'll need to install the Adafruit CircuitPython MCP230xx library on your CircuitPython board.

First make sure you are running the latest version of Adafruit CircuitPython for your board.

Next you'll need to install the necessary libraries to use the hardware--carefully follow the steps to find and install these libraries from Adafruit's CircuitPython library bundle.  Our introduction guide has a great page on how to install the library bundle for both express and non-express boards.

Remember for non-express boards like the, you'll need to manually install the necessary libraries from the bundle:

  • adafruit_mcp230xx.mpy
  • adafruit_bus_device

You can also download the adafruit_mcp230xx.mpy from its releases page on Github.

Before continuing make sure your board's lib folder or root filesystem has the adafruit_mcp230xx.mpy, and adafruit_bus_device files and folders copied over.

Next connect to the board's serial REPL so you are at the CircuitPython >>> prompt.


To demonstrate the usage of the device we'll initialize it and control the chip's digital I/O lines from the Python REPL.

Run the following code to import the necessary modules and initialize the I2C connection:

import board
import busio
import adafruit_mcp230xx
i2c = busio.I2C(board.SCL, board.SDA)

Remember if you're using a board that doesn't support hardware I2C (like the ESP8266) you need to use the bitbangio module instead:

import board
import bitbangio
import adafruit_mcp230xx
i2c = bitbangio.I2C(board.SCL, board.SDA)

Then create an instance of either the MCP23017 or MCP23008 class depending on which chip you're using.  For example the MCP23017:

mcp = adafruit_mcp230xx.MCP23017(i2c)

Or the MCP23008:

mcp = adafruit_mcp230xx.MCP23008(i2c)

Either class initializer can also be passed an optional address keyword to override the I2C address, for example if you set any of the A0, A1, or A2 pins as described in the datasheet.  By default the chip's 0x20 I2C address will be assumed (with A0, A1, A2 all grounded).

Next you're ready to control the GPIO ports of the chip.  This is as easy as using a DigitalInOut instance in CircuitPython, the MCP230xx library makes each chip pin look like CircuitPython DigitalInOut class.  You just need to call the get_pin function to retrieve an instance of the chip's DigitalInOut class. 

For example to create GPIO0 (or GPIOA0 on the MCP23017) as a digital output:

pin0 = mcp.get_pin(0)

Then toggle the pin high or low by controlling its value property, just like using the DigitalInOut class.  Try connecting the cathode or anode of a LED to the output to see it turn on or off (or use a multimeter to measure the output voltage).

pin0.value = True  # GPIO0 / GPIOA0 to high logic level
pin0.value = False # GPIO0 / GPIOA0 to low logic level

You can also set a pin as an input, again using the same functions and properties as the DigitalInOut class.  For example here's how to create GPIO1 / GPIOA1 as a digital input with a pull-up resistor enabled:

import digitalio
pin1 = mcp.get_pin(1)
pin1.direction = digitalio.Direction.INPUT
pin1.pull = digitalio.Pull.UP

Note that pull-down resistors are not supported by the chip.  If you need them you'll have to add external resistors yourself!

Again read the state of the input with the value property like a DigitalInOut instance.  Notice since the pull-up resistor is enabled if the pin is not connected it will read True / high logic level.  Connect the pin to ground and you'll see it read False / low logic level.


For the MCP23008 you can get a pin instance for any pin numbered 0 to 7.  These correspond to the GPIO0 to GPIO7 pins of the chip.

For the MCP23017 you can get a pin instance for any pin numbered 0 to 15.  These correspond to the GPIOA0 to GPIOA7, then GPIOB0 to GPIOB7 pins.  For example pin 12 corresponds to GPIOB4 on the MCP23017.

That's all there is to using the MCP230xx I2C I/O extender with CircuitPython!

Below is a complete example that will blink pin 0 and read the state of pin 1 with a pull-up resistor enabled.  Save this as on your board (being careful to switch to bitbangio if required for your board), then open the serial REPL to see the output:

# Simple demo of reading and writing the digital I/O of the MCP2300xx as if
# they were native CircuitPython digital inputs/outputs.
# Author: Tony DiCola
import time

import board
import busio
import digitalio

import adafruit_mcp230xx

# Initialize the I2C bus:
i2c = busio.I2C(board.SCL, board.SDA)

# Create an instance of either the MCP23008 or MCP23017 class depending on
# which chip you're using:
mcp = adafruit_mcp230xx.MCP23008(i2c)  # MCP23008
#mcp = adafruit_mcp230xx.MCP23017(i2c)  # MCP23017

# Optionally change the address of the device if you set any of the A0, A1, A2
# pins.  Specify the new address with a keyword parameter:
#mcp = adafruit_mcp230xx.MCP23017(i2c, address=0x21)  # MCP23017 w/ A0 set

# Now call the get_pin function to get an instance of a pin on the chip.
# This instance will act just like a digitalio.DigitalInOut class instance
# and has all the same properties and methods (except you can't set pull-down
# resistors, only pull-up!).  For the MCP23008 you specify a pin number from 0
# to 7 for the GP0...GP7 pins.  For the MCP23017 you specify a pin number from
# 0 to 15 for the GPIOA0...GPIOA7, GPIOB0...GPIOB7 pins (i.e. pin 12 is GPIOB4).
pin0 = mcp.get_pin(0)
pin1 = mcp.get_pin(1)

# Setup pin0 as an output that's at a high logic level.

# Setup pin1 as an input with a pull-up resistor enabled.  Notice you can also
# use properties to change this state.
pin1.direction = digitalio.Direction.INPUT
pin1.pull = digitalio.Pull.UP

# Now loop blinking the pin 0 output and reading the state of pin 1 input.
while True:
    # Blink pin 0 on and then off.
    pin0.value = True
    pin0.value = False
    # Read pin 1 and print its state.
    print('Pin 1 is at a high level: {0}'.format(pin1.value))
Last updated on 2018-03-08 at 09.22.55 AM Published on 2018-03-08 at 09.22.57 AM