feat(expander): snapshot INTF / INTCAP at begin() for deep-sleep wake source detection#304
Merged
JosipKuci merged 1 commit intoMay 19, 2026
Conversation
… source detection
IOExpander::begin() does a 22-byte sequential bulk read of the MCP23017's
registers starting at 0x00. That sequence passes through INTFA (0x0E) and
INTFB (0x0F) BEFORE INTCAPA (0x10) and INTCAPB (0x11) — and on the MCP,
reading INTCAP is what clears the INTF latch as a side effect. So by the
time begin() returns, the latch is cleared at the chip; subsequent calls
to getInt() return 0 even when a wake-triggering interrupt was actually
pending before begin() ran.
v11.0.1 added getIntState() which reads INTCAP fresh — but INTCAP can be
overwritten by any new interrupt event (capacitive noise, application
activity arming new interrupts, etc.) between begin() and the call. For
applications that need to reliably determine a deep-sleep wake source,
the only correct moment to capture INTF/INTCAP is during begin() itself,
before any subsequent transactions can disturb either register.
Snapshot the captured INTF and INTCAP into dedicated member variables
inside begin(), and expose them via:
uint16_t getInterruptFlagsAtBegin(); // INTFA in low byte, INTFB in high byte
uint16_t getInterruptCaptureAtBegin(); // INTCAPA in low, INTCAPB in high
Callers pair the two to reconstruct rising vs falling edges on each
interrupt-enabled pin even after begin() has cleared the live latch:
rising = INTF & INTCAP // pins HIGH at trigger = press
falling = INTF & ~INTCAP // pins LOW at trigger = release
Backward-compatible: no existing API or behavior changes; new public
methods and two new private members only.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
IOExpander::begin()does a 22-byte sequential bulk read of MCP23017registers
0x00..0x15. That sequence passes throughINTFA/B(0x0E,0x0F) before
INTCAPA/B(0x10, 0x11) — and on the MCP23017, readingINTCAP clears the INTF latch as a side effect.
So after
begin()returns, the live INTF register reads as0, evenwhen a wake-triggering interrupt was actually pending. Applications
using the MCP's INT line to wake the host from deep sleep can't
determine which pin caused the wake. v11.0.1's
getIntState()readsINTCAP fresh, but INTCAP can be overwritten by any new interrupt event
between
begin()and the call (e.g. capacitive noise during boot).The only reliable moment to capture the wake source is during
begin()itself.
Fix
Snapshot the cached pre-clear INTF/INTCAP values into dedicated members
in
begin(), expose via two new public methods:Callers pair them to distinguish rising vs falling edges:
Verification
Inkplate 10 V1 with
setIntPin(_, RISING)on the three touchpads.Pressing each pad produces the expected
INTFB/INTCAPBbit set;release-edge wakes show
INTFB != 0butINTCAPB == 0and arecorrectly filtered out by the application.
Compat / scope
Strictly additive — no existing API or behavior is modified. Two new
public methods, 4 bytes of state per instance. Same file gating as the
existing MCP code (
ARDUINO_INKPLATE6 || ARDUINO_INKPLATE10 || ARDUINO_INKPLATE6PLUS).