ESPHome 2026.4.3
Loading...
Searching...
No Matches
tca9555.cpp
Go to the documentation of this file.
1#include "tca9555.h"
2#include "esphome/core/log.h"
3
4static const uint8_t TCA9555_INPUT_PORT_REGISTER_0 = 0x00;
5static const uint8_t TCA9555_INPUT_PORT_REGISTER_1 = 0x01;
6static const uint8_t TCA9555_OUTPUT_PORT_REGISTER_0 = 0x02;
7static const uint8_t TCA9555_OUTPUT_PORT_REGISTER_1 = 0x03;
8static const uint8_t TCA9555_POLARITY_REGISTER_0 = 0x04;
9static const uint8_t TCA9555_POLARITY_REGISTER_1 = 0x05;
10static const uint8_t TCA9555_CONFIGURATION_PORT_0 = 0x06;
11static const uint8_t TCA9555_CONFIGURATION_PORT_1 = 0x07;
12
13namespace esphome {
14namespace tca9555 {
15
16static const char *const TAG = "tca9555";
17
19 if (!this->read_gpio_modes_()) {
20 this->mark_failed();
21 return;
22 }
23 if (!this->read_gpio_outputs_()) {
24 this->mark_failed();
25 return;
26 }
27
28 if (this->interrupt_pin_ != nullptr) {
29 this->interrupt_pin_->setup();
31 this->set_invalidate_on_read_(false);
32 }
33 this->disable_loop();
34}
37 ESP_LOGCONFIG(TAG, "TCA9555:");
38 LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
39 LOG_I2C_DEVICE(this)
40 if (this->is_failed()) {
41 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
42 }
43}
45 if (flags == gpio::FLAG_INPUT) {
46 // Set mode mask bit
47 this->mode_mask_ |= 1 << pin;
48 if (this->interrupt_pin_ == nullptr) {
49 this->enable_loop();
50 }
51 } else if (flags == gpio::FLAG_OUTPUT) {
52 // Clear mode mask bit
53 this->mode_mask_ &= ~(1 << pin);
54 }
55 // Write GPIO to enable input mode
56 this->write_gpio_modes_();
57}
59 this->reset_pin_cache_();
60 // Only disable the loop once INT has actually gone HIGH. Input transitions that straddle the
61 // I2C read leave INT asserted without re-firing a falling edge, which would strand us with
62 // stale state forever; keep looping until the line is released so we self-heal.
63 if (this->interrupt_pin_ != nullptr && this->interrupt_pin_->digital_read()) {
64 this->disable_loop();
65 }
66}
67
69 if (this->is_failed())
70 return false;
71 uint8_t data[2];
72 if (!this->read_bytes(TCA9555_OUTPUT_PORT_REGISTER_0, data, 2)) {
73 this->status_set_warning(LOG_STR("Failed to read output register"));
74 return false;
75 }
76 this->output_mask_ = (uint16_t(data[1]) << 8) | (uint16_t(data[0]) << 0);
78 return true;
79}
80
82 if (this->is_failed())
83 return false;
84 uint8_t data[2];
85 bool success = this->read_bytes(TCA9555_CONFIGURATION_PORT_0, data, 2);
86 if (!success) {
87 this->status_set_warning(LOG_STR("Failed to read mode register"));
88 return false;
89 }
90 this->mode_mask_ = (uint16_t(data[1]) << 8) | (uint16_t(data[0]) << 0);
91
93 return true;
94}
96 if (this->is_failed())
97 return false;
98 uint8_t data;
99 uint8_t bank_number = pin < 8 ? 0 : 1;
100 uint8_t register_to_read = bank_number ? TCA9555_INPUT_PORT_REGISTER_1 : TCA9555_INPUT_PORT_REGISTER_0;
101 if (!this->read_bytes(register_to_read, &data, 1)) {
102 this->status_set_warning(LOG_STR("Failed to read input register"));
103 return false;
104 }
105 uint8_t second_half = this->input_mask_ >> 8;
106 uint8_t first_half = this->input_mask_;
107 if (bank_number) {
108 this->input_mask_ = (data << 8) | (uint16_t(first_half) << 0);
109 } else {
110 this->input_mask_ = (uint16_t(second_half) << 8) | (data << 0);
111 }
112
113 this->status_clear_warning();
114 return true;
115}
116
117void TCA9555Component::digital_write_hw(uint8_t pin, bool value) {
118 if (this->is_failed())
119 return;
120
121 if (value) {
122 this->output_mask_ |= (1 << pin);
123 } else {
124 this->output_mask_ &= ~(1 << pin);
125 }
126
127 uint8_t data[2];
128 data[0] = this->output_mask_;
129 data[1] = this->output_mask_ >> 8;
130 if (!this->write_bytes(TCA9555_OUTPUT_PORT_REGISTER_0, data, 2)) {
131 this->status_set_warning(LOG_STR("Failed to write output register"));
132 return;
133 }
134
135 this->status_clear_warning();
136}
137
139 if (this->is_failed())
140 return false;
141 uint8_t data[2];
142
143 data[0] = this->mode_mask_;
144 data[1] = this->mode_mask_ >> 8;
145 if (!this->write_bytes(TCA9555_CONFIGURATION_PORT_0, data, 2)) {
146 this->status_set_warning(LOG_STR("Failed to write mode register"));
147 return false;
148 }
149 this->status_clear_warning();
150 return true;
151}
152
153bool TCA9555Component::digital_read_cache(uint8_t pin) { return this->input_mask_ & (1 << pin); }
154
156
157void TCA9555GPIOPin::setup() { this->pin_mode(this->flags_); }
158void TCA9555GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
159bool TCA9555GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
160void TCA9555GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
161size_t TCA9555GPIOPin::dump_summary(char *buffer, size_t len) const {
162 return buf_append_printf(buffer, len, 0, "%u via TCA9555", this->pin_);
163}
164
165} // namespace tca9555
166} // 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
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 write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len) const
Definition i2c.h:251
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len)
Compat APIs All methods below have been added for compatibility reasons.
Definition i2c.h:217
bool digital_read_hw(uint8_t pin) override
Definition tca9555.cpp:95
bool digital_read_cache(uint8_t pin) override
Definition tca9555.cpp:153
uint16_t mode_mask_
Mask for the pin mode - 1 means output, 0 means input.
Definition tca9555.h:36
InternalGPIOPin * interrupt_pin_
Definition tca9555.h:46
void setup() override
Check i2c availability and setup masks.
Definition tca9555.cpp:18
uint16_t input_mask_
The state read in digital_read_hw - 1 means HIGH, 0 means LOW.
Definition tca9555.h:40
float get_setup_priority() const override
Definition tca9555.cpp:155
uint16_t output_mask_
The mask to write as output state - 1 means HIGH, 0 means LOW.
Definition tca9555.h:38
void digital_write_hw(uint8_t pin, bool value) override
Definition tca9555.cpp:117
void pin_mode(uint8_t pin, gpio::Flags flags)
Definition tca9555.cpp:44
static void IRAM_ATTR gpio_intr(TCA9555Component *arg)
Definition tca9555.cpp:35
void digital_write(bool value) override
Definition tca9555.cpp:160
size_t dump_summary(char *buffer, size_t len) const override
Definition tca9555.cpp:161
void pin_mode(gpio::Flags flags) override
Definition tca9555.cpp:158
uint16_t flags
@ INTERRUPT_FALLING_EDGE
Definition gpio.h:51
@ FLAG_OUTPUT
Definition gpio.h:28
@ FLAG_INPUT
Definition gpio.h:27
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