#include "tca9555.h" #include "esphome/core/log.h" static const uint8_t TCA9555_INPUT_PORT_REGISTER_0 = 0x00; static const uint8_t TCA9555_INPUT_PORT_REGISTER_1 = 0x01; static const uint8_t TCA9555_OUTPUT_PORT_REGISTER_0 = 0x02; static const uint8_t TCA9555_OUTPUT_PORT_REGISTER_1 = 0x03; static const uint8_t TCA9555_POLARITY_REGISTER_0 = 0x04; static const uint8_t TCA9555_POLARITY_REGISTER_1 = 0x05; static const uint8_t TCA9555_CONFIGURATION_PORT_0 = 0x06; static const uint8_t TCA9555_CONFIGURATION_PORT_1 = 0x07; namespace esphome { namespace tca9555 { static const char *const TAG = "tca9555"; void TCA9555Component::setup() { if (!this->read_gpio_modes_()) { this->mark_failed(); return; } if (!this->read_gpio_outputs_()) { this->mark_failed(); return; } if (this->interrupt_pin_ != nullptr) { this->interrupt_pin_->setup(); this->interrupt_pin_->attach_interrupt(&TCA9555Component::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE); this->set_invalidate_on_read_(false); } this->disable_loop(); } void IRAM_ATTR TCA9555Component::gpio_intr(TCA9555Component *arg) { arg->enable_loop_soon_any_context(); } void TCA9555Component::dump_config() { ESP_LOGCONFIG(TAG, "TCA9555:"); LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); LOG_I2C_DEVICE(this) if (this->is_failed()) { ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } void TCA9555Component::pin_mode(uint8_t pin, gpio::Flags flags) { if (flags == gpio::FLAG_INPUT) { // Set mode mask bit this->mode_mask_ |= 1 << pin; if (this->interrupt_pin_ == nullptr) { this->enable_loop(); } } else if (flags == gpio::FLAG_OUTPUT) { // Clear mode mask bit this->mode_mask_ &= ~(1 << pin); } // Write GPIO to enable input mode this->write_gpio_modes_(); } void TCA9555Component::loop() { this->reset_pin_cache_(); if (this->interrupt_pin_ != nullptr) { this->disable_loop(); } } bool TCA9555Component::read_gpio_outputs_() { if (this->is_failed()) return false; uint8_t data[2]; if (!this->read_bytes(TCA9555_OUTPUT_PORT_REGISTER_0, data, 2)) { this->status_set_warning(LOG_STR("Failed to read output register")); return false; } this->output_mask_ = (uint16_t(data[1]) << 8) | (uint16_t(data[0]) << 0); this->status_clear_warning(); return true; } bool TCA9555Component::read_gpio_modes_() { if (this->is_failed()) return false; uint8_t data[2]; bool success = this->read_bytes(TCA9555_CONFIGURATION_PORT_0, data, 2); if (!success) { this->status_set_warning(LOG_STR("Failed to read mode register")); return false; } this->mode_mask_ = (uint16_t(data[1]) << 8) | (uint16_t(data[0]) << 0); this->status_clear_warning(); return true; } bool TCA9555Component::digital_read_hw(uint8_t pin) { if (this->is_failed()) return false; uint8_t data; uint8_t bank_number = pin < 8 ? 0 : 1; uint8_t register_to_read = bank_number ? TCA9555_INPUT_PORT_REGISTER_1 : TCA9555_INPUT_PORT_REGISTER_0; if (!this->read_bytes(register_to_read, &data, 1)) { this->status_set_warning(LOG_STR("Failed to read input register")); return false; } uint8_t second_half = this->input_mask_ >> 8; uint8_t first_half = this->input_mask_; if (bank_number) { this->input_mask_ = (data << 8) | (uint16_t(first_half) << 0); } else { this->input_mask_ = (uint16_t(second_half) << 8) | (data << 0); } this->status_clear_warning(); return true; } void TCA9555Component::digital_write_hw(uint8_t pin, bool value) { if (this->is_failed()) return; if (value) { this->output_mask_ |= (1 << pin); } else { this->output_mask_ &= ~(1 << pin); } uint8_t data[2]; data[0] = this->output_mask_; data[1] = this->output_mask_ >> 8; if (!this->write_bytes(TCA9555_OUTPUT_PORT_REGISTER_0, data, 2)) { this->status_set_warning(LOG_STR("Failed to write output register")); return; } this->status_clear_warning(); } bool TCA9555Component::write_gpio_modes_() { if (this->is_failed()) return false; uint8_t data[2]; data[0] = this->mode_mask_; data[1] = this->mode_mask_ >> 8; if (!this->write_bytes(TCA9555_CONFIGURATION_PORT_0, data, 2)) { this->status_set_warning(LOG_STR("Failed to write mode register")); return false; } this->status_clear_warning(); return true; } bool TCA9555Component::digital_read_cache(uint8_t pin) { return this->input_mask_ & (1 << pin); } float TCA9555Component::get_setup_priority() const { return setup_priority::IO; } void TCA9555GPIOPin::setup() { this->pin_mode(this->flags_); } void TCA9555GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); } bool TCA9555GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } void TCA9555GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } size_t TCA9555GPIOPin::dump_summary(char *buffer, size_t len) const { return buf_append_printf(buffer, len, 0, "%u via TCA9555", this->pin_); } } // namespace tca9555 } // namespace esphome