The PCA9555 and PCAL9555A are 16-bit I/O expanders from NXP Semiconductors
that communicate via I2C. They provide 16 GPIO pins organized into two 8-bit
ports (PORT_0: pins 0-7, PORT_1: pins 8-15), expanding your microcontrollerβs I/O
capabilities over a simple two-wire bus.
This driver provides a single, unified C++ class that supports both chip
variants. The chip type is auto-detected during initialization by probing the
extended register bank. Features exclusive to the PCAL9555A degrade gracefully
when a standard PCA9555 is detected.
The driver is hardware-agnostic β it uses the CRTP (Curiously Recurring Template
Pattern) for zero-overhead I2C abstraction. You only need to implement a small
I2cInterface class for your platform.
Chip Compatibility
The PCAL9555A is a pin-compatible superset of the PCA9555. The driver handles both
transparently:
Feature
PCA9555
PCAL9555A
16 GPIO pins (2 x 8-bit ports)
Yes
Yes
I2C addresses 0x20-0x27
Yes
Yes
Pin direction (input/output)
Yes
Yes
Pin read / write / toggle
Yes
Yes
Polarity inversion
Yes
Yes
Pull-up / pull-down resistors
β
Yes
Drive strength (4 levels)
β
Yes
Input latch
β
Yes
Interrupt mask / status
β
Yes
Output mode (push-pull / OD)
β
Yes
How detection works: During initialization, the driver reads a standard register
(INPUT_PORT_0, 0x00) to verify the bus is alive, then probes the Agile I/O register
(OUTPUT_CONF, 0x4F). If the chip ACKs, itβs a PCAL9555A. If it NACKs, itβs a PCA9555.
A follow-up read of INPUT_PORT_0 confirms the bus recovered. This 3-step sandwich
ensures reliable detection even on noisy buses.
Methods requiring PCAL9555A features return false and set Error::UnsupportedFeature
when called on a PCA9555. You can check at runtime with:
1
2
3
4
if(driver.HasAgileIO()){// PCAL9555A features availabledriver.SetDriveStrength(0,DriveStrength::Level2);}
Features
Dual-chip support: Auto-detects PCA9555 vs PCAL9555A at runtime
#include"pcal95555.hpp"classMyI2c:publicpcal95555::I2cInterface<MyI2c>{public:boolWrite(uint8_taddr,uint8_treg,constuint8_t*data,size_tlen){// Your platform I2C write implementation}boolRead(uint8_taddr,uint8_treg,uint8_t*data,size_tlen){// Your platform I2C read implementation}boolSetAddressPins(boola0,boola1,boola2){returnfalse;// Return false if address pins are hardwired}};
ESP32-S3 development board with PCA9555 or PCAL9555A connected via I2C
Build
1
2
3
4
5
6
7
8
9
10
cd examples/esp32
# List available apps
./scripts/build_app.sh list
# Build the comprehensive test suite
./scripts/build_app.sh pcal95555_comprehensive_test Debug
# Build the LED animation demo
./scripts/build_app.sh pcal95555_led_animation Debug
Flash and Monitor
1
2
3
4
5
6
7
8
# Flash and open serial monitor
./scripts/flash_app.sh flash_monitor pcal95555_comprehensive_test Debug
# Flash only
./scripts/flash_app.sh flash pcal95555_comprehensive_test Debug
# Monitor only (if already flashed)
./scripts/flash_app.sh monitor
Note (ESP32-S3 native USB): If using the USB-SERIAL-JTAG port (/dev/ttyACM0),
you may need to manually enter download mode: Hold BOOT, press RESET,
release BOOT, then immediately run the flash command.
API Reference
Initialization
Method
Description
PCAL95555(bus, a0, a1, a2, variant)
Construct from address pin levels
PCAL95555(bus, address, variant)
Construct from I2C address (0x20-0x27)
EnsureInitialized()
Explicitly trigger lazy initialization
ResetToDefault()
Reset all registers to power-on defaults
InitFromConfig()
Initialize from Kconfig compile-time values
GPIO Direction
Method
Description
SetPinDirection(pin, dir)
Set single pin input/output
SetMultipleDirections(mask, dir)
Set direction by 16-bit mask
SetDirections({...})
Set mixed directions via initializer list
Pin Read / Write
Method
Description
ReadPin(pin)
Read single pin state
WritePin(pin, value)
Write single pin HIGH/LOW
TogglePin(pin)
Toggle single pin
ReadPins({...})
Read multiple pins at once
WritePins({...})
Write multiple pins at once
Pull Resistors (PCAL9555A only)
Method
Description
SetPullEnable(pin, enable)
Enable/disable pull resistor
SetPullEnables({...})
Multi-pin pull enable
SetPullDirection(pin, pull_up)
Select pull-up or pull-down
SetPullDirections({...})
Multi-pin pull direction
Drive Strength (PCAL9555A only)
Method
Description
SetDriveStrength(pin, level)
Set drive strength (Level0-Level3)
SetDriveStrengths({...})
Multi-pin drive strength
Interrupts
Method
Description
PCAL9555A?
ConfigureInterrupt(pin, state)
Enable/disable per-pin interrupt
Yes
ConfigureInterrupts({...})
Multi-pin interrupt config
Yes
ConfigureInterruptMask(mask)
Set 16-bit interrupt mask
Yes
GetInterruptStatus()
Read and clear interrupt status
Yes
RegisterPinInterrupt(pin, edge, cb)
Register edge-triggered callback
No
UnregisterPinInterrupt(pin)
Remove pin callback
No
SetInterruptCallback(cb)
Register global interrupt callback
No
RegisterInterruptHandler()
Bind to hardware INT pin
No
HandleInterrupt()
Process interrupt (auto or manual)
No
Polarity
Method
Description
SetPinPolarity(pin, polarity)
Set input polarity (normal/inverted)
SetMultiplePolarities(mask, pol)
Set polarity by mask
SetPolarities({...})
Multi-pin polarity via initializer list
Input Latch (PCAL9555A only)
Method
Description
EnableInputLatch(pin, enable)
Enable/disable input latch
EnableMultipleInputLatches(mask, en)
Mask-based latch control
EnableInputLatches({...})
Multi-pin latch via initializer list
Output Mode (PCAL9555A only)
Method
Description
SetOutputMode(p0_od, p1_od)
Set push-pull or open-drain per port
Address Management
Method
Description
GetAddress()
Get current 7-bit I2C address
GetAddressBits()
Get A2-A0 bits (0-7)
ChangeAddress(a0, a1, a2)
Change address via pin levels
ChangeAddress(address)
Change address directly (0x20-0x27)
Error Handling
Method
Description
GetErrorFlags()
Get bitmask of active errors
ClearErrorFlags(mask)
Clear specific or all error flags
SetRetries(n)
Set I2C retry count
HasAgileIO()
Check if PCAL9555A features available
GetChipVariant()
Get detected variant enum
Error Flags
Flag
Value
Meaning
InvalidPin
0x0001
Pin index >= 16
InvalidMask
0x0002
Mask bits outside 0-15
I2CReadFail
0x0004
I2C read transaction failed
I2CWriteFail
0x0008
I2C write transaction failed
UnsupportedFeature
0x0010
PCAL9555A feature called on PCA9555
InvalidAddress
0x0020
I2C address outside valid 0x20-0x27 range
Examples
Comprehensive Test Suite (pcal95555_comprehensive_test)
A thorough test of every public API method, organized into 17 test sections:
A visual demo driving 16 LEDs through 10 animation patterns:
Sequential Chase β single LED scans left/right
Bounce β LED bounces between endpoints
Binary Counter β counts 0-65535 in binary on LEDs
Breathing (PWM) β software PWM fade-in/fade-out
Wave / Comet Tail β 4-LED comet sweeps back and forth
Random Sparkle β random LED patterns
Build-up / Teardown β LEDs light up then extinguish one by one
Accelerating Scan β speed ramps from 120ms to 1ms and back
Center Expand / Contract β symmetric outward/inward animation
Alternating Flash β port-vs-port and even-vs-odd flashing
Configure LEDS_ACTIVE_LOW for your wiring (common-anode vs common-cathode).
Testing
Running on hardware
1
2
3
4
# Build and flash the test suitecd examples/esp32
./scripts/build_app.sh pcal95555_comprehensive_test Debug
./scripts/flash_app.sh flash_monitor pcal95555_comprehensive_test Debug
The default main_task stack may be too small for the test suite (extensive logging).
Set in sdkconfig:
1
CONFIG_ESP_MAIN_TASK_STACK_SIZE=16384
ESP32-S3 flashing fails
If using native USB (/dev/ttyACM0), the auto-reset circuit may not work reliably.
Manually enter download mode: Hold BOOT β Press RESET β Release BOOT β
Run flash command immediately.
ChipVariant shows βUnknownβ
This means the 3-step detection could not confirm the chip type (bus error during
probe). Check wiring, pull-ups, and power supply.
Contributing
Pull requests and suggestions are welcome! Please:
Follow the existing code style (clang-format, Doxygen comments)
Add tests for new features in the comprehensive test suite
Update documentation for any API changes
Ensure the build passes for both Debug and Release configurations
License
This project is licensed under the GNU General Public License v3.0.
See the LICENSE file for details.