diff --git a/CODEOWNERS b/CODEOWNERS index d786dc165e..931e699566 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -89,6 +89,7 @@ esphome/components/json/* @OttoWinter esphome/components/kalman_combinator/* @Cat-Ion esphome/components/ledc/* @OttoWinter esphome/components/light/* @esphome/core +esphome/components/lilygo_t5_47/touchscreen/* @jesserockz esphome/components/lock/* @esphome/core esphome/components/logger/* @esphome/core esphome/components/ltr390/* @sjtrny diff --git a/esphome/codegen.py b/esphome/codegen.py index 3ea3df8706..52191e05e2 100644 --- a/esphome/codegen.py +++ b/esphome/codegen.py @@ -81,4 +81,5 @@ from esphome.cpp_types import ( # noqa InternalGPIOPin, gpio_Flags, EntityCategory, + Parented, ) diff --git a/esphome/components/lilygo_t5_47/__init__.py b/esphome/components/lilygo_t5_47/__init__.py new file mode 100644 index 0000000000..5499d096a9 --- /dev/null +++ b/esphome/components/lilygo_t5_47/__init__.py @@ -0,0 +1,3 @@ +import esphome.codegen as cg + +lilygo_t5_47_ns = cg.esphome_ns.namespace("lilygo_t5_47") diff --git a/esphome/components/lilygo_t5_47/touchscreen/__init__.py b/esphome/components/lilygo_t5_47/touchscreen/__init__.py new file mode 100644 index 0000000000..9ec6c925ee --- /dev/null +++ b/esphome/components/lilygo_t5_47/touchscreen/__init__.py @@ -0,0 +1,45 @@ +import esphome.codegen as cg +import esphome.config_validation as cv + +from esphome import pins +from esphome.components import i2c, touchscreen +from esphome.const import CONF_ID + +from .. import lilygo_t5_47_ns + +CODEOWNERS = ["@jesserockz"] +DEPENDENCIES = ["i2c"] + +LilygoT547Touchscreen = lilygo_t5_47_ns.class_( + "LilygoT547Touchscreen", + touchscreen.Touchscreen, + cg.Component, + i2c.I2CDevice, +) + +CONF_LILYGO_T5_47_TOUCHSCREEN_ID = "lilygo_t5_47_touchscreen_id" +CONF_INTERRUPT_PIN = "interrupt_pin" + + +CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(LilygoT547Touchscreen), + cv.Required(CONF_INTERRUPT_PIN): cv.All( + pins.internal_gpio_input_pin_schema + ), + } + ) + .extend(i2c.i2c_device_schema(0x5A)) + .extend(cv.COMPONENT_SCHEMA) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + await touchscreen.register_touchscreen(var, config) + + interrupt_pin = await cg.gpio_pin_expression(config[CONF_INTERRUPT_PIN]) + cg.add(var.set_interrupt_pin(interrupt_pin)) diff --git a/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp b/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp new file mode 100644 index 0000000000..b5cf63980b --- /dev/null +++ b/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp @@ -0,0 +1,141 @@ +#include "lilygo_t5_47_touchscreen.h" + +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace lilygo_t5_47 { + +static const char *const TAG = "lilygo_t5_47.touchscreen"; + +static const uint8_t POWER_REGISTER = 0xD6; +static const uint8_t TOUCH_REGISTER = 0xD0; + +static const uint8_t WAKEUP_CMD[1] = {0x06}; +static const uint8_t READ_FLAGS[1] = {0x00}; +static const uint8_t CLEAR_FLAGS[2] = {0x00, 0xAB}; +static const uint8_t READ_TOUCH[1] = {0x07}; + +#define ERROR_CHECK(err) \ + if ((err) != i2c::ERROR_OK) { \ + ESP_LOGE(TAG, "Failed to communicate!"); \ + this->status_set_warning(); \ + return; \ + } + +void Store::gpio_intr(Store *store) { store->touch = true; } + +void LilygoT547Touchscreen::setup() { + ESP_LOGCONFIG(TAG, "Setting up Lilygo T5 4.7 Touchscreen..."); + this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); + this->interrupt_pin_->setup(); + + this->store_.pin = this->interrupt_pin_->to_isr(); + this->interrupt_pin_->attach_interrupt(Store::gpio_intr, &this->store_, gpio::INTERRUPT_FALLING_EDGE); + + if (this->write(nullptr, 0) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "Failed to communicate!"); + this->interrupt_pin_->detach_interrupt(); + this->mark_failed(); + return; + } + + this->write_register(POWER_REGISTER, WAKEUP_CMD, 1); +} + +void LilygoT547Touchscreen::loop() { + if (!this->store_.touch) { + for (auto *listener : this->touch_listeners_) + listener->release(); + return; + } + this->store_.touch = false; + + uint8_t point = 0; + uint8_t buffer[40] = {0}; + uint32_t sum_l = 0, sum_h = 0; + + i2c::ErrorCode err; + err = this->write_register(TOUCH_REGISTER, READ_FLAGS, 1); + ERROR_CHECK(err); + + err = this->read(buffer, 7); + ERROR_CHECK(err); + + if (buffer[0] == 0xAB) { + this->write_register(TOUCH_REGISTER, CLEAR_FLAGS, 2); + return; + } + + point = buffer[5] & 0xF; + + if (point == 0) { + for (auto *listener : this->touch_listeners_) + listener->release(); + return; + } else if (point == 1) { + err = this->write_register(TOUCH_REGISTER, READ_TOUCH, 1); + ERROR_CHECK(err); + err = this->read(&buffer[5], 2); + ERROR_CHECK(err); + + sum_l = buffer[5] << 8 | buffer[6]; + } else if (point > 1) { + err = this->write_register(TOUCH_REGISTER, READ_TOUCH, 1); + ERROR_CHECK(err); + err = this->read(&buffer[5], 5 * (point - 1) + 3); + ERROR_CHECK(err); + + sum_l = buffer[5 * point + 1] << 8 | buffer[5 * point + 2]; + } + + this->write_register(TOUCH_REGISTER, CLEAR_FLAGS, 2); + + for (int i = 0; i < 5 * point; i++) + sum_h += buffer[i]; + + if (sum_l != sum_h) + point = 0; + + if (point) { + uint8_t offset; + for (int i = 0; i < point; i++) { + if (i == 0) { + offset = 0; + } else { + offset = 4; + } + + TouchPoint tp; + + tp.id = (buffer[i * 5 + offset] >> 4) & 0x0F; + tp.state = buffer[i * 5 + offset] & 0x0F; + if (tp.state == 0x06) + tp.state = 0x07; + + tp.y = (uint16_t)((buffer[i * 5 + 1 + offset] << 4) | ((buffer[i * 5 + 3 + offset] >> 4) & 0x0F)); + tp.x = (uint16_t)((buffer[i * 5 + 2 + offset] << 4) | (buffer[i * 5 + 3 + offset] & 0x0F)); + + this->defer([this, tp]() { this->send_touch_(tp); }); + } + } else { + TouchPoint tp; + tp.id = (buffer[0] >> 4) & 0x0F; + tp.state = 0x06; + tp.y = (uint16_t)((buffer[0 * 5 + 1] << 4) | ((buffer[0 * 5 + 3] >> 4) & 0x0F)); + tp.x = (uint16_t)((buffer[0 * 5 + 2] << 4) | (buffer[0 * 5 + 3] & 0x0F)); + + this->defer([this, tp]() { this->send_touch_(tp); }); + } + + this->status_clear_warning(); +} + +void LilygoT547Touchscreen::dump_config() { + ESP_LOGCONFIG(TAG, "Lilygo T5 47 Touchscreen:"); + LOG_I2C_DEVICE(this); + LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); +} + +} // namespace lilygo_t5_47 +} // namespace esphome diff --git a/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.h b/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.h new file mode 100644 index 0000000000..3d00e0b117 --- /dev/null +++ b/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.h @@ -0,0 +1,35 @@ +#pragma once + +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/touchscreen/touchscreen.h" +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/core/hal.h" + +namespace esphome { +namespace lilygo_t5_47 { + +struct Store { + volatile bool touch; + ISRInternalGPIOPin pin; + + static void gpio_intr(Store *store); +}; + +using namespace touchscreen; + +class LilygoT547Touchscreen : public Touchscreen, public Component, public i2c::I2CDevice { + public: + void setup() override; + void loop() override; + void dump_config() override; + + void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; } + + protected: + InternalGPIOPin *interrupt_pin_; + Store store_; +}; + +} // namespace lilygo_t5_47 +} // namespace esphome diff --git a/esphome/components/touchscreen/binary_sensor/__init__.py b/esphome/components/touchscreen/binary_sensor/__init__.py index cbd03c0a32..9dba821d4d 100644 --- a/esphome/components/touchscreen/binary_sensor/__init__.py +++ b/esphome/components/touchscreen/binary_sensor/__init__.py @@ -9,7 +9,11 @@ from .. import touchscreen_ns, CONF_TOUCHSCREEN_ID, Touchscreen, TouchListener DEPENDENCIES = ["touchscreen"] TouchscreenBinarySensor = touchscreen_ns.class_( - "TouchscreenBinarySensor", binary_sensor.BinarySensor, TouchListener + "TouchscreenBinarySensor", + binary_sensor.BinarySensor, + cg.Component, + TouchListener, + cg.Parented.template(Touchscreen), ) CONF_X_MIN = "x_min" @@ -39,7 +43,7 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_Y_MIN): cv.int_range(min=0, max=2000), cv.Required(CONF_Y_MAX): cv.int_range(min=0, max=2000), } - ), + ).extend(cv.COMPONENT_SCHEMA), validate_coords, ) @@ -47,7 +51,9 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await binary_sensor.register_binary_sensor(var, config) - hub = await cg.get_variable(config[CONF_TOUCHSCREEN_ID]) + await cg.register_component(var, config) + await cg.register_parented(var, config[CONF_TOUCHSCREEN_ID]) + cg.add( var.set_area( config[CONF_X_MIN], @@ -56,4 +62,3 @@ async def to_code(config): config[CONF_Y_MAX], ) ) - cg.add(hub.register_listener(var)) diff --git a/esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.h b/esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.h index 8fb7749766..7b8cac5c4c 100644 --- a/esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.h +++ b/esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.h @@ -1,13 +1,20 @@ #pragma once -#include "esphome/components/touchscreen/touchscreen.h" #include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/components/touchscreen/touchscreen.h" +#include "esphome/core/component.h" +#include "esphome/core/helpers.h" namespace esphome { namespace touchscreen { -class TouchscreenBinarySensor : public binary_sensor::BinarySensor, public TouchListener { +class TouchscreenBinarySensor : public binary_sensor::BinarySensor, + public Component, + public TouchListener, + public Parented { public: + void setup() override { this->parent_->register_listener(this); } + /// Set the touch screen area where the button will detect the touch. void set_area(int16_t x_min, int16_t x_max, int16_t y_min, int16_t y_max) { this->x_min_ = x_min; diff --git a/esphome/cpp_types.py b/esphome/cpp_types.py index 806a2d832c..110641d6c9 100644 --- a/esphome/cpp_types.py +++ b/esphome/cpp_types.py @@ -34,3 +34,4 @@ InternalGPIOPin = esphome_ns.class_("InternalGPIOPin", GPIOPin) gpio_ns = esphome_ns.namespace("gpio") gpio_Flags = gpio_ns.enum("Flags", is_class=True) EntityCategory = esphome_ns.enum("EntityCategory") +Parented = esphome_ns.class_("Parented") diff --git a/tests/test4.yaml b/tests/test4.yaml index de641d92ff..998db8ed2d 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -328,6 +328,7 @@ binary_sensor: number: 3 - platform: touchscreen + touchscreen_id: lilygo_touchscreen id: touch_key1 x_min: 0 x_max: 100 @@ -561,3 +562,12 @@ touchscreen: - logger.log: format: Touch at (%d, %d) args: ["touch.x", "touch.y"] + + - platform: lilygo_t5_47 + id: lilygo_touchscreen + interrupt_pin: GPIO36 + display: inkplate_display + on_touch: + - logger.log: + format: Touch at (%d, %d) + args: ["touch.x", "touch.y"]