Reset the Elecrow's GT911 the way Elecrow's own V3.0 example does (0.2.96, firmware 0.2.82)
CI / checks (push) Waiting to run
CI / firmware (add-on) (push) Blocked by required conditions
CI / firmware (min_version) (push) Blocked by required conditions

The 2 ms reset of ESPHome's expander support (0.2.81) left the chip silent: "Calibration error" with the PCA9557
answering at 0x18. A component of smart_display's (elecrow_touch_reset.h, setup priority 700: after the I2C bus,
before the GT911 driver) writes the expander as Elecrow's V3.0 ESPHome example does: every pin an output and low,
20 ms, the reset released, 100 ms, the interrupt line back to an input. The driver's reset and interrupt pins are gone.
This commit is contained in:
Xandrossian
2026-09-21 13:30:41 -07:00
parent da28d19961
commit 4ec8c7a935
9 changed files with 96 additions and 36 deletions
+17 -1
View File
@@ -7,12 +7,16 @@ into a screen's YAML.
`smart_display: partitions: 4mb_wide_slots` (firmware 0.2.79) builds with one of the partition tables of partitions.py
instead of ESPHome's own: two update slots of 1,984 KB on a 4 MB ESP32-S3, which the Elecrow CrowPanel 7.0" needs.
`smart_display: elecrow_touch_reset: {i2c_id: ..., address: 0x18}` (firmware 0.2.82) brings the GT911 of the Elecrow
CrowPanel 7.0" V3.0 out of reset through its PCA9557 before the touch driver runs (elecrow_touch_reset.h).
"""
import logging
import esphome.codegen as cg
from esphome.components import esp32
from esphome.components import esp32, i2c
import esphome.config_validation as cv
from esphome.const import CONF_ID
from esphome.core import CORE
from esphome.helpers import write_file_if_changed
@@ -24,6 +28,10 @@ _LOGGER = logging.getLogger(__name__)
CONF_LANGUAGE = "language"
CONF_PARTITIONS = "partitions"
CONF_ELECROW_TOUCH_RESET = "elecrow_touch_reset"
elecrow_ns = cg.global_ns.namespace("elecrow")
TouchReset = elecrow_ns.class_("TouchReset", cg.Component, i2c.I2CDevice)
def _language(value):
@@ -36,6 +44,8 @@ def _language(value):
CONFIG_SCHEMA = cv.Schema({
cv.Optional(CONF_LANGUAGE, default="en"): _language,
cv.Optional(CONF_PARTITIONS): cv.one_of(*partitions.TABLES, lower=True),
cv.Optional(CONF_ELECROW_TOUCH_RESET): cv.Schema({cv.GenerateID(): cv.declare_id(TouchReset)})
.extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x18)),
})
@@ -51,3 +61,9 @@ async def to_code(config):
table = CORE.relative_build_path("smart_display_partitions.csv")
write_file_if_changed(table, partitions.TABLES[config[CONF_PARTITIONS]])
esp32.add_extra_build_file("partitions.csv", table)
if CONF_ELECROW_TOUCH_RESET in config:
conf = config[CONF_ELECROW_TOUCH_RESET]
reset = cg.new_Pvariable(conf[CONF_ID])
await cg.register_component(reset, conf)
await i2c.register_i2c_device(reset, conf)
cg.add_build_flag("-DESP_SCREENS_ELECROW_TOUCH_RESET=1")
@@ -0,0 +1,41 @@
#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.
//
// 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 {
auto write = [this](uint8_t reg, uint8_t value) {
const uint8_t data[2] = {reg, value};
return this->write(data, sizeof(data)) == esphome::i2c::ERROR_OK;
};
bool ok = write(0x03, 0x00); // configuration: every pin an output
ok = write(0x01, 0x00) && ok; // output: reset low, interrupt low (address 0x5D), the rest low
esphome::delay(20);
ok = write(0x01, 0x01) && ok; // reset released, the interrupt line still low
esphome::delay(100);
ok = write(0x03, 0x02) && ok; // the interrupt line an input again; the GT911 drives it from here
if (ok) {
ESP_LOGI("touch", "GT911 reset through the PCA9557 at 0x%02X", this->address_);
} 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_);
}
}
};
} // namespace elecrow
#endif