From 9ea27e68ee0ed94b6b693c14fab1ac10db31aba2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 4 Apr 2026 22:52:40 -1000 Subject: [PATCH] [pcf8574][pca9554] Disable loop when all pins are outputs (#15455) --- esphome/components/pca9554/pca9554.cpp | 11 +++++++++-- esphome/components/pcf8574/pcf8574.cpp | 11 +++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/esphome/components/pca9554/pca9554.cpp b/esphome/components/pca9554/pca9554.cpp index 9b300eaac2..ac4f119dfe 100644 --- a/esphome/components/pca9554/pca9554.cpp +++ b/esphome/components/pca9554/pca9554.cpp @@ -40,9 +40,11 @@ void PCA9554Component::setup() { this->interrupt_pin_->attach_interrupt(&PCA9554Component::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE); // Don't invalidate cache on read — only invalidate when interrupt fires this->set_invalidate_on_read_(false); - // With interrupt pin, only run loop when interrupt fires - this->disable_loop(); } + // Disable loop until an input pin is configured via pin_mode() + // For interrupt-driven mode, loop is re-enabled by the ISR + // For polling mode, loop is re-enabled when pin_mode() registers an input pin + this->disable_loop(); } void IRAM_ATTR PCA9554Component::gpio_intr(PCA9554Component *arg) { arg->enable_loop_soon_any_context(); } void PCA9554Component::loop() { @@ -89,6 +91,11 @@ void PCA9554Component::pin_mode(uint8_t pin, gpio::Flags flags) { if (flags == gpio::FLAG_INPUT) { // Clear mode mask bit this->config_mask_ &= ~(1 << pin); + // Enable polling loop for input pins (not needed for interrupt-driven mode + // where the ISR handles re-enabling loop) + if (this->interrupt_pin_ == nullptr) { + this->enable_loop(); + } } else if (flags == gpio::FLAG_OUTPUT) { // Set mode mask bit this->config_mask_ |= 1 << pin; diff --git a/esphome/components/pcf8574/pcf8574.cpp b/esphome/components/pcf8574/pcf8574.cpp index 1eeef663b0..bf4a9442a2 100644 --- a/esphome/components/pcf8574/pcf8574.cpp +++ b/esphome/components/pcf8574/pcf8574.cpp @@ -21,9 +21,11 @@ void PCF8574Component::setup() { this->interrupt_pin_->attach_interrupt(&PCF8574Component::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE); // Don't invalidate cache on read — only invalidate when interrupt fires this->set_invalidate_on_read_(false); - // With interrupt pin, only run loop when interrupt fires - this->disable_loop(); } + // Disable loop until an input pin is configured via pin_mode() + // For interrupt-driven mode, loop is re-enabled by the ISR + // For polling mode, loop is re-enabled when pin_mode() registers an input pin + this->disable_loop(); } void IRAM_ATTR PCF8574Component::gpio_intr(PCF8574Component *arg) { arg->enable_loop_soon_any_context(); } void PCF8574Component::loop() { @@ -66,6 +68,11 @@ void PCF8574Component::pin_mode(uint8_t pin, gpio::Flags flags) { this->mode_mask_ &= ~(1 << pin); // Write GPIO to enable input mode this->write_gpio_(); + // Enable polling loop for input pins (not needed for interrupt-driven mode + // where the ISR handles re-enabling loop) + if (this->interrupt_pin_ == nullptr) { + this->enable_loop(); + } } else if (flags == gpio::FLAG_OUTPUT) { // Set mode mask bit this->mode_mask_ |= 1 << pin;