diff --git a/components/smart_display/__init__.py b/components/smart_display/__init__.py index 6eef24d..e131c0c 100644 --- a/components/smart_display/__init__.py +++ b/components/smart_display/__init__.py @@ -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") diff --git a/components/smart_display/elecrow_touch_reset.h b/components/smart_display/elecrow_touch_reset.h new file mode 100644 index 0000000..054c838 --- /dev/null +++ b/components/smart_display/elecrow_touch_reset.h @@ -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 diff --git a/docs/ELECROW.md b/docs/ELECROW.md index a75dba7..33743ef 100644 --- a/docs/ELECROW.md +++ b/docs/ELECROW.md @@ -41,12 +41,13 @@ suits a screen that stands further away. The GT911 is polled every 20 ms over I2C at 400 kHz. The **V3.0** has a PCA9557 I/O expander (I2C 0x18) on the GT911's reset (IO0) and interrupt (IO1) lines, and a unit tested here kept its GT911 in reset until something drove -them (`touchscreen is marked FAILED: Calibration error` in the log, firmware 0.2.79 and 0.2.80). The board file -(firmware 0.2.81) declares the expander as ESPHome's `pca9554` (the same register map) and hands the GT911 driver its -reset and interrupt pins on it: the driver runs Elecrow's own sequence, reset low with the interrupt line low, reset -high, then 56 ms, which also fixes the address at 0x5D. A V2.0 board without the expander boots its GT911 by the -pull-up as before; the expander's setup fails there, harmlessly, and the driver tries 0x14 by itself when 0x5D does -not answer, so no address override is ever needed. +them (`touchscreen is marked FAILED: Calibration error` in the log, firmware 0.2.79 to 0.2.81; 0.2.81 tried ESPHome's +own expander support, whose 2 ms reset pulse the chip ignored). `smart_display: elecrow_touch_reset` (firmware 0.2.82, +`components/smart_display/elecrow_touch_reset.h`) runs the sequence of Elecrow's V3.0 ESPHome example before the +driver looks for the chip: 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, the interrupt line back to an input. The log says `GT911 reset +through the PCA9557`. A V2.0 board without the expander boots its GT911 by the pull-up as before (the writes fail, the +log says so), and the driver tries 0x14 by itself when 0x5D does not answer, so no address override is ever needed. The bus is scanned at boot. For a diagnosis, give the screen `logger: level: DEBUG` in its Override YAML (Screen settings → Override YAML) and read its log: the scan lists 0x18 (the expander) and 0x5D or 0x14 (the GT911), and the diff --git a/packages/boards/elecrow-crowpanel7.yaml b/packages/boards/elecrow-crowpanel7.yaml index eadb8f3..3bd9f96 100644 --- a/packages/boards/elecrow-crowpanel7.yaml +++ b/packages/boards/elecrow-crowpanel7.yaml @@ -383,6 +383,10 @@ psram: # CLOSE_CARDS_HOOK, MAY_OPEN_EXTRA, TICK_HOOK, AWAY_EXTRA and ALERT_SHOW_HOOK emptied) it is 1,754 KB, 86.4 %. smart_display: partitions: 4mb_wide_slots + # The GT911 out of reset through the V3.0's PCA9557 before the driver runs (elecrow_touch_reset.h, firmware 0.2.82). + elecrow_touch_reset: + i2c_id: touch_bus + address: 0x18 # The icon fonts at 2 bits a pixel (four levels of edge smoothing instead of sixteen) where the text keeps its four: # the three sizes of the 314 tile icons are 312 KB at 4 bits, half that at 2. The text fonts are the sharper for it. @@ -487,29 +491,21 @@ text_sensor: update_interval: 60s lambda: 'return {"elecrow"};' -# Pinout of the CrowPanel 7.0" (DIS08070H, V2.0 and V3.0): Elecrow's wiki and the ESPHome devices page. The V3.0 has a -# PCA9557 I/O expander at I2C 0x18 on the GT911's reset (IO0) and interrupt (IO1) lines, and the GT911 stays in reset -# until something drives them (firmware 0.2.81: a unit whose touch never answered, "Calibration error" at setup). The -# expander gets the same register map as the PCA9554 ESPHome knows, and the GT911 driver does Elecrow's own reset -# through it: reset low with the interrupt line low (that picks address 0x5D), reset high, 56 ms. On a V2.0 without -# the expander its setup fails, the GT911 boots by its pull-up as before, and the driver still finds it (it tries 0x14 -# by itself when 0x5D does not answer). +# Pinout of the CrowPanel 7.0" (DIS08070H, V2.0 and V3.0): Elecrow's wiki and their V3.0 ESPHome example. The V3.0 has +# a PCA9557 I/O expander at I2C 0x18 on the GT911's reset (IO0) and interrupt (IO1) lines, and the GT911 stays in +# reset until something drives them: on a unit tested here the driver reported "Calibration error" at setup +# (firmware 0.2.79 to 0.2.81, the last one through ESPHome's own expander support, whose 2 ms reset the chip ignored). +# elecrow_touch_reset (below) runs Elecrow's own sequence before the driver looks for the chip; a V2.0 without the +# expander boots its GT911 by the pull-up as before, and the driver tries 0x14 by itself when 0x5D does not answer. i2c: id: touch_bus sda: GPIO19 scl: GPIO20 frequency: 400kHz - # The scan logs what answers on the bus (0x18 the expander, 0x5D or 0x14 the GT911), with `logger: level: DEBUG` in - # a screen's Override YAML: which revision a screen is, and whether the bus is alive at all. It runs before the - # GT911's reset, so it upsets nothing. + # The scan logs what answers on the bus with `logger: level: DEBUG` in a screen's Override YAML: 0x18 is the + # expander; the GT911 is still in reset at that moment and shows up in the driver's own line instead. scan: true -pca9554: - - id: touch_expander - i2c_id: touch_bus - address: 0x18 - pin_count: 8 - output: - platform: ledc pin: GPIO2 @@ -533,12 +529,6 @@ touchscreen: i2c_id: touch_bus # The reset through the expander picks 0x5D; the driver tries 0x14 by itself when that does not answer. address: 0x5D - reset_pin: - pca9554: touch_expander - number: 0 - interrupt_pin: - pca9554: touch_expander - number: 1 display: my_display update_interval: 20ms transform: diff --git a/packages/core.yaml b/packages/core.yaml index eb2532e..c0021ce 100644 --- a/packages/core.yaml +++ b/packages/core.yaml @@ -14,7 +14,7 @@ # fonts/materialdesignicons-webfont.ttf (Apache 2.0, github.com/Templarian/MaterialDesign-Webfont). ############################################################ substitutions: - SCREEN_FIRMWARE_VERSION: "0.2.81" + SCREEN_FIRMWARE_VERSION: "0.2.82" DASHBOARD_INBOX_INTERNAL: "false" CALIBRATION_ON_BOOT: "false" # a bench build: start on the touch test page ROOM_NAME: "" # top left until ESP Screens sends the screen name diff --git a/screen_manager/CHANGELOG.md b/screen_manager/CHANGELOG.md index fe8ab33..8826612 100644 --- a/screen_manager/CHANGELOG.md +++ b/screen_manager/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.2.96 (firmware 0.2.82) + +The Elecrow's touch chip reset the way Elecrow does it. + +- **Elecrow CrowPanel 7.0" V3.0: Elecrow's own reset sequence.** Firmware 0.2.81 reset the GT911 through ESPHome's expander support, a 2 ms pulse the chip ignored on the unit tested (its log still said `Calibration error`, with the PCA9557 answering at 0x18). The board file now runs the sequence of Elecrow's V3.0 ESPHome example, byte for byte, before the touch driver looks for the chip: every expander pin an output and low, 20 ms, the reset released, 100 ms, the interrupt line back to an input (`smart_display: elecrow_touch_reset`, components/smart_display/elecrow_touch_reset.h). The log says `GT911 reset through the PCA9557`, or that no expander answered (a V2.0 board, which boots its GT911 by the pull-up). +- Needs firmware 0.2.82 on an Elecrow: press **Update** on the screen. Elecrow firmware: 1,866,128 bytes, 91.9 % of its update slot. + ## 0.2.95 (firmware 0.2.81) The Elecrow's touch chip brought out of reset. diff --git a/screen_manager/app/core.py b/screen_manager/app/core.py index 5790e92..97a3522 100644 --- a/screen_manager/app/core.py +++ b/screen_manager/app/core.py @@ -54,7 +54,7 @@ REFS = {'cyd': 'main', 'guition': 'main', 'elecrow': 'main'} # is the small one. The Elecrow CrowPanel 7.0" (app 0.2.93) is the Guition's kind at 800x480, without the Rotation. LARGE_BOARDS = ('guition', 'elecrow') # Firmware shipped with this app release; screens below it get an update offer. -FIRMWARE_VERSION = '0.2.81' +FIRMWARE_VERSION = '0.2.82' # The Auto standby switch a screen offers Home Assistant automations. AUTO_STANDBY_MIN_FIRMWARE = '0.2.41' # The settings page the screen opens itself, and the screen.settings tile that opens it. diff --git a/screen_manager/config.yaml b/screen_manager/config.yaml index 43c91f8..915eea6 100644 --- a/screen_manager/config.yaml +++ b/screen_manager/config.yaml @@ -1,5 +1,5 @@ name: ESP Screen Manager -version: "0.2.95" +version: "0.2.96" slug: esp_screen_manager description: Choose tiles for your CYD and Guition touchscreen, without reflashing. url: https://gittea.trippedonmy.name/Xandrossian/homeassistant_espscreen diff --git a/tests/test_elecrow.py b/tests/test_elecrow.py index c527f06..d35b1a4 100644 --- a/tests/test_elecrow.py +++ b/tests/test_elecrow.py @@ -57,10 +57,15 @@ class BoardFile(unittest.TestCase): self.assertIn('pin: GPIO2', TEXT) # The USB pins are the touch I2C: the header that frees them. self.assertIn('', TEXT) - # The V3.0's PCA9557 (as ESPHome's pca9554, the same registers) brings the GT911 out of reset (firmware 0.2.81). - self.assertIn('address: 0x18', TEXT) - for pin, number in (('reset_pin', 0), ('interrupt_pin', 1)): - self.assertIn(f' {pin}:\n pca9554: touch_expander\n number: {number}', TEXT) + # The V3.0's PCA9557 brings the GT911 out of reset before the driver runs (firmware 0.2.82). + self.assertIn(' elecrow_touch_reset:\n i2c_id: touch_bus\n address: 0x18', TEXT) + code = '\n'.join(line for line in TEXT.split('\n') if not line.lstrip().startswith('#')) + self.assertNotIn('reset_pin', code) + self.assertNotIn('interrupt_pin', code) + header = (ROOT / 'components/smart_display/elecrow_touch_reset.h').read_text() + for step in ('write(0x03, 0x00)', 'write(0x01, 0x00)', 'delay(20)', 'write(0x01, 0x01)', 'delay(100)', 'write(0x03, 0x02)'): + self.assertIn(step, header) + self.assertIn('return 700.0f', header) self.assertIn('assertion_level: SILENT', TEXT) def test_the_flash_fits_by_three_measures(self):