#include "ch422g.h" #include "esphome/core/log.h" namespace esphome { namespace ch422g { static const uint8_t CH422G_REG_MODE = 0x24; static const uint8_t CH422G_MODE_OUTPUT = 0x01; // enables output mode on 0-7 static const uint8_t CH422G_MODE_OPEN_DRAIN = 0x04; // enables open drain mode on 8-11 static const uint8_t CH422G_REG_IN = 0x26; // read reg for input bits static const uint8_t CH422G_REG_OUT = 0x38; // write reg for output bits 0-7 static const uint8_t CH422G_REG_OUT_UPPER = 0x23; // write reg for output bits 8-11 static const char *const TAG = "ch422g"; void CH422GComponent::setup() { // set outputs before mode this->write_outputs_(); // Set mode and check for errors if (!this->set_mode_(this->mode_value_) || !this->read_inputs_()) { ESP_LOGE(TAG, "CH422G not detected at 0x%02X", this->address_); this->mark_failed(); return; } ESP_LOGCONFIG(TAG, "Initialization complete. Warning: %d, Error: %d", this->status_has_warning(), this->status_has_error()); } void CH422GComponent::loop() { // Clear all the previously read flags. this->pin_read_flags_ = 0x00; } void CH422GComponent::dump_config() { ESP_LOGCONFIG(TAG, "CH422G:"); LOG_I2C_DEVICE(this) if (this->is_failed()) { ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } void CH422GComponent::pin_mode(uint8_t pin, gpio::Flags flags) { if (pin < 8) { if (flags & gpio::FLAG_OUTPUT) { this->mode_value_ |= CH422G_MODE_OUTPUT; } } else { if (flags & gpio::FLAG_OPEN_DRAIN) { this->mode_value_ |= CH422G_MODE_OPEN_DRAIN; } } } bool CH422GComponent::digital_read(uint8_t pin) { if (this->pin_read_flags_ == 0 || this->pin_read_flags_ & (1 << pin)) { // Read values on first access or in case it's being read again in the same loop this->read_inputs_(); } this->pin_read_flags_ |= (1 << pin); return (this->input_bits_ & (1 << pin)) != 0; } void CH422GComponent::digital_write(uint8_t pin, bool value) { if (value) { this->output_bits_ |= (1 << pin); } else { this->output_bits_ &= ~(1 << pin); } this->write_outputs_(); } bool CH422GComponent::read_inputs_() { if (this->is_failed()) { return false; } uint8_t result; // reading inputs requires the chip to be in input mode, possibly temporarily. if (this->mode_value_ & CH422G_MODE_OUTPUT) { this->set_mode_(this->mode_value_ & ~CH422G_MODE_OUTPUT); result = this->read_reg_(CH422G_REG_IN); this->set_mode_(this->mode_value_); } else { result = this->read_reg_(CH422G_REG_IN); } this->input_bits_ = result; this->status_clear_warning(); return true; } // Write a register. Can't use the standard write_byte() method because there is no single pre-configured i2c address. bool CH422GComponent::write_reg_(uint8_t reg, uint8_t value) { auto err = this->bus_->write_readv(reg, &value, 1, nullptr, 0); if (err != i2c::ERROR_OK) { this->status_set_warning(str_sprintf("write failed for register 0x%X, error %d", reg, err).c_str()); return false; } this->status_clear_warning(); return true; } uint8_t CH422GComponent::read_reg_(uint8_t reg) { uint8_t value; auto err = this->bus_->write_readv(reg, nullptr, 0, &value, 1); if (err != i2c::ERROR_OK) { this->status_set_warning(str_sprintf("read failed for register 0x%X, error %d", reg, err).c_str()); return 0; } this->status_clear_warning(); return value; } bool CH422GComponent::set_mode_(uint8_t mode) { return this->write_reg_(CH422G_REG_MODE, mode); } bool CH422GComponent::write_outputs_() { return this->write_reg_(CH422G_REG_OUT, static_cast(this->output_bits_)) && this->write_reg_(CH422G_REG_OUT_UPPER, static_cast(this->output_bits_ >> 8)); } float CH422GComponent::get_setup_priority() const { return setup_priority::IO; } // Run our loop() method very early in the loop, so that we cache read values // before other components call our digital_read() method. float CH422GComponent::get_loop_priority() const { return 9.0f; } // Just after WIFI void CH422GGPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); } bool CH422GGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) ^ this->inverted_; } void CH422GGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value ^ this->inverted_); } std::string CH422GGPIOPin::dump_summary() const { return str_sprintf("EXIO%u via CH422G", pin_); } void CH422GGPIOPin::set_flags(gpio::Flags flags) { flags_ = flags; this->parent_->pin_mode(this->pin_, flags); } } // namespace ch422g } // namespace esphome