ESPHome 2026.6.0-dev
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
14
15static const char *const TAG = "tca9555";
16
18 if (!this->read_gpio_modes_()) {
19 this->mark_failed();
20 return;
21 }
22 if (!this->read_gpio_outputs_()) {
23 this->mark_failed();
24 return;
25 }
26
27 if (this->interrupt_pin_ != nullptr) {
28 this->interrupt_pin_->setup();
30 this->set_invalidate_on_read_(false);
31 }
32 this->disable_loop();
33}
36 ESP_LOGCONFIG(TAG, "TCA9555:");
37 LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
38 LOG_I2C_DEVICE(this)
39 if (this->is_failed()) {
40 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
41 }
42}
44 if (flags == gpio::FLAG_INPUT) {
45 // Set mode mask bit
46 this->mode_mask_ |= 1 << pin;
47 if (this->interrupt_pin_ == nullptr) {
48 this->enable_loop();
49 }
50 } else if (flags == gpio::FLAG_OUTPUT) {
51 // Clear mode mask bit
52 this->mode_mask_ &= ~(1 << pin);
53 }
54 // Write GPIO to enable input mode
55 this->write_gpio_modes_();
56}
58 this->reset_pin_cache_();
59 // Only disable the loop once INT has actually gone HIGH. Input transitions that straddle the
60 // I2C read leave INT asserted without re-firing a falling edge, which would strand us with
61 // stale state forever; keep looping until the line is released so we self-heal.
62 if (this->interrupt_pin_ != nullptr && this->interrupt_pin_->digital_read()) {
63 this->disable_loop();
64 }
65}
66
68 if (this->is_failed())
69 return false;
70 uint8_t data[2];
71 if (!this->read_bytes(TCA9555_OUTPUT_PORT_REGISTER_0, data, 2)) {
72 this->status_set_warning(LOG_STR("Failed to read output register"));
73 return false;
74 }
75 this->output_mask_ = (uint16_t(data[1]) << 8) | (uint16_t(data[0]) << 0);
77 return true;
78}
79
81 if (this->is_failed())
82 return false;
83 uint8_t data[2];
84 bool success = this->read_bytes(TCA9555_CONFIGURATION_PORT_0, data, 2);
85 if (!success) {
86 this->status_set_warning(LOG_STR("Failed to read mode register"));
87 return false;
88 }
89 this->mode_mask_ = (uint16_t(data[1]) << 8) | (uint16_t(data[0]) << 0);
90
92 return true;
93}
95 if (this->is_failed())
96 return false;
97 uint8_t data;
98 uint8_t bank_number = pin < 8 ? 0 : 1;
99 uint8_t register_to_read = bank_number ? TCA9555_INPUT_PORT_REGISTER_1 : TCA9555_INPUT_PORT_REGISTER_0;
100 if (!this->read_bytes(register_to_read, &data, 1)) {
101 this->status_set_warning(LOG_STR("Failed to read input register"));
102 return false;
103 }
104 uint8_t second_half = this->input_mask_ >> 8;
105 uint8_t first_half = this->input_mask_;
106 if (bank_number) {
107 this->input_mask_ = (data << 8) | (uint16_t(first_half) << 0);
108 } else {
109 this->input_mask_ = (uint16_t(second_half) << 8) | (data << 0);
110 }
111
112 this->status_clear_warning();
113 return true;
114}
115
116void TCA9555Component::digital_write_hw(uint8_t pin, bool value) {
117 if (this->is_failed())
118 return;
119
120 if (value) {
121 this->output_mask_ |= (1 << pin);
122 } else {
123 this->output_mask_ &= ~(1 << pin);
124 }
125
126 uint8_t data[2];
127 data[0] = this->output_mask_;
128 data[1] = this->output_mask_ >> 8;
129 if (!this->write_bytes(TCA9555_OUTPUT_PORT_REGISTER_0, data, 2)) {
130 this->status_set_warning(LOG_STR("Failed to write output register"));
131 return;
132 }
133
134 this->status_clear_warning();
135}
136
138 if (this->is_failed())
139 return false;
140 uint8_t data[2];
141
142 data[0] = this->mode_mask_;
143 data[1] = this->mode_mask_ >> 8;
144 if (!this->write_bytes(TCA9555_CONFIGURATION_PORT_0, data, 2)) {
145 this->status_set_warning(LOG_STR("Failed to write mode register"));
146 return false;
147 }
148 this->status_clear_warning();
149 return true;
150}
151
152bool TCA9555Component::digital_read_cache(uint8_t pin) { return this->input_mask_ & (1 << pin); }
153
155
156void TCA9555GPIOPin::setup() { this->pin_mode(this->flags_); }
157void TCA9555GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
158bool TCA9555GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
159void TCA9555GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
160size_t TCA9555GPIOPin::dump_summary(char *buffer, size_t len) const {
161 return buf_append_printf(buffer, len, 0, "%u via TCA9555", this->pin_);
162}
163
164} // namespace esphome::tca9555
void mark_failed()
Mark this component as failed.
bool is_failed() const
Definition component.h:272
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:246
void disable_loop()
Disable this component's loop.
void status_clear_warning()
Definition component.h:289
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:94
bool digital_read_cache(uint8_t pin) override
Definition tca9555.cpp:152
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:17
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:154
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:116
void pin_mode(uint8_t pin, gpio::Flags flags)
Definition tca9555.cpp:43
static void IRAM_ATTR gpio_intr(TCA9555Component *arg)
Definition tca9555.cpp:34
void digital_write(bool value) override
Definition tca9555.cpp:159
size_t dump_summary(char *buffer, size_t len) const override
Definition tca9555.cpp:160
void pin_mode(gpio::Flags flags) override
Definition tca9555.cpp:157
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:39
const void size_t len
Definition hal.h:64