MCP23017 Breakout Pinout, I²C Address Jumpers, Interrupts and ESP32 Wiring

MCP23017 hardware reference: breakout pinout, 0x20–0x27 solder address bridges, 16 GPIO mappings, IA/IB interrupts, schematic, ESP32 wiring and Adafruit example.

The MCP23017AliExpress price adds 16 configurable digital I/O pins to an ESP32AliExpress price using just SDA and SCL. Unlike the PCF8574AliExpress price and PCF8575 quasi-bidirectional expanders, it provides conventional direction registers, output latches, optional input pull-ups, input polarity inversion and two interrupt outputs. The key to making the module work is choosing the correct breakout-board pinout and its three I²C address settings before writing code.

Quick answer: An MCP23017 on a compatible 3.3 V breakout normally appears at 0x20 with its three address inputs LOW. On the Adafruit STEMMA QT breakout, three open rear solder bridges select that default; closing the bridges marked +1, +2 and +4 can select any address through 0x27. For a classic ESP32 DevKit: 3V3 → VIN, GND → GND, GPIO21 → SDA, GPIO22 → SCL. The simple Arduino example later uses Adafruit_MCP23X17.

Adafruit MCP23017 STEMMA QT breakout front pinout: GPA0 to GPA7, GPB0 to GPB7, VIN GND SCL SDA, D0 D1 D2, RST, IA and IB
MCP23017 Adafruit breakout reference: two 8-bit GPIO banks, hardware address selection, reset and separate interrupt signals. Follow the actual board silkscreen.

The board layout in this guide refers specifically to the Adafruit MCP23017 I²C GPIO Expander Breakout with STEMMA QT connectors, GPIO ground pads, D0/D1/D2 and IA/IB pins. Generic blue MCP23017 modules, DIP chips and other breakouts may have completely different physical header orders, default pull-ups and address-link positions. Their chip-level functions are the same, but their PCB pinouts must not be assumed identical. See the manufacturer pinout guide before connecting a board.

MCP23017 specifications and what the pins mean

CharacteristicMCP23017 / Adafruit breakout
Type16-bit I²C general-purpose digital I/O expander
Digital I/OGPA0–GPA7 (PORTA) and GPB0–GPB7 (PORTB)
Arduino library numbering0–7 = GPA0–GPA7; 8–15 = GPB0–GPB7
Address range7-bit 0x20–0x27 via three hardware address inputs
Chip supply1.8–5.5 V according to Microchip; use 3.3 V when wired as illustrated to ESP32
I²C speed100 kHz and 400 kHz at suitable voltage; 1.7 MHz mode needs stricter conditions and is not required here
GPIO configurationDirection, internal input pull-ups, output latch, polarity inversion
InterruptsTwo configurable outputs, INTA for PORTA and INTB for PORTB; optional mirroring
ResetActive-LOW RESET; breakout front label RST
LED drivePush-pull outputs may source or sink current within device and total-current limits; always use an LED resistor
Analog inputs/PWMNeither ADC nor hardware PWM is provided by MCP23017
I²C vs SPIMCP23017 is I²C; MCP23S17 is the related SPI version

The A in GPA0 means PORTA, not analog input. The same distinction matters when reading the Adafruit front label D0: it is the hardware I²C address bit, not GPIO channel GPA0. The chip-level address-input names A0/A1/A2 can otherwise be confused with its GPIO pins.

Important: Adafruit documents a silicon-related I²C readout hazard involving GPA7 and GPB7. For reliable new designs, reserve these channels for outputs only, especially on modules whose silicon revision is unknown. The chip has 16 physical GPIO pins, but this is an important limitation if you are designing a 16-button input bank. Use GPA0–GPA6 and GPB0–GPB6 for inputs, or choose a different expander if you need all sixteen inputs.

Board pinout: PORTA, PORTB, power and control

Both ports contain eight bits, but the library pin number is a convenient continuous index rather than the chip’s physical package pin number. This prevents common errors when copying example code. The correct library mapping is:

Breakout / chip signalLibrary pin IDFunction
GPA00Digital GPIO bank A
GPA11Digital GPIO bank A
GPA22Digital GPIO bank A
GPA33Digital GPIO bank A
GPA44Digital GPIO bank A
GPA55Digital GPIO bank A
GPA66Digital GPIO bank A
GPA77Digital GPIO bank A
GPB08Digital GPIO bank B
GPB19Digital GPIO bank B
GPB210Digital GPIO bank B
GPB311Digital GPIO bank B
GPB412Digital GPIO bank B
GPB513Digital GPIO bank B
GPB614Digital GPIO bank B
GPB715Digital GPIO bank B

Additional breakout connections: VIN is supply power; GND is ground; SDA and SCL are I²C data and clock; IA and IB are the two interrupt outputs; RST resets the device when pulled LOW; D0/D1/D2 select its I²C address. The two STEMMA QT connectors carry the same four-wire I²C bus. Multiple boards can share it when their addresses differ.

On Adafruit’s breakout there are also individual ground pads for the GPIO. These are handy when wiring momentary switches, but do not confuse the extra ground pads with additional MCP23017 GPIO signals. The exact front-board row order must be taken from the official front-view pinout—a generic MCP23017 PCB image cannot substitute.

Changing the I²C address using solder jumpers

The Adafruit board ships with three rear address bridges open. They are silkscreened +1, +2 and +4, corresponding to the front header inputs D0, D1 and D2. The bridges change the 7-bit address by tying the corresponding selection line HIGH. Close a bridge with a small solder link only after turning off power; inspect for shorts with magnification.

Adafruit MCP23017 breakout rear address solder bridges marked +1 +2 +4, separate LED jumper, and I2C address table 0x20 through 0x27
On the Adafruit breakout, the three rear bridges add +1, +2 and +4 to the base address 0x20; the LED cut-jumper is a separate feature.
+4 / D2+2 / D1+1 / D07-bit address
OpenOpenOpen0x20
OpenOpenClosed0x21
OpenClosedOpen0x22
OpenClosedClosed0x23
ClosedOpenOpen0x24
ClosedOpenClosed0x25
ClosedClosedOpen0x26
ClosedClosedClosed0x27

For instance, closing +4 and +1, while leaving +2 open, makes the device 0x25. Then initialise the library with mcp.begin_I2C(0x25, &Wire). If you use the front hardware pins instead of solder, connect D0/D1/D2 to the appropriate logic rail according to the manufacturer’s guidance; never leave bare IC address pins floating on an unverified generic board.

Important: The separate rear power LED cut-jumper disables the indicator LED when its trace is cut. It does not change the I²C address. Likewise, an MCP23017 is not a PCF8574A: the latter has a different chip family and address options, despite similar-looking boards.

MCP23017 functional schematic and internal registers

At the chip level, the essential connections are straightforward: supply and a local decoupling capacitor, I²C pull-ups to the bus logic voltage, three defined address inputs, an inactive HIGH reset and optional interrupt lines. The Adafruit breakout already implements the required board-specific support circuitry, including I²C pull-ups; do not add a second set without considering the combined resistance.

MCP23017 chip-level functional schematic at 3.3 V with SDA SCL, I2C pull-ups, A0 A1 A2 address inputs, RESET, INTA INTB, GPIO LED and button
Conceptual chip-level circuit at 3.3 V, not a reproduction of the full Adafruit PCB schematic. The board already contains its own I²C pull-ups.

The functional drawing is not a reproduction of Adafruit’s PCB netlist. For a true manufacturer schematic and precise copper layout, refer to the official Downloads / EagleCAD files and schematic. Drawings of generic modules should be labelled by their own revision.

RegisterPurpose
IODIRA / IODIRBSelect input (1) or output (0) for each GPIO
GPPUA / GPPUBEnable internal pull-up resistors for inputs
GPIOA / GPIOBRead port state or access pin values
OLATA / OLATBHold output latch values
GPINTENA / GPINTENBEnable interrupt-on-change for selected input pins
INTCONA / INTCONBSelect interrupt comparison against previous state or DEFVAL
DEFVALA / DEFVALBReference value used in default-value interrupt mode
INTFA / INTFBInterrupt-source flags
INTCAPA / INTCAPBCapture port states when an interrupt occurs
IOCONInterrupt mirror/polarity/drive and register addressing behaviour

This distinction matters when migrating from PCF8575: MCP23017 has true I/O direction control. Its input pull-ups are configurable rather than relying on quasi-bidirectional current sourcing. Each output can drive HIGH or LOW, so a small LED plus a resistor can run in either direction, subject to the IC’s limits. For relay coils, motors, solenoids or high-current loads, add the appropriate driver stage and flyback protection.

ESP32 to MCP23017 wiring (classic ESP32 DevKit)

Use a common 3.3 V supply and logic reference for the example. The official Adafruit STEMMA QT breakout is documented for 3–5 V use, but a low-cost generic module might pull SDA/SCL to its own VCC. Supplying such a module at 5 V and connecting it directly to the ESP32 can expose the ESP32 pins to an unsafe voltage. When in doubt, power the module from 3.3 V and check its pull-up circuit.

Classic ESP32 WROOM32 DevKit GPIO21 SDA GPIO22 SCL 3V3 VIN and common ground wired to Adafruit MCP23017 breakout
ESP32-WROOM-32 development board to Adafruit MCP23017 breakout: four I²C/power wires. These GPIO assignments are examples for the classic ESP32.
ESP32-WROOM-32 DevKitMCP23017 Adafruit breakoutWhy
3V3VIN3.3 V supply, matched to ESP32 logic
GNDGNDShared 0 V reference
GPIO21SDAI²C data
GPIO22SCLI²C clock
Optional GPIO27IAInterrupt indication for PORTA in advanced example
Not connectedIBPort B interrupt optional
Not connectedRSTNormal operation; reset already handled by breakout
Open rear address bridgesD0/D1/D2Use 7-bit address 0x20

GPIO21/GPIO22 are a conventional choice for many classic ESP32 development boards. GPIO assignments on ESP32-C3, ESP32-C6, ESP32-S3 and custom boards can differ. Match your board’s real pins and pass the chosen assignments to Wire.begin(SDA, SCL). For a quick check, run an I²C scanner before adding the LED/button code.

Install the Arduino library

  • In Arduino IDE 2, open Library Manager and install Adafruit MCP23017 Arduino Library.
  • Install any requested dependency such as Adafruit BusIO when prompted.
  • Select the correct ESP32 board package and serial port.
  • Power the expander at 3.3 V as shown. Keep the rear address bridges open for 0x20.
  • The current Adafruit header is <Adafruit_MCP23X17.h>; older code using Adafruit_MCP23017.h or mcp.begin() may refer to an obsolete library API.

The following example uses the manufacturer-maintained Adafruit MCP23017 Arduino Library. In that library, GPA0 = 0 and GPB0 = 8. This is a small, directly usable ESP32 sketch rather than a generic pseudocode fragment.

Arduino example: button on GPA0 controls an LED on GPB0

Wire a normally-open button between GPA0 and GND. Wire an LED anode through a 470 Ω series resistor to GPB0, with its cathode going to GND. This example drives the LED HIGH when the button is pressed and LOW otherwise. It deliberately avoids GPA7 and GPB7 as inputs.

MCP23017 button on GPA0 wired to GND, LED and series resistor from GPB0 to GND, optional INTA interrupt to ESP32 GPIO27
Simple verification: a push button on GPA0 reads LOW when pressed; a series-resistor LED on GPB0 switches on in response. INTA is optional.
#include <Wire.h>
#include <Adafruit_MCP23X17.h>

constexpr int SDA_PIN = 21;      // classic ESP32 DevKit
constexpr int SCL_PIN = 22;
constexpr uint8_t MCP_ADDR = 0x20;
constexpr uint8_t BUTTON = 0;   // GPA0, Adafruit pin ID 0
constexpr uint8_t LED = 8;      // GPB0, Adafruit pin ID 8

Adafruit_MCP23X17 mcp;

void setup() {
  Serial.begin(115200);
  delay(300);
  Wire.begin(SDA_PIN, SCL_PIN);
  Wire.setClock(100000);       // reliable starting point

  if (!mcp.begin_I2C(MCP_ADDR, &Wire)) {
    Serial.println("MCP23017 not found. Check power, SDA/SCL, address.");
    while (true) delay(1000);
  }

  mcp.pinMode(BUTTON, INPUT_PULLUP);
  mcp.pinMode(LED, OUTPUT);
  mcp.digitalWrite(LED, LOW);
  Serial.println("MCP23017 ready: press the GPA0 button.");
}

void loop() {
  bool pressed = (mcp.digitalRead(BUTTON) == LOW);
  mcp.digitalWrite(LED, pressed ? HIGH : LOW);

  static bool lastReported = false;
  if (pressed != lastReported) {
    Serial.println(pressed ? "Button PRESSED" : "Button released");
    lastReported = pressed;
  }
  delay(25);  // simple polling delay; not a robust debounce filter
}

Expected behaviour: the Serial Monitor at 115200 baud first shows a ready message. Pressing the button on GPA0 pulls its internally pulled-up input LOW. The sketch then drives GPB0 HIGH, lighting the series-resistor LED, and prints Button PRESSED. Releasing the button switches the LED off. The 25 ms delay reduces output activity but a fully debounced interface should also validate that the contact state remains stable for a chosen interval.

If the expander does not initialise, the sketch reports an error before attempting to use the GPIO pins. The library’s begin_I2C(address, &Wire), pinMode, digitalRead and digitalWrite method names match the current Adafruit header. A real hardware bench test should still be performed on the exact PCB revision before labelling the design production-ready.

I²C scanner for 0x20–0x27

With the board connected but no peripherals attached to its GPIO, run this scanner if the library says it cannot find the MCP23017. A valid address can be anywhere from 0x20 to 0x27; a response at 0x20 is the expected default for the Adafruit board with all address links open.

#include <Wire.h>
void setup() {
  Serial.begin(115200);
  Wire.begin(21, 22);
  delay(200);
}
void loop() {
  for (uint8_t a = 0x20; a <= 0x27; a++) {
    Wire.beginTransmission(a);
    if (Wire.endTransmission() == 0) {
      Serial.printf("I2C device found at 0x%02X\n", a);
    }
  }
  delay(2000);
}

A scanner only confirms that some device acknowledges an I²C address: it does not prove its identity. If another GPIO expander or sensor uses 0x20 on the same bus, move one MCP23017 to a different address using the rear jumpers. For eight devices the available addresses are 0x20–0x27, allowing up to 128 physical GPIO lines before accounting for system electrical and bus-loading limits.

How INTA and INTB work, and when to use them

Unlike a simple 16-input polling loop, MCP23017 can notify the ESP32 when a configured input changes. Its two interrupt outputs are IA/INTA for PORTA and IB/INTB for PORTB. You can configure their active polarity, output type and optional mirroring (both IRQ pins responding to either port). The GPINTEN, INTCON, DEFVAL, INTF and INTCAP registers determine what is detected and how its source is read.

For a first experiment, keep the button on GPA0 and connect the breakout’s IA to GPIO27 on the classic ESP32. The Adafruit API supports mcp.setupInterrupts(true, false, LOW) for mirrored, active-driven LOW interrupt outputs, followed by mcp.setupInterruptPin(0, CHANGE). In the main loop, monitor the ESP32 interrupt pin and read the captured state using mcp.getCapturedInterrupt() or clear the pending event with mcp.clearInterrupts(). Avoid doing I²C reads or writing serial output inside an ESP32 ISR; have an ISR set a volatile flag and handle the bus in the loop instead.

With unmirrored interrupts, a change on PORTB is signalled by IB rather than IA. Setting IOCON.MIRROR combines the two interrupt sources, but clearing an interrupt still requires observing the relevant port’s captured/current state. You may prefer polling for a handful of slow mechanical buttons; IRQs become valuable when several inputs are monitored and the ESP32 should avoid continuously requesting the whole bus.

MCP23017 versus PCF8575 and PCF8574

FeatureMCP23017PCF8575PCF8574
Channels16168
I/O behaviourConfigured input or push-pull outputQuasi-bidirectionalQuasi-bidirectional
Input pull-upsPer-pin configurableWeak source when set HIGHWeak source when set HIGH
Interrupt outputsTwo, configurableOne, change detectionOne, change detection
I²C defaultTypically 0x20Typically 0x200x20 for PCF8574; PCF8574A range starts at 0x38
Best fitFlexible GPIO and interruptsSimple 16-line expansionEconomical 8-line expansion

Choose MCP23017 when you need strong software control over input versus output behaviour, bank-specific IRQs, pull-ups or interrupt capture. Choose PCF8575 for uncomplicated 16-line digital expansion where quasi-bidirectional electrical behaviour suits the circuit. Neither part should be treated as an ADC, PWM driver or direct motor/relay driver. For ADC channels, the ADS1115 is a different board family.

Troubleshooting: common MCP23017 mistakes

  • No device at 0x20: swap-check SDA/SCL, confirm common ground, verify 3.3 V at VIN, scan 0x20–0x27 and inspect the +1/+2/+4 solder bridges.
  • GPIO wiring seems reversed: remember Adafruit pin IDs 0–7 correspond to GPA0–GPA7, while 8–15 correspond to GPB0–GPB7. A package pin number is not a library pin ID.
  • Button reads continuously LOW: ensure the button is normally open to GND and configured as INPUT_PULLUP; verify the switch is not bridging adjacent PCB pads.
  • LED does not light: check polarity, resistor location and GPB0 rather than GPA0. The 470 Ω resistor belongs in series with the LED.
  • Unexplained bus lockups: do not use GPA7/GPB7 as inputs on affected silicon; check pull-up voltage and excessive capacitance or long wiring.
  • Interrupt stays asserted: make sure the port capture/current state has been read or its IRQ cleared in the library, and check whether level-versus-change mode suits the switch.
  • Two devices respond together: give them unique I²C addresses using different solder-bridge combinations.
  • Wrong library examples: prefer Adafruit_MCP23X17.h and begin_I2C(), not outdated interfaces from pre-2.x tutorials.

Official references and related esp32.co.uk/ articles

Bottom line: use the correct breakout pinout, keep the ESP32 bus at 3.3 V, select an unused address from 0x20–0x27, and distinguish address inputs from GPIO A-bank pins. Start with the Adafruit Arduino button/LED sketch; add interrupts only when the application needs them. When designing a button bank, do not overlook Adafruit’s GPA7/GPB7 input warning.

CONTINUE EXPLORING / ESP32 & ELECTRONICS

Read next

Practical guides related to this article.

Explore the topic →
HARDWARE DECISION GUIDEWhich ESP32 board should you use?

Compare practical options before you choose hardware.

Compare options →