Inquire: Call 0086-755-23203480, or reach out via the form below/your sales contact to discuss our design, manufacturing, and assembly capabilities.
Quote: Email your PCB files to Sales@pcbsync.com (Preferred for large files) or submit online. We will contact you promptly. Please ensure your email is correct.
Notes: For PCB fabrication, we require PCB design file in Gerber RS-274X format (most preferred), *.PCB/DDB (Protel, inform your program version) format or *.BRD (Eagle) format. For PCB assembly, we require PCB design file in above mentioned format, drilling file and BOM. Click to download BOM template To avoid file missing, please include all files into one folder and compress it into .zip or .rar format.
After designing control panels requiring dozens of buttons, LEDs, and switches, I’ve learned that running out of GPIO pins on <a href=”https://pcbsync.com/arduino/”>Arduino</a> happens faster than most makers expect. The Arduino Uno provides only 20 digital I/O pins—after allocating pins for serial communication, I2C, SPI, and basic project requirements, you’re left with precious few for sensors, displays, and controls. The MCP23017 Arduino combination solves this limitation elegantly, adding 16 additional GPIO pins while consuming only two I2C pins for communication. Even better, the clever I2C addressing scheme enables connecting up to eight MCP23017 chips on a single bus, potentially expanding to 128 GPIO pins total.
The maker community often tries complex workarounds when facing GPIO shortages—multiplexing LEDs, using shift registers, or upgrading to expensive Mega boards. These approaches add complexity and cost while the MCP23017 Arduino integration provides straightforward expansion at minimal expense. Understanding proper implementation transforms GPIO limitations from project roadblocks into non-issues. Whether building LED matrix displays, keypad matrices, relay control panels, or sensor networks, mastering MCP23017 usage enables professional-grade projects without GPIO constraints.
From understanding I2C addressing and pin configuration to implementing interrupt-driven input monitoring, from controlling LED arrays to reading button matrices, this comprehensive tutorial covers everything needed to leverage the MCP23017’s capabilities effectively in real-world Arduino projects.
Understanding the MCP23017 GPIO Expander
Before wiring circuits and writing code, understanding the MCP23017’s capabilities and limitations ensures choosing it for appropriate applications.
MCP23017 Key Specifications
Specification
Value
Practical Impact
GPIO Pins
16 (two 8-bit ports)
Double Uno’s digital pins
Interface
I2C (2-wire)
Uses only SCL/SDA
Operating Voltage
1.8V to 5.5V
Works with 3.3V and 5V systems
Output Current
25mA per pin
Drives LEDs directly
Input Pull-ups
Configurable 100kΩ
Simplifies button interfacing
Clock Speed
Up to 1.7MHz (I2C)
Fast response time
I2C Addresses
8 configurable
Chain up to 8 modules
Package Options
DIP-28, SOIC-28, SSOP-28
DIP for breadboarding
Port Structure: The MCP23017 organizes pins into two 8-bit ports labeled PORTA (GPA0-GPA7) and PORTB (GPB0-GPB7). Each port can be configured independently, enabling flexible applications like using PORTA for outputs (LEDs) while PORTB handles inputs (buttons).
Current Capabilities: Each pin sources or sinks 25mA maximum, sufficient for standard LEDs with current-limiting resistors. The entire chip has a combined maximum current rating of 125mA, preventing powering 16 LEDs at 25mA simultaneously—design accordingly.
Require analog inputs: CD4051 analog multiplexer or additional ADCs
Speed critical: Direct Arduino pins always faster than I2C communication
MCP23017 Pinout and I2C Addressing
Understanding physical connections and addressing prevents common wiring mistakes and enables multi-chip configurations.
MCP23017 Pin Configuration
The MCP23017 comes in a 28-pin DIP package (breadboard-friendly) with logical pin grouping:
Pin Number(s)
Pin Name
Function
21-28
GPA0-GPA7
Port A GPIO pins
1-8
GPB0-GPB7
Port B GPIO pins
9
VDD
Power supply (1.8-5.5V)
10
VSS
Ground
12
SCL
I2C Clock
13
SDA
I2C Data
18
RESET
Active-low reset (connect to VDD via 10kΩ)
15-17
A0, A1, A2
Address selection pins
19, 20
INTA, INTB
Interrupt outputs (optional)
11
NC
Not connected
Power Connections: VDD (pin 9) connects to 5V or 3.3V depending on your Arduino logic level. VSS (pin 10) connects to ground. Add 0.1µF ceramic capacitor between VDD and VSS close to chip for decoupling.
Reset Pin: Pin 18 must be HIGH for normal operation. Connect directly to VDD or through 10kΩ pull-up resistor. Pulling LOW resets the chip to default state.
I2C Address Configuration
The MCP23017’s base I2C address is 0x20 (binary: 0100 A2 A1 A0). The three address pins (A0, A1, A2) modify the final three bits:
A2
A1
A0
I2C Address
Hex Value
0
0
0
32
0x20
0
0
1
33
0x21
0
1
0
34
0x22
0
1
1
35
0x23
1
0
0
36
0x24
1
0
1
37
0x25
1
1
0
38
0x26
1
1
1
39
0x27
Address Selection: Connect address pins to GND for logic LOW or VDD for logic HIGH. The default configuration (all three grounded) results in address 0x20—the most common setup for single-chip applications.
Multiple MCP23017 Setup: When using multiple chips, assign unique addresses to each. Connect the second chip’s A0 to VDD (address 0x21), third chip’s A1 to VDD (address 0x22), etc. This enables controlling 128 total GPIO pins (8 chips × 16 pins) on one I2C bus.
Wiring MCP23017 to Arduino
Proper physical connections ensure reliable I2C communication and correct GPIO operation.
I2C Pull-up Resistors: The I2C bus requires pull-up resistors (typically 4.7kΩ or 10kΩ) on both SDA and SCL lines. Many Arduino boards include these pull-ups internally, but adding external 4.7kΩ resistors from SDA/SCL to 5V improves reliability, especially with long wires or multiple I2C devices.
Decoupling Capacitor: Place 0.1µF ceramic capacitor between VDD (pin 9) and VSS (pin 10) as close to the chip as possible. This filters power supply noise preventing erratic behavior.
Connecting Outputs (LEDs)
LED Connection to MCP23017 Pin:
MCP23017 GPIO pin → 220Ω resistor → LED anode (+)
LED cathode (-) → GND
The 220Ω resistor limits current to safe levels (~20mA with 5V supply). Calculate appropriate resistor value: R = (VDD – LED_forward_voltage) / desired_current
Multiple LED Connections: Connect LEDs to as many GPIO pins as needed, remembering the combined current limitation (125mA total across all pins). Eight LEDs at 15mA each consume 120mA—approaching the limit.
Connecting Inputs (Buttons)
Button with Internal Pull-up:
Button terminal 1 → MCP23017 GPIO pin
Button terminal 2 → GND
Enable internal pull-up in software
The MCP23017’s configurable 100kΩ internal pull-ups eliminate external resistor requirements. When button presses, pin reads LOW; when released, internal pull-up holds pin HIGH.
External Pull-up (if preferred):
10kΩ resistor → 5V to MCP23017 GPIO pin
Button between GPIO pin and GND
External pull-ups provide stronger signal but require additional components. Use when signal integrity concerns exist or faster response needed.
Installing and Using MCP23017 Library
Software integration requires proper library installation and understanding basic functions.
Adafruit MCP23X17 Library Installation
Library Manager Method (recommended):
Open Arduino IDE → Sketch → Include Library → Manage Libraries
Download from GitHub: https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library
Extract ZIP to Arduino/libraries folder
Restart Arduino IDE
Basic LED Blink Example
#include <Adafruit_MCP23X17.h>
Adafruit_MCP23X17 mcp;
void setup() {
Serial.begin(9600);
// Initialize MCP23017 at default address 0x20
if (!mcp.begin_I2C()) {
Serial.println(“MCP23017 not found!”);
while(1);
}
// Configure pin 0 as output
mcp.pinMode(0, OUTPUT);
Serial.println(“MCP23017 initialized”);
}
void loop() {
mcp.digitalWrite(0, HIGH); // LED on
delay(500);
mcp.digitalWrite(0, LOW); // LED off
delay(500);
}
This basic example demonstrates core functions: initialization, pin configuration, and digital output control. The syntax mirrors standard Arduino functions making transition seamless.
Reading Button Input
#include <Adafruit_MCP23X17.h>
Adafruit_MCP23X17 mcp;
void setup() {
Serial.begin(9600);
if (!mcp.begin_I2C()) {
Serial.println(“Error initializing MCP23017!”);
while(1);
}
// Configure pin 8 as input with pull-up
mcp.pinMode(8, INPUT_PULLUP);
Serial.println(“Ready”);
}
void loop() {
// Read button state (LOW when pressed)
if (mcp.digitalRead(8) == LOW) {
Serial.println(“Button pressed!”);
delay(200); // Simple debounce
}
}
The INPUT_PULLUP mode enables MCP23017’s internal 100kΩ pull-up resistor, simplifying button interfacing without external components.
Advanced MCP23017 Features and Applications
Beyond basic I/O, the MCP23017 offers features enabling sophisticated applications.
Controlling Multiple Devices (LED Array)
#include <Adafruit_MCP23X17.h>
Adafruit_MCP23X17 mcp;
void setup() {
Serial.begin(9600);
mcp.begin_I2C();
// Configure all PORTA pins (0-7) as outputs
for(int pin = 0; pin < 8; pin++) {
mcp.pinMode(pin, OUTPUT);
}
}
void loop() {
// Running light effect
for(int pin = 0; pin < 8; pin++) {
mcp.digitalWrite(pin, HIGH);
delay(100);
mcp.digitalWrite(pin, LOW);
}
}
This example demonstrates sequential LED control creating a “running light” effect across eight LEDs connected to PORTA.
Port-Wide Operations
Writing to pins individually works but inefficient when controlling multiple pins simultaneously. The MCP23017 supports port-wide operations:
void setup() {
mcp.begin_I2C();
// Configure entire PORTA as outputs using register write
mcp.writeGPIOAB(0x00FF); // PORTA outputs (0x00), PORTB outputs (0xFF)
}
void loop() {
// Turn on all PORTA LEDs simultaneously
mcp.writeGPIOA(0xFF);
delay(500);
// Turn off all PORTA LEDs
mcp.writeGPIOA(0x00);
delay(500);
}
Port-wide operations execute single I2C transaction controlling 8 pins versus eight separate transactions—significantly faster for synchronized outputs.
Using Multiple MCP23017 Chips
#include <Adafruit_MCP23X17.h>
Adafruit_MCP23X17 mcp1; // Address 0x20
Adafruit_MCP23X17 mcp2; // Address 0x21
void setup() {
Serial.begin(9600);
// Initialize first chip at default address
if (!mcp1.begin_I2C(0x20)) {
Serial.println(“MCP1 initialization failed!”);
}
// Initialize second chip at address 0x21
if (!mcp2.begin_I2C(0x21)) {
Serial.println(“MCP2 initialization failed!”);
}
// Configure pins on both chips
mcp1.pinMode(0, OUTPUT);
mcp2.pinMode(0, OUTPUT);
}
void loop() {
// Control LEDs on both chips
mcp1.digitalWrite(0, HIGH);
mcp2.digitalWrite(0, LOW);
delay(500);
mcp1.digitalWrite(0, LOW);
mcp2.digitalWrite(0, HIGH);
delay(500);
}
Each MCP23017 object manages one chip at its unique I2C address, enabling independent control of 32 total GPIO pins (16 per chip).
Interrupt-Driven Input Monitoring
The MCP23017 includes interrupt capability notifying Arduino when input pins change state—far more efficient than constantly polling inputs:
#include <Adafruit_MCP23X17.h>
Adafruit_MCP23X17 mcp;
const int INTERRUPT_PIN = 2; // Arduino interrupt pin
Interrupt mode dramatically reduces I2C traffic by eliminating constant polling. The MCP23017 only interrupts Arduino when input state changes.
Practical Application: 4×4 Keypad Matrix
Matrix keypads efficiently read multiple buttons using fewer pins through row-column scanning:
#include <Adafruit_MCP23X17.h>
Adafruit_MCP23X17 mcp;
// Keypad configuration
const int ROWS = 4;
const int COLS = 4;
// Map MCP23017 pins
int rowPins[ROWS] = {0, 1, 2, 3}; // PORTA
int colPins[COLS] = {8, 9, 10, 11}; // PORTB
char keys[ROWS][COLS] = {
{‘1’, ‘2’, ‘3’, ‘A’},
{‘4’, ‘5’, ‘6’, ‘B’},
{‘7’, ‘8’, ‘9’, ‘C’},
{‘*’, ‘0’, ‘#’, ‘D’}
};
void setup() {
Serial.begin(9600);
mcp.begin_I2C();
// Configure rows as outputs, columns as inputs with pull-ups
for(int i = 0; i < ROWS; i++) {
mcp.pinMode(rowPins[i], OUTPUT);
mcp.digitalWrite(rowPins[i], HIGH);
}
for(int i = 0; i < COLS; i++) {
mcp.pinMode(colPins[i], INPUT_PULLUP);
}
}
char getKey() {
for(int row = 0; row < ROWS; row++) {
// Set current row LOW
mcp.digitalWrite(rowPins[row], LOW);
// Check each column
for(int col = 0; col < COLS; col++) {
if(mcp.digitalRead(colPins[col]) == LOW) {
// Key pressed, return it
char key = keys[row][col];
// Wait for release
while(mcp.digitalRead(colPins[col]) == LOW);
// Restore row HIGH
mcp.digitalWrite(rowPins[row], HIGH);
return key;
}
}
// Restore row HIGH
mcp.digitalWrite(rowPins[row], HIGH);
}
return 0; // No key pressed
}
void loop() {
char key = getKey();
if(key) {
Serial.print(“Key pressed: “);
Serial.println(key);
}
delay(100);
}
This keypad reader scans 16 buttons using only 8 GPIO pins through matrix scanning—demonstrating GPIO expander efficiency for input-heavy applications.
Useful Resources for MCP23017 Development
Official Documentation
MCP23017 Datasheet: https://ww1.microchip.com/downloads/en/devicedoc/20001952c.pdf Complete technical reference from Microchip including register descriptions and electrical specifications.
Adafruit MCP23017 Guide: https://learn.adafruit.com/adafruit-mcp23017-i2c-gpio-expander Comprehensive tutorial with wiring diagrams, library usage, and example projects.
Adafruit MCP23X17 Library: https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library Official Arduino library source code with examples and documentation.
Hardware Suppliers
Adafruit MCP23017 Breakout: https://www.adafruit.com/product/732 Quality breakout board with headers and STEMMA QT connectors.
SparkFun Qwiic GPIO: https://www.sparkfun.com/products/17047 Alternative breakout with Qwiic connectors for solderless connections.
Alternative Libraries and Tools
MCP23017 Arduino Library by Bertrand Lemasle: https://github.com/blemasle/arduino-mcp23017 Alternative library with different feature set and API.
I2C Scanner Sketch: https://playground.arduino.cc/Main/I2cScanner/ Useful tool for detecting I2C device addresses on your bus.
Frequently Asked Questions
Q: Why doesn’t my MCP23017 respond when I try to communicate?
A: Check these common issues: (1) Verify I2C wiring—SDA to A4, SCL to A5 on Arduino Uno (different pins on Mega: SDA pin 20, SCL pin 21), (2) Confirm address pins (A0, A1, A2) match your code—all grounded = 0x20, (3) Check RESET pin—must be HIGH (connected to VDD), (4) Measure voltage at VDD pin—should match Arduino supply (5V or 3.3V), (5) Run I2C scanner sketch to detect device presence and confirm address. If scanner doesn’t find device, hardware connection problem exists. Add or strengthen I2C pull-up resistors (4.7kΩ) if wires exceed 6 inches.
Q: Can I mix 3.3V and 5V devices on the same I2C bus?
A: Yes, with precautions. The MCP23017 operates from 1.8V to 5.5V, so power it from whichever voltage your system uses. For I2C communication, the bus voltage determines logic levels. If mixing 3.3V and 5V devices, power MCP23017 from 3.3V and ensure I2C pull-ups connect to 3.3V (not 5V). Most 5V Arduino tolerates 3.3V I2C levels adequately. For guaranteed compatibility, use level shifters between 3.3V and 5V I2C segments. GPIO pins should only connect to circuits matching MCP23017’s supply voltage.
Q: How fast can I read/write MCP23017 GPIO pins?
A: Speed depends on I2C clock frequency (default 100kHz). Each I2C transaction (reading or writing single pin) requires ~0.5-1ms including communication overhead. Reading 16 pins individually takes ~8-16ms versus ~1ms reading entire ports simultaneously via readGPIOAB(). For high-speed applications (>1kHz sampling), direct Arduino pins outperform I2C expanders. MCP23017 excels for moderate-speed applications (buttons, LEDs, relays) where wiring simplicity matters more than maximum speed. The MCP23017’s 1.7MHz I2C capability enables faster operation if your Arduino supports it, but improvement remains modest compared to direct GPIO.
Q: How many LEDs can I control from one MCP23017?
A: Theoretically 16 (one per GPIO pin), but practical limit is ~5-6 at full brightness. Each pin sources/sinks 25mA maximum, but the entire chip’s combined current must not exceed 125mA. Six LEDs at 20mA each = 120mA, approaching the limit. For controlling more LEDs, reduce brightness (higher current-limiting resistors) or use external drivers (transistors, MOSFET arrays, LED driver chips). For matrix displays, use LED driver chips (MAX7219, TLC5940) designed for that purpose. The MCP23017 works well for indicator LEDs and control panel status lights but isn’t optimized for high-density LED arrays.
Q: Do I need pull-up resistors on I2C lines if Arduino has internal ones?
A: Depends on bus conditions. Arduino’s internal pull-ups (~50kΩ) work adequately for short connections (under 6 inches) with 1-2 devices. For better reliability, especially with long wires, multiple devices, or high-speed communication, add external 4.7kΩ resistors from SDA and SCL to VDD. External pull-ups provide stronger signal rise times improving noise immunity. If experiencing intermittent communication errors, glitches, or unreliable detection, external pull-ups often solve these issues. In professional designs, always include external pull-ups rather than relying on internal ones.
Conclusion: Unlimited GPIO Within Reach
Mastering MCP23017 Arduino integration removes GPIO limitations that otherwise constrain project complexity. The ability to add 16 bidirectional pins while consuming only two I2C connections transforms what’s possible with standard Arduino boards. Whether building control panels with dozens of buttons and indicators, interfacing relay boards for home automation, reading keypad matrices, or controlling LED displays, the MCP23017 provides professional-grade GPIO expansion at minimal cost and complexity.
From my perspective designing commercial control systems, GPIO expanders aren’t luxury add-ons—they’re fundamental building blocks enabling practical product design without upgrading to expensive microcontrollers. The MCP23017’s mature ecosystem, extensive library support, and straightforward I2C interface make it accessible to beginners while providing features professionals depend upon for reliable operation.
Whether expanding a single Arduino Uno to handle 32 GPIO points or building systems with 128 I/O using eight daisy-chained MCP23017 chips, understanding proper implementation enables projects limited only by imagination rather than GPIO availability. The difference between abandoning ambitious projects due to pin shortages and confidently building full-featured systems comes down to mastering GPIO expansion techniques like the MCP23017 offers.
Inquire: Call 0086-755-23203480, or reach out via the form below/your sales contact to discuss our design, manufacturing, and assembly capabilities.
Quote: Email your PCB files to Sales@pcbsync.com (Preferred for large files) or submit online. We will contact you promptly. Please ensure your email is correct.
Notes: For PCB fabrication, we require PCB design file in Gerber RS-274X format (most preferred), *.PCB/DDB (Protel, inform your program version) format or *.BRD (Eagle) format. For PCB assembly, we require PCB design file in above mentioned format, drilling file and BOM. Click to download BOM template To avoid file missing, please include all files into one folder and compress it into .zip or .rar format.