Component
digitalio (DigitalIOHardware) — affects native GPIO and expander (seesaw/MCP/etc.) pins
Summary
Two digitalio pin-correctness bugs surfaced while bringing up a Seesaw expander (Adafruit LED Arcade Button 1x4, PID 5296) on a LilyGO T-Dongle C5. Both live in src/components/digitalIO/hardware.cpp and apply equally to native and expander pins.
Bug 1 — is_inverted is silently ignored
ws_digitalio_Add.is_inverted is decoded but never applied. Handle_DigitalIO_Add() constructs the pin without it, so there is no way to request an active-low pin — isInverted: true in the Add is a no-op for both reads and writes.
// controller.cpp (before)
new DigitalIOHardware(pin_num, msg->gpio_direction, msg->sample_mode,
initial_value, (ulong)(msg->period * 1000.0f), expander_drv);
// msg->is_inverted dropped on the floor
Fix: thread is_inverted through the constructor and apply it consistently — ReadValue() reports the logical (inverted) value, Write() and SetMode()'s initial OUTPUT drive emit the inverted electrical level. _value now holds the logical value; the electrical level is inverted for active-low pins.
Bug 2 — first-read change detection uses a guessed baseline
_prv_value is seeded from initial_value (which is false for an input with no initial write). The first CheckEvent() therefore compares a real reading against a fabricated "previous" value:
bool DigitalIOHardware::CheckEvent() {
ReadValue();
if (_value == _prv_value) return false; // _prv_value is a guess on the first call
_prv_value = _value;
return true;
}
- If the guess is the opposite of the real initial level → a phantom edge is emitted.
- If the guess happens to match the real level → the true initial state is swallowed and the baseline is (coincidentally) seeded.
Either way the first comparison is meaningless because _prv_value can't be known before the first read.
Fix: add a _first_read flag; the first CheckEvent() seeds _prv_value from the actual reading and reports it as the pin's initial state, then subsequent reads do normal change detection.
Fix / verification
HIL-tested on a T-Dongle C5 with an Adafruit LED Arcade Button 1x4 (seesaw @ 0x3A): the switch on a seesaw GPIO pin (EXP_0x3a_18, INPUT_PULLUP, event mode) now reports clean value: 0 on press / value: 1 on release.
Note
The seesaw driver has no pull-down direction available because the digitalio Direction enum only defines D_INPUT, D_INPUT_PULL_UP, D_OUTPUT — no D_INPUT_PULL_DOWN. Not needed for this active-low button, but worth considering for active-high inputs (seesaw and ESP32 both support INPUT_PULLDOWN).
Component
digitalio (
DigitalIOHardware) — affects native GPIO and expander (seesaw/MCP/etc.) pinsSummary
Two digitalio pin-correctness bugs surfaced while bringing up a Seesaw expander (Adafruit LED Arcade Button 1x4, PID 5296) on a LilyGO T-Dongle C5. Both live in
src/components/digitalIO/hardware.cppand apply equally to native and expander pins.Bug 1 —
is_invertedis silently ignoredws_digitalio_Add.is_invertedis decoded but never applied.Handle_DigitalIO_Add()constructs the pin without it, so there is no way to request an active-low pin —isInverted: truein the Add is a no-op for both reads and writes.Fix: thread
is_invertedthrough the constructor and apply it consistently —ReadValue()reports the logical (inverted) value,Write()andSetMode()'s initial OUTPUT drive emit the inverted electrical level._valuenow holds the logical value; the electrical level is inverted for active-low pins.Bug 2 — first-read change detection uses a guessed baseline
_prv_valueis seeded frominitial_value(which isfalsefor an input with no initial write). The firstCheckEvent()therefore compares a real reading against a fabricated "previous" value:Either way the first comparison is meaningless because
_prv_valuecan't be known before the first read.Fix: add a
_first_readflag; the firstCheckEvent()seeds_prv_valuefrom the actual reading and reports it as the pin's initial state, then subsequent reads do normal change detection.Fix / verification
api-v2-pins-as-strings)HIL-tested on a T-Dongle C5 with an Adafruit LED Arcade Button 1x4 (seesaw @ 0x3A): the switch on a seesaw GPIO pin (
EXP_0x3a_18,INPUT_PULLUP, event mode) now reports cleanvalue: 0on press /value: 1on release.Note
The seesaw driver has no pull-down direction available because the digitalio
Directionenum only definesD_INPUT,D_INPUT_PULL_UP,D_OUTPUT— noD_INPUT_PULL_DOWN. Not needed for this active-low button, but worth considering for active-high inputs (seesaw and ESP32 both supportINPUT_PULLDOWN).