Shift Registers and I/O Expanders for Arduino and ESP32: 74HC595, MCP23017, and PCF8574 Explained
Every microcontroller project eventually runs into the same wall: you need more pins than the board has. An ESP32 has plenty of GPIO for most projects, but a hobby Arduino Uno or Nano runs out fast once you're driving a 7-segment display, reading a button matrix, and controlling a few relays at the same time. The fix isn't a bigger board — it's a shift register or I/O expander chip that turns a handful of pins into many more. This guide covers the three chips that solve this in almost every hobby project: the 74HC595 shift register, the MCP23017 I2C expander, and the PCF8574 I2C expander, plus when to reach for each one.
74HC595: Output Expansion, One Pin Chain at a Time
The 74HC595 is a serial-in, parallel-out (SIPO) shift register: you shift 8 bits of data into it serially over three pins (data, clock, latch), and it presents those 8 bits as 8 output pins, held stable until the next update. The real trick is that 595s daisy-chain — the output of one feeds the input of the next, so three microcontroller pins can drive 8, 16, 24, or more outputs by chaining registers, with the only added cost being a few extra clock cycles per chip in the chain. This makes it the standard choice for driving large 7-segment displays, LED arrays, and any project needing many simple on/off outputs where the microcontroller only has a few pins to spare. It's output-only, though — it can't read inputs, and it has no addressing scheme, so every chip in the chain shares the same three control lines.
Its input-reading counterpart is the 74HC165, a parallel-in, serial-out (PISO) shift register that works the same way in reverse: wire up to 8 buttons or switches to its parallel inputs, and read all 8 states back over the same three-wire serial interface, daisy-chainable the same way. Together, a 595 and a 165 chain let you drive dozens of outputs and read dozens of inputs off the same handful of microcontroller pins — the classic combination behind DIY arcade cabinet and macro-keypad controllers.
MCP23017: 16 Bidirectional Pins Over I2C
The MCP23017 is a 16-bit I2C GPIO expander — each of its 16 pins can independently be configured as input or output, with built-in pull-up resistor options and interrupt-on-change support that can wake your microcontroller only when a monitored pin actually changes state, instead of polling constantly. Up to 8 MCP23017s can share a single I2C bus simultaneously by setting their three address pins (A0-A2) differently, giving you up to 128 extra GPIO pins over just two wires (SDA/SCL) plus power. This makes it the right choice whenever you need many bidirectional pins — reading a large button/switch panel, driving relay banks, or building a control panel with dozens of discrete I/O points — and want them addressable individually rather than shifted in bulk. See our I2C wiring guide for bus setup basics if you haven't used I2C peripherals before.
PCF8574: The Simpler 8-Bit I2C Expander
The PCF8574 is a smaller, cheaper, quasi-bidirectional 8-bit I2C expander, most commonly found pre-soldered onto LCD "I2C backpack" modules that turn a 16-pin parallel character LCD into a 2-wire I2C device. It's simpler than the MCP23017 (no true tri-state input/output configuration, no interrupt-on-change on most variants, and its open-drain-style outputs source current weakly, which matters if you're trying to directly drive an LED rather than just switch a logic-level signal) but it's inexpensive and ubiquitous, and for driving an LCD, a handful of status LEDs, or reading a few buttons, it does the job without the MCP23017's extra pin count you don't need.
ChipPinsDirectionProtocolMax per BusBest For 74HC5958 (chainable)Output onlySPI-like (3-wire serial)Unlimited (daisy-chain)LED matrices, 7-segment displays, many simple outputs 74HC1658 (chainable)Input onlySPI-like (3-wire serial)Unlimited (daisy-chain)Button/switch matrices, keypad reading MCP2301716Bidirectional, per-pinI2C8 chips (128 pins)Control panels, relay banks, interrupt-driven inputs PCF85748Quasi-bidirectionalI2C8 chips (64 pins)LCD backpacks, simple low-current I/OChoosing Between Them
If the need is "drive a lot of LEDs or a 7-segment display and don't need to read anything back," reach for a 74HC595 chain — it's the cheapest, simplest path and doesn't touch your I2C bus at all, leaving that free for sensors. If the need is "read a lot of buttons or switches," a 74HC165 chain does the same job in reverse. If the need is genuinely bidirectional — a mixed panel of buttons, LEDs, and relays, especially one that should trigger an interrupt rather than being polled — the MCP23017 is the more capable and only modestly more expensive choice. The PCF8574 mostly shows up because it's already soldered to an LCD module you bought; there's rarely a reason to seek it out standalone when the MCP23017 does more for a similar price.
Practical Gotchas
Every pin on these chips has a real current limit (typically 20-25mA per pin on the 74HC595, similar on the expanders) — enough to directly drive a single LED through a resistor, not enough to drive a relay coil or a strip of LEDs without a transistor or MOSFET buffer stage. See our motor and driver guide for the buffering pattern if you're switching anything beyond a single LED. I2C expanders need pull-up resistors on SDA/SCL (4.7kΩ is a common default, lower if you have many devices or long wire runs) — most breakout boards include them, but chaining several boards can double up pull-ups and drag the bus down; remove all but one board's resistors if you see I2C communication errors with multiple expanders on one bus. And if you're mixing a 5V Arduino with 3.3V-only peripherals or an ESP32, check the expander's logic level tolerance before wiring it directly — see our logic level shifter guide if there's a mismatch.
None of these chips are exciting on their own, but they're the quiet infrastructure behind almost every project that outgrows its microcontroller's pin count without outgrowing the microcontroller itself. Knowing which one fits — chained serial for bulk simple I/O, I2C expander for addressable bidirectional I/O — saves a redesign around a bigger board that was never actually necessary.
Related Guides
- I2C vs SPI vs UART: How to Choose and Use Serial Communication Protocols
- I2C Wiring and Protocol Guide for Arduino, ESP32, and Raspberry Pi
- How to Use a Logic Analyzer for Digital Signal Debugging: Saleae, Sigrok, and Protocol Decoding
- Battery Fuel Gauge ICs for Lithium Projects: MAX17048, BQ27441, and Coulomb Counting Explained
- Real-Time Clock Modules for Arduino and ESP32: DS3231 vs DS1307, Battery Backup, and NTP Sync
- HD44780 Character LCD Wiring Guide: I2C Backpacks, Direct Parallel, and Custom Characters
- Driving E-Paper Displays with ESP32 and Arduino: SPI Wiring, GxEPD2, and Partial Refresh
- Capacitive Touch Sensing for Makers: TTP223, MPR121, and ESP32 Native Touch Pins