Port expander MCP23017

If you want to control many I/O ports with a microcontroller you can use shift registers like the 74HC595. This solution is quite cheap but requires three lines to control and only works in one direction. If you also want to read in data you need another chip like the 74HC165 and additional lines if you want to have this parallel to the output.

A much more flexible solution is the port expander MCP23017. This chip will be controlled using I2C and provides 16 ports which can be configured individually as input or output.

There is also the variant MCP23008 with less ports and the variant MCP23S17 with SPI instead of I2C.


The variant with a DIP28 package has a pinout as following:


Pin Function Description Pin Function Description
1 GPB0 I/O-Port 8 28 GPA7 I/O-Port 7
2 GPB1 I/O-Port 9 27 GPA6 I/O-Port 6
3 GPB2 I/O-Port 10 26 GPA5 I/O-Port 5
4 GPB3 I/O-Port 11 25 GPA4 I/O-Port 4
5 GPB4 I/O-Port 12 24 GPA3 I/O-Port 3
6 GPB5 I/O-Port 13 23 GPA2 I/O-Port 2
7 GPB6 I/O-Port 14 22 GPA1 I/O-Port 1
8 GPB7 I/O-Port 15 21 GPA0 I/O-Port 0
9 VDD +1,8 V bis +5,5 V 20 INTA
11 N.C. / CS SPI 18 RESET
12 SCL / SCK I2C SCL / SPI 17 A2 I2C-Adresse
13 SDA / SI I2C SDA / SPI 16 A1 I2C-Adresse
14 N.C. / SO SPI 15 A0 I2C-Adresse


The supply voltage should be between 1.8 und 5.5 volts. Therefore the use with a microcontroller which works at 3.3 volts is no problem.


The variants with I2C (MCP23017, MCP23008) will be controlled using SCL and SDA.

For SPI (MCP23S17, MCP23S08) you use CS, SCK, SI and SO.


These pins provide the 16 I/O ports. Each port can be configured individually as input or output. The ports are grouped to group A and B. If you configure the ports you can also do this in groups.

When used as an input, a 100 kΩ pull-up resistor can optionally be activated for each port.


With this input the chip can be reset. To do this you have to pull down the input to ground (VSS) temporarily. For the regular operation you can also connect this pin to VDD continously.


These inputs are the interrupt inputs. The polarity can be configured and you can also connect both inputs together.


The inputs A0-A2 define the I2C address as following:

A0 A1 A2 I2C address
0 0 0 0x20 (32)
1 0 0 0x21 (33)
0 1 0 0x22 (34)
1 1 0 0x23 (35)
0 0 1 0x24 (36)
1 0 1 0x25 (37)
0 1 1 0x26 (38)
1 1 1 0x27 (39)

This allows to use up to 8 port expanders together which then provide up to 128 I/O ports.

Controlling with Adafruit_MCP23X17

Adafruit provides a library to control the chips MCP23017, MCP23S17, MCP23008 and MCP23S08.

Description on arduino.cc

Source code on Github

On Github you also find examples how to use the library. As a minimal example following the control of 4 LEDs on GPA0-GPA3 as “running light”:

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_MCP23X17.h>

Adafruit_MCP23X17 mcp;

void setup() {
  if (!mcp.begin_I2C()) {
    while (1);

  mcp.pinMode(0, OUTPUT);
  mcp.pinMode(1, OUTPUT);
  mcp.pinMode(2, OUTPUT);
  mcp.pinMode(3, OUTPUT);

void loop() {
  mcp.digitalWrite(0, HIGH);
  mcp.digitalWrite(0, LOW);
  mcp.digitalWrite(1, HIGH);
  mcp.digitalWrite(1, LOW);
  mcp.digitalWrite(2, HIGH);
  mcp.digitalWrite(2, LOW);
  mcp.digitalWrite(3, HIGH);
  mcp.digitalWrite(3, LOW);

The complete build with a D1 mini on a breadboard:

Test setup with a D1 mini and MCP23017