#include "tca6408.h" #include "esphome/core/log.h" namespace esphome { namespace tca6408 { static const char *const TAG = "tca6408"; // Registers static const uint8_t REG_INPUT = 0x00; static const uint8_t REG_OUTPUT = 0x01; static const uint8_t REG_POLARITY = 0x02; static const uint8_t REG_CONFIG = 0x03; void TCA6408Component::setup() { ESP_LOGCONFIG(TAG, "Setting up TCA6408 @ 0x%02X", this->address_); // Default safe state: all inputs this->mode_mask_ = 0xFF; this->output_mask_ = 0x00; this->input_mask_ = 0x00; this->polarity_mask_ = 0x00; if (this->write_register(REG_POLARITY, 0x00) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Failed to init polarity register"); this->mark_failed(); return; } if (this->write_register(REG_CONFIG, this->mode_mask_) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Failed to init config register"); this->mark_failed(); return; } this->read_inputs_(); if (this->interrupt_pin_ != nullptr) { this->interrupt_pin_->setup(); this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); this->interrupt_pin_->attach_interrupt( &TCA6408Component::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE); this->enable_loop(); ESP_LOGCONFIG(TAG, "Interrupt mode enabled"); } else { this->enable_loop(); ESP_LOGCONFIG(TAG, "Polling mode enabled"); } ESP_LOGCONFIG(TAG, "TCA6408 setup complete"); } void IRAM_ATTR TCA6408Component::gpio_intr(TCA6408Component *arg) { arg->interrupt_triggered_ = true; arg->enable_loop_soon_any_context(); } void TCA6408Component::loop() { if (this->interrupt_triggered_) { this->interrupt_triggered_ = false; this->read_inputs_(); this->reset_pin_cache_(); if (this->interrupt_pin_ && this->interrupt_pin_->digital_read()) { // INT released -> stop looping until next interrupt this->disable_loop(); } } } void TCA6408Component::dump_config() { ESP_LOGCONFIG(TAG, "TCA6408:"); LOG_I2C_DEVICE(this); ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); LOG_PIN(" INT Pin: ", this->interrupt_pin_); } bool TCA6408Component::read_inputs_() { uint8_t data = 0; if (this->read_register(REG_INPUT, &data) != i2c::ERROR_OK) { this->status_set_warning(); return false; } this->input_mask_ = data; this->status_clear_warning(); return true; } bool TCA6408Component::digital_read_hw(uint8_t pin) { // full refresh for accuracy return this->read_inputs_(); } bool TCA6408Component::digital_read_cache(uint8_t pin) { bool value = (this->input_mask_ >> pin) & 0x01; if (this->polarity_mask_ & (1 << pin)) { value = !value; } return value; } void TCA6408Component::digital_write_hw(uint8_t pin, bool value) { if (value) this->output_mask_ |= (1 << pin); else this->output_mask_ &= ~(1 << pin); if (this->write_register(REG_OUTPUT, this->output_mask_) != i2c::ERROR_OK) { this->status_set_warning(); } else { this->status_clear_warning(); } } void TCA6408Component::pin_mode(uint8_t pin, gpio::Flags flags) { if (flags & gpio::FLAG_OUTPUT) { this->mode_mask_ &= ~(1 << pin); } else { this->mode_mask_ |= (1 << pin); } this->write_register(REG_CONFIG, this->mode_mask_); } void TCA6408Component::set_polarity_inversion(uint8_t pin, bool inverted) { if (inverted) this->polarity_mask_ |= (1 << pin); else this->polarity_mask_ &= ~(1 << pin); this->write_register(REG_POLARITY, this->polarity_mask_); } // ================= GPIO PIN WRAPPER ================= void TCA6408GPIOPin::set_parent(TCA6408Component *parent) { this->parent_ = parent; } void TCA6408GPIOPin::set_pin(uint8_t pin) { this->pin_ = pin; } void TCA6408GPIOPin::setup() { this->pin_mode(this->flags_); } void TCA6408GPIOPin::pin_mode(gpio::Flags flags) { this->flags_ = flags; this->parent_->pin_mode(this->pin_, flags); } bool TCA6408GPIOPin::digital_read() { return this->parent_->digital_read_cache(this->pin_) != this->inverted_; } void TCA6408GPIOPin::digital_write(bool value) { this->parent_->digital_write_hw(this->pin_, value != this->inverted_); } void TCA6408GPIOPin::set_inverted(bool inverted) { this->inverted_ = inverted; this->parent_->set_polarity_inversion(this->pin_, inverted); } size_t TCA6408GPIOPin::dump_summary(char *buffer, size_t len) const { return snprintf(buffer, len, "TCA6408 pin %u", this->pin_); } } // namespace tca6408 } // namespace esphome