ESPHome 2026.4.3
Loading...
Searching...
No Matches
pca9554.cpp
Go to the documentation of this file.
1#include "pca9554.h"
2#include "esphome/core/log.h"
3
4namespace esphome {
5namespace pca9554 {
6
7// for 16 bit expanders, these addresses will be doubled.
8const uint8_t INPUT_REG = 0;
9const uint8_t OUTPUT_REG = 1;
10const uint8_t INVERT_REG = 2;
11const uint8_t CONFIG_REG = 3;
12
13static const char *const TAG = "pca9554";
14
16 this->reg_width_ = (this->pin_count_ + 7) / 8;
17 // Test to see if device exists
18 if (!this->read_inputs_()) {
19 ESP_LOGE(TAG, "PCA95xx not detected at 0x%02X", this->address_);
20 this->mark_failed();
21 return;
22 }
23
24 // No polarity inversion
25 this->write_register_(INVERT_REG, 0);
26 // All inputs at initialization
27 this->config_mask_ = 0;
28 // Invert mask as the part sees a 1 as an input
29 this->write_register_(CONFIG_REG, ~this->config_mask_);
30 // All outputs low
31 this->output_mask_ = 0;
32 this->write_register_(OUTPUT_REG, this->output_mask_);
33 // Read the inputs
34 this->read_inputs_();
35 ESP_LOGD(TAG, "Initialization complete. Warning: %d, Error: %d", this->status_has_warning(),
36 this->status_has_error());
37
38 if (this->interrupt_pin_ != nullptr) {
39 this->interrupt_pin_->setup();
41 // Don't invalidate cache on read — only invalidate when interrupt fires
42 this->set_invalidate_on_read_(false);
43 }
44 // Disable loop until an input pin is configured via pin_mode()
45 // For interrupt-driven mode, loop is re-enabled by the ISR
46 // For polling mode, loop is re-enabled when pin_mode() registers an input pin
47 this->disable_loop();
48}
51 // Invalidate the cache so the next digital_read() triggers a fresh I2C read
52 this->reset_pin_cache_();
53 // Only disable the loop once INT has actually gone HIGH. Input transitions that straddle the
54 // I2C read leave INT asserted without re-firing a falling edge, which would strand us with
55 // stale state forever; keep looping until the line is released so we self-heal.
56 if (this->interrupt_pin_ != nullptr && this->interrupt_pin_->digital_read()) {
57 this->disable_loop();
58 }
59}
60
62 ESP_LOGCONFIG(TAG,
63 "PCA9554:\n"
64 " I/O Pins: %d",
65 this->pin_count_);
66 LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
67 LOG_I2C_DEVICE(this)
68 if (this->is_failed()) {
69 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
70 }
71}
72
74 // Read all pins from hardware into input_mask_
75 return this->read_inputs_(); // Return true if I2C read succeeded, false on error
76}
77
79 // Return the cached pin state from input_mask_
80 return this->input_mask_ & (1 << pin);
81}
82
83void PCA9554Component::digital_write_hw(uint8_t pin, bool value) {
84 if (value) {
85 this->output_mask_ |= (1 << pin);
86 } else {
87 this->output_mask_ &= ~(1 << pin);
88 }
89 this->write_register_(OUTPUT_REG, this->output_mask_);
90}
91
93 if (flags == gpio::FLAG_INPUT) {
94 // Clear mode mask bit
95 this->config_mask_ &= ~(1 << pin);
96 // Enable polling loop for input pins (not needed for interrupt-driven mode
97 // where the ISR handles re-enabling loop)
98 if (this->interrupt_pin_ == nullptr) {
99 this->enable_loop();
100 }
101 } else if (flags == gpio::FLAG_OUTPUT) {
102 // Set mode mask bit
103 this->config_mask_ |= 1 << pin;
104 }
105 this->write_register_(CONFIG_REG, ~this->config_mask_);
106}
107
109 uint8_t inputs[2];
110
111 if (this->is_failed()) {
112 ESP_LOGD(TAG, "Device marked failed");
113 return false;
114 }
115
116 this->last_error_ = this->read_register(INPUT_REG * this->reg_width_, inputs, this->reg_width_);
117 if (this->last_error_ != i2c::ERROR_OK) {
118 this->status_set_warning();
119 ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_);
120 return false;
121 }
122 this->status_clear_warning();
123 this->input_mask_ = inputs[0];
124 if (this->reg_width_ == 2) {
125 this->input_mask_ |= inputs[1] << 8;
126 }
127 return true;
128}
129
130bool PCA9554Component::write_register_(uint8_t reg, uint16_t value) {
131 uint8_t outputs[2];
132 outputs[0] = (uint8_t) value;
133 outputs[1] = (uint8_t) (value >> 8);
134 this->last_error_ = this->write_register(reg * this->reg_width_, outputs, this->reg_width_);
135 if (this->last_error_ != i2c::ERROR_OK) {
136 this->status_set_warning();
137 ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_);
138 return false;
139 }
140
141 this->status_clear_warning();
142 return true;
143}
144
146
149bool PCA9554GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
150void PCA9554GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
151size_t PCA9554GPIOPin::dump_summary(char *buffer, size_t len) const {
152 return buf_append_printf(buffer, len, 0, "%u via PCA9554", this->pin_);
153}
154
155} // namespace pca9554
156} // namespace esphome
void mark_failed()
Mark this component as failed.
bool is_failed() const
Definition component.h:284
void enable_loop_soon_any_context()
Thread and ISR-safe version of enable_loop() that can be called from any context.
void enable_loop()
Enable this component's loop.
Definition component.h:258
bool status_has_warning() const
Definition component.h:290
bool status_has_error() const
Definition component.h:292
void disable_loop()
Disable this component's loop.
void status_clear_warning()
Definition component.h:306
virtual void setup()=0
virtual bool digital_read()=0
void attach_interrupt(void(*func)(T *), T *arg, gpio::InterruptType type) const
Definition gpio.h:107
bool digital_read(P pin)
Read the state of the given pin.
Definition cached_gpio.h:37
ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len) const
writes an array of bytes to a specific register in the I²C device
Definition i2c.cpp:34
uint8_t address_
store the address of the device on the bus
Definition i2c.h:270
ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len)
reads an array of bytes from a specific register in the I²C device
Definition i2c.cpp:25
float get_setup_priority() const override
Definition pca9554.cpp:145
size_t pin_count_
number of bits the expander has
Definition pca9554.h:42
bool digital_read_cache(uint8_t pin) override
Definition pca9554.cpp:78
InternalGPIOPin * interrupt_pin_
Definition pca9554.h:53
bool write_register_(uint8_t reg, uint16_t value)
Definition pca9554.cpp:130
static void IRAM_ATTR gpio_intr(PCA9554Component *arg)
Definition pca9554.cpp:49
uint16_t output_mask_
The mask to write as output state - 1 means HIGH, 0 means LOW.
Definition pca9554.h:48
size_t reg_width_
width of registers
Definition pca9554.h:44
void digital_write_hw(uint8_t pin, bool value) override
Definition pca9554.cpp:83
bool digital_read_hw(uint8_t pin) override
Definition pca9554.cpp:73
uint16_t input_mask_
The state of the actual input pin states - 1 means HIGH, 0 means LOW.
Definition pca9554.h:50
void pin_mode(uint8_t pin, gpio::Flags flags)
Helper function to set the pin mode of a pin.
Definition pca9554.cpp:92
uint16_t config_mask_
Mask for the pin config - 1 means OUTPUT, 0 means INPUT.
Definition pca9554.h:46
void setup() override
Check i2c availability and setup masks.
Definition pca9554.cpp:15
esphome::i2c::ErrorCode last_error_
Storage for last I2C error seen.
Definition pca9554.h:52
void pin_mode(gpio::Flags flags) override
Definition pca9554.cpp:148
PCA9554Component * parent_
Definition pca9554.h:73
void digital_write(bool value) override
Definition pca9554.cpp:150
size_t dump_summary(char *buffer, size_t len) const override
Definition pca9554.cpp:151
uint16_t flags
@ INTERRUPT_FALLING_EDGE
Definition gpio.h:51
@ FLAG_OUTPUT
Definition gpio.h:28
@ FLAG_INPUT
Definition gpio.h:27
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:14
const uint8_t INPUT_REG
Definition pca9554.cpp:8
const uint8_t INVERT_REG
Definition pca9554.cpp:10
const uint8_t CONFIG_REG
Definition pca9554.cpp:11
const uint8_t OUTPUT_REG
Definition pca9554.cpp:9
constexpr float IO
For components that represent GPIO pins like PCF8573.
Definition component.h:38
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:1045