#pragma once // The GT911 of the Elecrow CrowPanel 7.0" V3.0 hangs on a PCA9557 I/O expander (I2C 0x18): IO0 is its reset, IO1 its // interrupt line, and it stays in reset until something drives them (firmware 0.2.82). This is Elecrow's own sequence // from their V3.0 ESPHome example, byte for byte: every expander pin an output and low (the reset held, the interrupt // line low so the chip takes address 0x5D), 20 ms, the reset released, 100 ms for the chip to come up, then the // interrupt line back to an input. It runs after the I2C bus is up and before the GT911 driver looks for the chip // (setup priority 700, between the bus's 1000 and the driver's 600), so the driver finds it at 0x5D. A V2.0 board // has no expander: the writes fail, the log says so, and the GT911 boots by its pull-up as before. // // probe() and reset() are the touch_probe and touch_reset actions of the board file (firmware 0.2.83): the expander's // registers and whether a GT911 answers at 0x5D or 0x14 (its product id, "911"), and the same sequence with values of // one's own, for a diagnosis over the API without a new build. // // Only the Elecrow's board file builds this (`smart_display: elecrow_touch_reset:`, which sets the flag), so the // header adds nothing to a CYD, which has no I2C at all. #if defined(ESP_SCREENS_ELECROW_TOUCH_RESET) && ESP_SCREENS_ELECROW_TOUCH_RESET #include "esphome/components/i2c/i2c.h" #include "esphome/core/component.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" namespace elecrow { class TouchReset : public esphome::Component, public esphome::i2c::I2CDevice { public: float get_setup_priority() const override { return 700.0f; } void setup() override { this->reset(0x00, 0x00, 0x01, 20, 100, 0x02); } // Elecrow's sequence with its values: `config` the expander's configuration register during the reset (0 = every // pin an output), `low` its output register while the reset is held, `high` after the release, `final_config` // afterwards; then the probe. void reset(int config, int low, int high, int hold_ms, int wait_ms, int final_config) { bool ok = this->write_reg_(0x03, config); ok = this->write_reg_(0x01, low) && ok; esphome::delay(hold_ms); ok = this->write_reg_(0x01, high) && ok; esphome::delay(wait_ms); ok = this->write_reg_(0x03, final_config) && ok; if (ok) { ESP_LOGI("touch", "GT911 reset through the PCA9557 at 0x%02X: config 0x%02X, out 0x%02X -> 0x%02X, %d + %d ms, config 0x%02X", this->address_, config, low, high, hold_ms, wait_ms, final_config); } else { ESP_LOGW("touch", "No PCA9557 at 0x%02X: the GT911 was not reset (a V2.0 board boots it by its pull-up)", this->address_); } this->probe(); } // The expander's four registers and the GT911's answer at both addresses. void probe() { uint8_t regs[4] = {0, 0, 0, 0}; bool ok = true; for (uint8_t reg = 0; reg < 4; ++reg) { ok = this->write(®, 1) == esphome::i2c::ERROR_OK && this->read(®s[reg], 1) == esphome::i2c::ERROR_OK && ok; } ESP_LOGI("touch", "PCA9557 at 0x%02X: %s input 0x%02X output 0x%02X polarity 0x%02X config 0x%02X", this->address_, ok ? "answers," : "no answer;", regs[0], regs[1], regs[2], regs[3]); for (uint8_t address : {0x5D, 0x14}) { esphome::i2c::I2CDevice chip; chip.set_i2c_bus(this->bus_); chip.set_i2c_address(address); const uint8_t product_id[2] = {0x81, 0x40}; uint8_t id[4] = {0, 0, 0, 0}; const bool written = chip.write(product_id, sizeof(product_id)) == esphome::i2c::ERROR_OK; const bool read = written && chip.read(id, sizeof(id)) == esphome::i2c::ERROR_OK; ESP_LOGI("touch", "GT911 at 0x%02X: %s%s product id %02X %02X %02X %02X (\"%c%c%c\")", address, written ? "write ok, " : "no write ack, ", read ? "read ok," : "no read,", id[0], id[1], id[2], id[3], id[0] >= 32 && id[0] < 127 ? id[0] : '.', id[1] >= 32 && id[1] < 127 ? id[1] : '.', id[2] >= 32 && id[2] < 127 ? id[2] : '.'); } } protected: bool write_reg_(uint8_t reg, int value) { const uint8_t data[2] = {reg, static_cast(value)}; return this->write(data, sizeof(data)) == esphome::i2c::ERROR_OK; } }; } // namespace elecrow #endif