• Skip to main content
  • Skip to header right navigation
  • Skip to site footer
  • YouTube
  • MakerWorld
  • Instagram
  • Threads
  • Bluesky
  • Mastodon
  • Facebook
makerhacks-logo

Maker Hacks

Ideas, news & tutorials for makers and hackers – Arduino/Raspberry Pi, 3D printing, robotics, laser cutting, and more

  • Home
  • About
  • 3D Printing
  • Laser Cutting
  • CNC
  • Programming
  • Free Arduino Course
  • Free Robotics Course
  • Recommendations

Use the MCP23017 GPIO Port Expander to Add 16 IO Pins

You are here: Home / Electronics and Hardware / Use the MCP23017 GPIO Port Expander to Add 16 IO Pins
Author: Chris Garrett

MCP23017/MCP23008 port expanders add more GPIO pins using I2C which means you only need two wires to get 8, 16 or even more lines of IO

Arduino, Raspberry Pi, Pico and ESP32 projects very often run out of GPIO pins, and that can be frustrating and limiting.

You could upgrade to a more expensive board that offers more IO, such as an Arduino Mega. There are ways to use shift registers too.

Alternatively, you can use “polling” techniques, where you scan rows and columns really quickly, which we will look at in a future project where I build my own dream hand-wired mechanical keyboard.

Instead, today I am going to show how you can use a port expander like the MCP23008 or MCP23017.

Why use MCP23008 or MCP23017?

The great advantage to using MCP23008 or MCP23017 is they work using I2C, and that means you only lose two pins to gain 8 or 16 GPIO, depending on the chip you buy.

Even better, because I2C can be daisy chained, you can have a whole bunch of these chips – up to 8, with unique addresses. That makes 128 I/O pins!

Not only can you add IO, but each pin can have an internal pull-up resistor, meaning fewer components, and there are interrupts so you don’t have to keep checking the inputs to see if they have changed.

You can use these chips with 3.3V or 5V devices, so it is a great, small, and inexpensive option for most of the projects you might come across.

As well as expanding your IO, it also protects your sensitive electronics – it can handle drawing around 20mA on each pin – and if it does fry, it is a lot cheaper to replace than some of our vintage and/or fragile computers.

They come as bare chips, as I have here, or many companies produce break-out boards and shields to make it even more convenient for prototyping.

More info is available at the official datasheet.

MCP23017 Pins and Wiring up to Arduino

MCP23017 Pin diagram
MCP23017 Pin diagram

The MCP23017 and MCP23008 are pretty similar except the MCP23017 has 16 IO pins, while the 08 has 8.

Confusingly, the power and ground are usually labelled as VDD (Power) and VSS (Ground) so I amended the diagram here to make it clearer.

The next two important pins are SCK and SDA, which are the I2C communication lines. They need to connect to your microcontroller I2C pins, for example A4 and A5 on the Arduino Uno.

There are three Address pins, A0, A1, A2, which are set high or low to configure the I2C address of the chip. Connecting all three to ground makes the address Hex 0x20.

Finally, the Reset pin needs to be held high (connect to 5v, for example) because it is activated when low.

Arduino blink sketch with MCP23017
Arduino blink sketch with MCP23017

Yeah using all this wiring to blink one LED seems a bit of a waste of time, but once you have one pin working it is super easy to then use the other 15!

If you are using Arduino, the best option is to add the Adafruit MCP23X17 library and use it like so:

#include <Adafruit_MCP23X17.h>
#define LED_PIN 8     // MCP23XXX pin LED is attached to

Adafruit_MCP23X17 mcp;

void setup() {

  // Wait for connection
  if (!mcp.begin_I2C()) {
    while (1);
  }

  // configure chosen pin as output
  mcp.pinMode(LED_PIN, OUTPUT);

}

void loop() {
  mcp.digitalWrite(LED_PIN, HIGH);
  delay(500);
  mcp.digitalWrite(LED_PIN, LOW);
  delay(500);
}

Using MCP23017 with Raspberry Pi Pico and MicroPython

If you don’t have ready access to a third-party library, never fear because controlling the chip and reading the inputs is done pretty simply via I2C registers.

On the MCP23017, there are two “banks” of pins. Those on the right are Bank A and those on the left are Bank B.

Each bank are addressed using registers to set which pins you wish to make input, which to make output, and which pins need pull-up resistors.

First we will look at MicroPython on the Pico:

from time import sleep
import machine

i2c = machine.I2C(0,
                  scl=machine.Pin(17),
                  sda=machine.Pin(16),)
i2cdevices = i2c.scan()
print(i2cdevices)
sleep(0.50)


i2c.writeto_mem(0x20, 0x00, b'\xFF')
i2c.writeto_mem(0x20, 0x01, b'\x00')
i2c.writeto_mem(0x20, 0x13, b'\x00')

while(1):
    
    i2c.writeto_mem(0x20, 0x13, b'\xFF')
    sleep(0.50)
    i2c.writeto_mem(0x20, 0x13, b'\x00')
    sleep(0.50)

As you can see, we have:

SCL on Pin 17

SDA on Pin 16

Power is 3v, and we also need Ground

Using MCP23017 with Raspberry Pi Pico
Using MCP23017 with Raspberry Pi Pico

We can see if our device is visible with the i2c.scan() which returns a list of I2C devices that have been detected, albeit using a brute-force method so don’t rely on it too much.

Our chip has been set to be 0x20, however, so we can write to that device and the relevant registers like this:

# Bank A
i2c.writeto_mem(0x20, 0x00, b'\xFF')
# Bank B
i2c.writeto_mem(0x20, 0x01, b'\x00')

Here we say we want Bank A (Hex 0) to be all inputs (hex FF), all Bank B (Hex 01) to be outputs (all 0). We turn the Led on and off using:

# All Bank B HIGH
i2c.writeto_mem(0x20, 0x13, b'\xFF')
# All Bank B LOW
i2c.writeto_mem(0x20, 0x13, b'\x00')

For input it is only slightly different, we just read from register 12, but we do set the pull-up resisters to be on, using register 0x0C:

from time import sleep
import machine

i2c = machine.I2C(0,
                  scl=machine.Pin(17),
                  sda=machine.Pin(16),)

i2c.writeto_mem(0x20, 0x00, b'\xFF')
i2c.writeto_mem(0x20, 0x01, b'\x00')
i2c.writeto_mem(0x20, 0x0C, b'\xFF')
i2c.writeto_mem(0x20, 0x13, b'\x00')

while(1):
    
    button = i2c.readfrom_mem(0x20, 0x12, 1)
    print(button)
    
    if (button!=b'\xFF'):
        i2c.writeto_mem(0x20, 0x13, b'\xFF')
    else:
        i2c.writeto_mem(0x20, 0x13, b'\x00')
    sleep(0.10)

MCP23017 with I2C on Neo6502

Neo6502 example using the MCP23017
Neo6502 example using the MCP23017

Here is the same Blink setup, but with the Neo6502.

Pin 1 – 5v

Pin 2 – Gnd

Pin 4 – SCK

Pin 5 – SDA

On the Neo6502 GPIO (Neo6502 is the fantastic credit-card sized 6502 computer) we can use the BASIC iwrite instruction to send our commands to the chip, Again, we are using Hex 20 as the I2C address:

iwrite $20, $0, $FF

This would set Bank A as Inputs ($FF in hex is 255 in decimal, which in binary means each pin is 1 or “On”). To set as output we set each “Off” or zero, as here where we set Bank B as outputs:

iwrite $20, $1, $0

To read or write there are the registers $12 for Bank A and $13 for Bank B. Therefore if you want to set all of Bank B “High”, aka switch all those pins “On”, we can do:

iwrite $20, $13, $FF

As it stands, we can read from Bank A and get the result of all the pins, high or low, using:

iread($20,$12)

The problem is the result won’t change next time we read it as it will “remember” it’s value, so we need to use a pull-up. The registers for Pull-Ups are $0C and $0D as appropriate, so we set Bank A to use them like so:

iwrite $20,$C,$FF

Here is a full BASIC listing for the Neo6502 using a MCP23017 to light an LED when a button is pressed:

Neo6502 basic listing
Neo6502 using a MCP23017 to light an LED when a button is pressed:
Category: Electronics and HardwareTag: arduino, neo6502, python, raspberry pi

About Chris Garrett

Marketing nerd by day, maker, retro gaming, tabletop war/roleplaying nerd by night. Co-author of the Problogger Book with Darren Rowse. Husband, Dad, 🇨🇦 Canadian.

Check out Retro Game Coders for retro gaming/computing.

☕️ Support Chris on Ko-Fi to get exclusive content and rewards!

Previous Post:PiUSB – How to Make a Wifi Memory Stick Using Pi Zero W
Next Post:Neo6502 ESP8266 Serial and WIFI

Sidebar

  • Facebook
  • Instagram
  • YouTube

Featured Posts

  • CyberBrick Review + Getting Started
  • xTool M2 Review
  • xTool F2 Ultra Review
  • xTool F2 Review
  • xTool S1 Review
  • Anycubic Kobra S1 Combo Review
  • Makera Z1 Pro Review
  • CNC Buyer's Guide
  • eufyMake E1 UV Printer Review
  • eufyMake Purifier P1
  • Z80 on a Breadboard
  • OpenSCAD MakerWorld Customizer

30 Days to Arduino

Arduino Tutorials
  • 3D Printing
  • CNC
  • Electronics and Hardware
  • Laser Cutting
  • News and Reviews
  • Software and Programming

arduino budget cad cnc diode laser glowforge Hacks Ideas laser cutter linux Makes making python raspberry pi Reviews technology tips xtool xTool Lasers

  • YouTube
  • MakerWorld
  • Instagram
  • Threads
  • Bluesky
  • Mastodon
  • Facebook

.

Maker Hacks Blog Copyright © 2026 · All Rights Reserved · Privacy Policy