Touchscreen component and driver fixes (#5997)

* Introduce calibration settings for all touchscreen drivers.
this will override the common values.
The x,y coordinates only calculated when the right calibrations are set.

* resolve issues reported by CI

* remove unneeded spaces and newlines

* Forgot to remove some obsolete code

* remove get_setup_priority from xpt2046

* remove media_player changes.

* media_player: removed to much,

* Update suggestions

* referd back the `get_setup_priority` removal so it can be moved into a othe PR.

* tt21100: restore init read

* fix spacing

* load native display dimensions instead of using internal dimensons.
and load it only onse on setup

* moved `update_touches()` to protexted section

* adding Clydes PR#6049

* add multitouch test script

* Update all Touchscreen replacing `get_*_internal` to `get_native_*`

* fixed some CI recomendations

* couple of fixes

* make sure the display is running before  touchscreen is setup

* fix clang

* revert back last changes

* xpt2046: change log level for testing

* logging information

* add test file

* fix polling issue with the for example the xpt2046

* fixed some CI issues

* fixed some CI issues

* restore mirror parameter discriptions

* same for the swap_xy

* same for the transform

* remove the above const from const.py

* and put  the above const bacl const.py

* Merge branch 'nvds-touchscreen-fix1' of https://github.com/nielsnl68/esphome into nvds-touchscreen-fix1

* and put  the above const bacl const.py

* [tt21100] making interupt pin optional

* [tt21100] making interupt pin optional (now complete)

* update the display part based on @clyde'
s changes.

* fix issue with ft6x36 touvhscreen

* reverd back touch check. add comment

* add some extra checks to the ft6x36

* add an other log and a typo fixed

* okay an other fix.

* add an extra check like others do
and fix data type

* [ft6336] fix update race when ts is touched.

* [touchscreen] update some log's with a verbose level.

* fix clang issues

* fix the clang issues

* fix the clang issues

* fix virtual issue.

* fix the clang issues

* an other clang issues

* remove anti-aliased fonts support. It does not belong here.

* remove anti-aliased fonts support. It does not belong here.

* rename test script

* Moving the test files to there right location.

* rename the test files

* clean up the code

* add a new line

* clang fixings

* clang fixings

* remove comment

* remove comment

* Update esphome/components/touchscreen/__init__.py

Co-authored-by: guillempages <guillempages@users.noreply.github.com>

* Update esphome/components/touchscreen/__init__.py

Co-authored-by: guillempages <guillempages@users.noreply.github.com>

* Update esphome/components/touchscreen/__init__.py

Co-authored-by: guillempages <guillempages@users.noreply.github.com>

* Update esphome/components/touchscreen/touchscreen.cpp

* Update esphome/components/touchscreen/touchscreen.cpp

* [ft63x6] add threshold

---------

Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
Co-authored-by: guillempages <guillempages@users.noreply.github.com>
This commit is contained in:
NP v/d Spek 2024-02-28 03:42:11 +01:00 committed by GitHub
parent c43c9ad1c5
commit 5393a09872
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 422 additions and 157 deletions

View File

@ -175,10 +175,15 @@ class Display : public PollingComponent {
/// Clear the entire screen by filling it with OFF pixels. /// Clear the entire screen by filling it with OFF pixels.
void clear(); void clear();
/// Get the width of the image in pixels with rotation applied. /// Get the calculated width of the display in pixels with rotation applied.
virtual int get_width() = 0; virtual int get_width() { return this->get_width_internal(); }
/// Get the height of the image in pixels with rotation applied. /// Get the calculated height of the display in pixels with rotation applied.
virtual int get_height() = 0; virtual int get_height() { return this->get_height_internal(); }
/// Get the native (original) width of the display in pixels.
int get_native_width() { return this->get_width_internal(); }
/// Get the native (original) height of the display in pixels.
int get_native_height() { return this->get_height_internal(); }
/// Set a single pixel at the specified coordinates to default color. /// Set a single pixel at the specified coordinates to default color.
inline void draw_pixel_at(int x, int y) { this->draw_pixel_at(x, y, COLOR_ON); } inline void draw_pixel_at(int x, int y) { this->draw_pixel_at(x, y, COLOR_ON); }
@ -538,6 +543,9 @@ class Display : public PollingComponent {
void do_update_(); void do_update_();
void clear_clipping_(); void clear_clipping_();
virtual int get_height_internal() = 0;
virtual int get_width_internal() = 0;
/** /**
* This method fills a triangle using only integer variables by using a * This method fills a triangle using only integer variables by using a
* modified bresenham algorithm. * modified bresenham algorithm.

View File

@ -22,9 +22,6 @@ class DisplayBuffer : public Display {
/// Set a single pixel at the specified coordinates to the given color. /// Set a single pixel at the specified coordinates to the given color.
void draw_pixel_at(int x, int y, Color color) override; void draw_pixel_at(int x, int y, Color color) override;
virtual int get_height_internal() = 0;
virtual int get_width_internal() = 0;
protected: protected:
virtual void draw_absolute_pixel_internal(int x, int y, Color color) = 0; virtual void draw_absolute_pixel_internal(int x, int y, Color color) = 0;

View File

@ -34,6 +34,7 @@ void EKTF2232Touchscreen::setup() {
// Get touch resolution // Get touch resolution
uint8_t received[4]; uint8_t received[4];
if (this->x_raw_max_ == this->x_raw_min_) {
this->write(GET_X_RES, 4); this->write(GET_X_RES, 4);
if (this->read(received, 4)) { if (this->read(received, 4)) {
ESP_LOGE(TAG, "Failed to read X resolution!"); ESP_LOGE(TAG, "Failed to read X resolution!");
@ -42,7 +43,9 @@ void EKTF2232Touchscreen::setup() {
return; return;
} }
this->x_raw_max_ = ((received[2])) | ((received[3] & 0xf0) << 4); this->x_raw_max_ = ((received[2])) | ((received[3] & 0xf0) << 4);
}
if (this->y_raw_max_ == this->y_raw_min_) {
this->write(GET_Y_RES, 4); this->write(GET_Y_RES, 4);
if (this->read(received, 4)) { if (this->read(received, 4)) {
ESP_LOGE(TAG, "Failed to read Y resolution!"); ESP_LOGE(TAG, "Failed to read Y resolution!");
@ -51,7 +54,7 @@ void EKTF2232Touchscreen::setup() {
return; return;
} }
this->y_raw_max_ = ((received[2])) | ((received[3] & 0xf0) << 4); this->y_raw_max_ = ((received[2])) | ((received[3] & 0xf0) << 4);
}
this->set_power_state(true); this->set_power_state(true);
} }

View File

@ -66,8 +66,14 @@ class FT5x06Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice
return; return;
} }
// reading the chip registers to get max x/y does not seem to work. // reading the chip registers to get max x/y does not seem to work.
this->x_raw_max_ = this->display_->get_width(); if (this->display_ != nullptr) {
this->y_raw_max_ = this->display_->get_height(); if (this->x_raw_max_ == this->x_raw_min_) {
this->x_raw_max_ = this->display_->get_native_width();
}
if (this->y_raw_max_ == this->y_raw_min_) {
this->x_raw_max_ = this->display_->get_native_height();
}
}
esph_log_config(TAG, "FT5x06 Touchscreen setup complete"); esph_log_config(TAG, "FT5x06 Touchscreen setup complete");
} }

View File

@ -12,21 +12,23 @@
// Reference: https://focuslcds.com/content/FT6236.pdf // Reference: https://focuslcds.com/content/FT6236.pdf
namespace esphome { namespace esphome {
namespace ft63x6 { namespace ft63x6 {
static const uint8_t FT6X36_ADDR_DEVICE_MODE = 0x00;
static const uint8_t FT63X6_ADDR_TD_STATUS = 0x02;
static const uint8_t FT63X6_ADDR_TOUCH1_STATE = 0x03; static const uint8_t FT63X6_ADDR_TOUCH1_STATE = 0x03;
static const uint8_t FT63X6_ADDR_TOUCH1_X = 0x03; static const uint8_t FT63X6_ADDR_TOUCH1_X = 0x03;
static const uint8_t FT63X6_ADDR_TOUCH1_ID = 0x05; static const uint8_t FT63X6_ADDR_TOUCH1_ID = 0x05;
static const uint8_t FT63X6_ADDR_TOUCH1_Y = 0x05; static const uint8_t FT63X6_ADDR_TOUCH1_Y = 0x05;
static const uint8_t FT63X6_ADDR_TOUCH1_WEIGHT = 0x07;
static const uint8_t FT63X6_ADDR_TOUCH1_MISC = 0x08;
static const uint8_t FT6X36_ADDR_THRESHHOLD = 0x80;
static const uint8_t FT6X36_ADDR_TOUCHRATE_ACTIVE = 0x88;
static const uint8_t FT63X6_ADDR_CHIP_ID = 0xA3;
static const uint8_t FT63X6_ADDR_TOUCH2_STATE = 0x09; static const char *const TAG = "FT63X6";
static const uint8_t FT63X6_ADDR_TOUCH2_X = 0x09;
static const uint8_t FT63X6_ADDR_TOUCH2_ID = 0x0B;
static const uint8_t FT63X6_ADDR_TOUCH2_Y = 0x0B;
static const char *const TAG = "FT63X6Touchscreen";
void FT63X6Touchscreen::setup() { void FT63X6Touchscreen::setup() {
ESP_LOGCONFIG(TAG, "Setting up FT63X6Touchscreen Touchscreen..."); ESP_LOGCONFIG(TAG, "Setting up FT63X6 Touchscreen...");
if (this->interrupt_pin_ != nullptr) { if (this->interrupt_pin_ != nullptr) {
this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
this->interrupt_pin_->setup(); this->interrupt_pin_->setup();
@ -35,9 +37,8 @@ void FT63X6Touchscreen::setup() {
if (this->reset_pin_ != nullptr) { if (this->reset_pin_ != nullptr) {
this->reset_pin_->setup(); this->reset_pin_->setup();
}
this->hard_reset_(); this->hard_reset_();
}
// Get touch resolution // Get touch resolution
if (this->x_raw_max_ == this->x_raw_min_) { if (this->x_raw_max_ == this->x_raw_min_) {
@ -46,6 +47,15 @@ void FT63X6Touchscreen::setup() {
if (this->y_raw_max_ == this->y_raw_min_) { if (this->y_raw_max_ == this->y_raw_min_) {
this->y_raw_max_ = 480; this->y_raw_max_ = 480;
} }
uint8_t chip_id = this->read_byte_(FT63X6_ADDR_CHIP_ID);
if (chip_id != 0) {
ESP_LOGI(TAG, "FT6336U touch driver started chipid: %d", chip_id);
} else {
ESP_LOGE(TAG, "FT6336U touch driver failed to start");
}
this->write_byte(FT6X36_ADDR_DEVICE_MODE, 0x00);
this->write_byte(FT6X36_ADDR_THRESHHOLD, this->threshold_);
this->write_byte(FT6X36_ADDR_TOUCHRATE_ACTIVE, 0x0E);
} }
void FT63X6Touchscreen::hard_reset_() { void FT63X6Touchscreen::hard_reset_() {
@ -65,27 +75,60 @@ void FT63X6Touchscreen::dump_config() {
} }
void FT63X6Touchscreen::update_touches() { void FT63X6Touchscreen::update_touches() {
uint8_t data[15];
uint16_t touch_id, x, y; uint16_t touch_id, x, y;
if (!this->read_bytes(0x00, (uint8_t *) data, 15)) { uint8_t touches = this->read_touch_number_();
ESP_LOGE(TAG, "Failed to read touch data"); if ((touches == 0x00) || (touches == 0xff)) {
this->skip_update_ = true; // ESP_LOGD(TAG, "No touches detected");
return; return;
} }
if (((data[FT63X6_ADDR_TOUCH1_STATE] >> 6) & 0x01) == 0) { ESP_LOGV(TAG, "Touches found: %d", touches);
touch_id = data[FT63X6_ADDR_TOUCH1_ID] >> 4; // id1 = 0 or 1
x = encode_uint16(data[FT63X6_ADDR_TOUCH1_X] & 0x0F, data[FT63X6_ADDR_TOUCH1_X + 1]); for (auto point = 0; point < touches; point++) {
y = encode_uint16(data[FT63X6_ADDR_TOUCH1_Y] & 0x0F, data[FT63X6_ADDR_TOUCH1_Y + 1]); if (((this->read_touch_event_(point)) & 0x01) == 0) { // checking event flag bit 6 if it is null
this->add_raw_touch_position_(touch_id, x, y); touch_id = this->read_touch_id_(point); // id1 = 0 or 1
x = this->read_touch_x_(point);
y = this->read_touch_y_(point);
if ((x == 0) && (y == 0)) {
ESP_LOGW(TAG, "Reporting a (0,0) touch on %d", touch_id);
} }
if (((data[FT63X6_ADDR_TOUCH2_STATE] >> 6) & 0x01) == 0) { this->add_raw_touch_position_(touch_id, x, y, this->read_touch_weight_(point));
touch_id = data[FT63X6_ADDR_TOUCH2_ID] >> 4; // id1 = 0 or 1
x = encode_uint16(data[FT63X6_ADDR_TOUCH2_X] & 0x0F, data[FT63X6_ADDR_TOUCH2_X + 1]);
y = encode_uint16(data[FT63X6_ADDR_TOUCH2_Y] & 0x0F, data[FT63X6_ADDR_TOUCH2_Y + 1]);
this->add_raw_touch_position_(touch_id, x, y);
} }
}
}
uint8_t FT63X6Touchscreen::read_touch_number_() { return this->read_byte_(FT63X6_ADDR_TD_STATUS) & 0x0F; }
// Touch 1 functions
uint16_t FT63X6Touchscreen::read_touch_x_(uint8_t touch) {
uint8_t read_buf[2];
read_buf[0] = this->read_byte_(FT63X6_ADDR_TOUCH1_X + (touch * 6));
read_buf[1] = this->read_byte_(FT63X6_ADDR_TOUCH1_X + 1 + (touch * 6));
return ((read_buf[0] & 0x0f) << 8) | read_buf[1];
}
uint16_t FT63X6Touchscreen::read_touch_y_(uint8_t touch) {
uint8_t read_buf[2];
read_buf[0] = this->read_byte_(FT63X6_ADDR_TOUCH1_Y + (touch * 6));
read_buf[1] = this->read_byte_(FT63X6_ADDR_TOUCH1_Y + 1 + (touch * 6));
return ((read_buf[0] & 0x0f) << 8) | read_buf[1];
}
uint8_t FT63X6Touchscreen::read_touch_event_(uint8_t touch) {
return this->read_byte_(FT63X6_ADDR_TOUCH1_X + (touch * 6)) >> 6;
}
uint8_t FT63X6Touchscreen::read_touch_id_(uint8_t touch) {
return this->read_byte_(FT63X6_ADDR_TOUCH1_ID + (touch * 6)) >> 4;
}
uint8_t FT63X6Touchscreen::read_touch_weight_(uint8_t touch) {
return this->read_byte_(FT63X6_ADDR_TOUCH1_WEIGHT + (touch * 6));
}
uint8_t FT63X6Touchscreen::read_touch_misc_(uint8_t touch) {
return this->read_byte_(FT63X6_ADDR_TOUCH1_MISC + (touch * 6)) >> 4;
}
uint8_t FT63X6Touchscreen::read_byte_(uint8_t addr) {
uint8_t byte = 0;
this->read_byte(addr, &byte);
return byte;
} }
} // namespace ft63x6 } // namespace ft63x6

View File

@ -16,6 +16,8 @@ namespace ft63x6 {
using namespace touchscreen; using namespace touchscreen;
static const uint8_t FT6X36_DEFAULT_THRESHOLD = 22;
class FT63X6Touchscreen : public Touchscreen, public i2c::I2CDevice { class FT63X6Touchscreen : public Touchscreen, public i2c::I2CDevice {
public: public:
void setup() override; void setup() override;
@ -23,18 +25,26 @@ class FT63X6Touchscreen : public Touchscreen, public i2c::I2CDevice {
void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; } void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; }
void set_reset_pin(GPIOPin *pin) { this->reset_pin_ = pin; } void set_reset_pin(GPIOPin *pin) { this->reset_pin_ = pin; }
void set_threshold(uint8_t threshold) { this->threshold_ = threshold; }
protected: protected:
void hard_reset_(); void hard_reset_();
uint8_t read_byte_(uint8_t addr);
void update_touches() override; void update_touches() override;
InternalGPIOPin *interrupt_pin_{nullptr}; InternalGPIOPin *interrupt_pin_{nullptr};
GPIOPin *reset_pin_{nullptr}; GPIOPin *reset_pin_{nullptr};
uint8_t threshold_{FT6X36_DEFAULT_THRESHOLD};
uint8_t read_touch_count_(); uint8_t read_touch_number_();
uint16_t read_touch_coordinate_(uint8_t coordinate);
uint8_t read_touch_id_(uint8_t id_address); uint16_t read_touch_x_(uint8_t touch);
uint16_t read_touch_y_(uint8_t touch);
uint8_t read_touch_event_(uint8_t touch);
uint8_t read_touch_id_(uint8_t touch);
uint8_t read_touch_weight_(uint8_t touch);
uint8_t read_touch_misc_(uint8_t touch);
uint8_t read_byte_(uint8_t addr);
}; };
} // namespace ft63x6 } // namespace ft63x6

View File

@ -3,7 +3,7 @@ import esphome.config_validation as cv
from esphome import pins from esphome import pins
from esphome.components import i2c, touchscreen from esphome.components import i2c, touchscreen
from esphome.const import CONF_ID, CONF_INTERRUPT_PIN, CONF_RESET_PIN from esphome.const import CONF_ID, CONF_INTERRUPT_PIN, CONF_RESET_PIN, CONF_THRESHOLD
CODEOWNERS = ["@gpambrozio"] CODEOWNERS = ["@gpambrozio"]
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]
@ -26,6 +26,7 @@ CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend(
pins.internal_gpio_input_pin_schema pins.internal_gpio_input_pin_schema
), ),
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_THRESHOLD): cv.uint8_t,
} }
).extend(i2c.i2c_device_schema(0x38)) ).extend(i2c.i2c_device_schema(0x38))
) )

View File

@ -48,9 +48,13 @@ void GT911Touchscreen::setup() {
if (err == i2c::ERROR_OK) { if (err == i2c::ERROR_OK) {
err = this->read(data, sizeof(data)); err = this->read(data, sizeof(data));
if (err == i2c::ERROR_OK) { if (err == i2c::ERROR_OK) {
if (this->x_raw_max_ == this->x_raw_min_) {
this->x_raw_max_ = encode_uint16(data[1], data[0]); this->x_raw_max_ = encode_uint16(data[1], data[0]);
}
if (this->y_raw_max_ == this->y_raw_min_) {
this->y_raw_max_ = encode_uint16(data[3], data[2]); this->y_raw_max_ = encode_uint16(data[3], data[2]);
esph_log_d(TAG, "Read max_x/max_y %d/%d", this->x_raw_max_, this->y_raw_max_); }
esph_log_d(TAG, "calibration max_x/max_y %d/%d", this->x_raw_max_, this->y_raw_max_);
} }
} }
} }

View File

@ -38,9 +38,14 @@ void LilygoT547Touchscreen::setup() {
} }
this->write_register(POWER_REGISTER, WAKEUP_CMD, 1); this->write_register(POWER_REGISTER, WAKEUP_CMD, 1);
if (this->display_ != nullptr) {
this->x_raw_max_ = this->get_width_(); if (this->x_raw_max_ == this->x_raw_min_) {
this->y_raw_max_ = this->get_height_(); this->x_raw_max_ = this->display_->get_native_width();
}
if (this->y_raw_max_ == this->y_raw_min_) {
this->x_raw_max_ = this->display_->get_native_height();
}
}
} }
void LilygoT547Touchscreen::update_touches() { void LilygoT547Touchscreen::update_touches() {

View File

@ -3,14 +3,17 @@ import esphome.codegen as cg
from esphome.components import display from esphome.components import display
from esphome import automation from esphome import automation
from esphome.const import ( from esphome.const import (
CONF_ON_TOUCH, CONF_ON_TOUCH,
CONF_ON_RELEASE, CONF_ON_RELEASE,
CONF_SWAP_XY,
CONF_MIRROR_X, CONF_MIRROR_X,
CONF_MIRROR_Y, CONF_MIRROR_Y,
CONF_SWAP_XY,
CONF_TRANSFORM, CONF_TRANSFORM,
CONF_CALIBRATION,
) )
from esphome.core import coroutine_with_priority from esphome.core import coroutine_with_priority
CODEOWNERS = ["@jesserockz", "@nielsnl68"] CODEOWNERS = ["@jesserockz", "@nielsnl68"]
@ -34,6 +37,56 @@ CONF_ON_UPDATE = "on_update"
CONF_TOUCH_TIMEOUT = "touch_timeout" CONF_TOUCH_TIMEOUT = "touch_timeout"
CONF_X_MIN = "x_min"
CONF_X_MAX = "x_max"
CONF_Y_MIN = "y_min"
CONF_Y_MAX = "y_max"
def validate_calibration(config):
if CONF_CALIBRATION in config:
calibration_config = config[CONF_CALIBRATION]
if (
cv.int_([CONF_X_MIN]) != 0
and cv.int_(calibration_config[CONF_X_MAX]) != 0
and abs(
cv.int_(calibration_config[CONF_X_MIN])
- cv.int_(calibration_config[CONF_X_MAX])
)
< 10
):
raise cv.Invalid("Calibration X values difference must be more than 10")
if (
cv.int_(calibration_config[CONF_Y_MIN]) != 0
and cv.int_(calibration_config[CONF_Y_MAX]) != 0
and abs(
cv.int_(calibration_config[CONF_Y_MIN])
- cv.int_(calibration_config[CONF_Y_MAX])
)
< 10
):
raise cv.Invalid("Calibration Y values difference must be more than 10")
return config
def calibration_schema(default_max_values):
return cv.Schema(
{
cv.Optional(CONF_X_MIN, default=0): cv.int_range(min=0, max=4095),
cv.Optional(CONF_X_MAX, default=default_max_values): cv.int_range(
min=0, max=4095
),
cv.Optional(CONF_Y_MIN, default=0): cv.int_range(min=0, max=4095),
cv.Optional(CONF_Y_MAX, default=default_max_values): cv.int_range(
min=0, max=4095
),
},
validate_calibration,
)
def touchscreen_schema(default_touch_timeout): def touchscreen_schema(default_touch_timeout):
return cv.Schema( return cv.Schema(
{ {
@ -49,6 +102,7 @@ def touchscreen_schema(default_touch_timeout):
cv.positive_time_period_milliseconds, cv.positive_time_period_milliseconds,
cv.Range(max=cv.TimePeriod(milliseconds=65535)), cv.Range(max=cv.TimePeriod(milliseconds=65535)),
), ),
cv.Optional(CONF_CALIBRATION): calibration_schema(0),
cv.Optional(CONF_ON_TOUCH): automation.validate_automation(single=True), cv.Optional(CONF_ON_TOUCH): automation.validate_automation(single=True),
cv.Optional(CONF_ON_UPDATE): automation.validate_automation(single=True), cv.Optional(CONF_ON_UPDATE): automation.validate_automation(single=True),
cv.Optional(CONF_ON_RELEASE): automation.validate_automation(single=True), cv.Optional(CONF_ON_RELEASE): automation.validate_automation(single=True),
@ -74,6 +128,17 @@ async def register_touchscreen(var, config):
cg.add(var.set_mirror_x(transform[CONF_MIRROR_X])) cg.add(var.set_mirror_x(transform[CONF_MIRROR_X]))
cg.add(var.set_mirror_y(transform[CONF_MIRROR_Y])) cg.add(var.set_mirror_y(transform[CONF_MIRROR_Y]))
if CONF_CALIBRATION in config:
calibration_config = config[CONF_CALIBRATION]
cg.add(
var.set_calibration(
calibration_config[CONF_X_MIN],
calibration_config[CONF_X_MAX],
calibration_config[CONF_Y_MIN],
calibration_config[CONF_Y_MAX],
)
)
if CONF_ON_TOUCH in config: if CONF_ON_TOUCH in config:
await automation.build_automation( await automation.build_automation(
var.get_touch_trigger(), var.get_touch_trigger(),

View File

@ -13,6 +13,15 @@ void Touchscreen::attach_interrupt_(InternalGPIOPin *irq_pin, esphome::gpio::Int
irq_pin->attach_interrupt(TouchscreenInterrupt::gpio_intr, &this->store_, type); irq_pin->attach_interrupt(TouchscreenInterrupt::gpio_intr, &this->store_, type);
this->store_.init = true; this->store_.init = true;
this->store_.touched = false; this->store_.touched = false;
ESP_LOGD(TAG, "Attach Touch Interupt");
}
void Touchscreen::call_setup() {
if (this->display_ != nullptr) {
this->display_width_ = this->display_->get_native_width();
this->display_height_ = this->display_->get_native_height();
}
PollingComponent::call_setup();
} }
void Touchscreen::update() { void Touchscreen::update() {
@ -20,19 +29,22 @@ void Touchscreen::update() {
this->store_.touched = true; this->store_.touched = true;
} else { } else {
// no need to poll if we have interrupts. // no need to poll if we have interrupts.
ESP_LOGW(TAG, "Touch Polling Stopped. You can safely remove the 'update_interval:' variable from the YAML file.");
this->stop_poller(); this->stop_poller();
} }
} }
void Touchscreen::loop() { void Touchscreen::loop() {
if (this->store_.touched) { if (this->store_.touched) {
ESP_LOGVV(TAG, "<< Do Touch loop >>");
this->first_touch_ = this->touches_.empty(); this->first_touch_ = this->touches_.empty();
this->need_update_ = false; this->need_update_ = false;
this->was_touched_ = this->is_touched_;
this->is_touched_ = false; this->is_touched_ = false;
this->skip_update_ = false; this->skip_update_ = false;
for (auto &tp : this->touches_) { for (auto &tp : this->touches_) {
if (tp.second.state == STATE_PRESSED || tp.second.state == STATE_UPDATED) { if (tp.second.state == STATE_PRESSED || tp.second.state == STATE_UPDATED) {
tp.second.state = tp.second.state | STATE_RELEASING; tp.second.state |= STATE_RELEASING;
} else { } else {
tp.second.state = STATE_RELEASED; tp.second.state = STATE_RELEASED;
} }
@ -42,7 +54,7 @@ void Touchscreen::loop() {
this->update_touches(); this->update_touches();
if (this->skip_update_) { if (this->skip_update_) {
for (auto &tp : this->touches_) { for (auto &tp : this->touches_) {
tp.second.state = tp.second.state & -STATE_RELEASING; tp.second.state &= ~STATE_RELEASING;
} }
} else { } else {
this->store_.touched = false; this->store_.touched = false;
@ -65,11 +77,13 @@ void Touchscreen::add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_r
} else { } else {
tp = this->touches_[id]; tp = this->touches_[id];
tp.state = STATE_UPDATED; tp.state = STATE_UPDATED;
tp.y_prev = tp.y;
tp.x_prev = tp.x;
} }
tp.x_raw = x_raw; tp.x_raw = x_raw;
tp.y_raw = y_raw; tp.y_raw = y_raw;
tp.z_raw = z_raw; tp.z_raw = z_raw;
if (this->x_raw_max_ != this->x_raw_min_ and this->y_raw_max_ != this->y_raw_min_) {
x = this->normalize_(x_raw, this->x_raw_min_, this->x_raw_max_, this->invert_x_); x = this->normalize_(x_raw, this->x_raw_min_, this->x_raw_max_, this->invert_x_);
y = this->normalize_(y_raw, this->y_raw_min_, this->y_raw_max_, this->invert_y_); y = this->normalize_(y_raw, this->y_raw_min_, this->y_raw_max_, this->invert_y_);
@ -77,9 +91,11 @@ void Touchscreen::add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_r
std::swap(x, y); std::swap(x, y);
} }
tp.x = (uint16_t) ((int) x * this->get_width_() / 0x1000); tp.x = (uint16_t) ((int) x * this->display_width_ / 0x1000);
tp.y = (uint16_t) ((int) y * this->get_height_() / 0x1000); tp.y = (uint16_t) ((int) y * this->display_height_ / 0x1000);
} else {
tp.state |= STATE_CALIBRATE;
}
if (tp.state == STATE_PRESSED) { if (tp.state == STATE_PRESSED) {
tp.x_org = tp.x; tp.x_org = tp.x;
tp.y_org = tp.y; tp.y_org = tp.y;
@ -94,7 +110,20 @@ void Touchscreen::add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_r
} }
void Touchscreen::send_touches_() { void Touchscreen::send_touches_() {
TouchPoints_t touches;
for (auto tp : this->touches_) {
ESP_LOGV(TAG, "Touch status: %d/%d: raw:(%4d,%4d,%4d) calc:(%3d,%4d)", tp.second.id, tp.second.state,
tp.second.x_raw, tp.second.y_raw, tp.second.z_raw, tp.second.x, tp.second.y);
touches.push_back(tp.second);
}
if (this->need_update_ || (!this->is_touched_ && this->was_touched_)) {
this->update_trigger_.trigger(touches);
for (auto *listener : this->touch_listeners_) {
listener->update(touches);
}
}
if (!this->is_touched_) { if (!this->is_touched_) {
if (this->was_touched_) {
if (this->touch_timeout_ > 0) { if (this->touch_timeout_ > 0) {
this->cancel_timeout(TAG); this->cancel_timeout(TAG);
} }
@ -102,11 +131,9 @@ void Touchscreen::send_touches_() {
for (auto *listener : this->touch_listeners_) for (auto *listener : this->touch_listeners_)
listener->release(); listener->release();
this->touches_.clear(); this->touches_.clear();
} else { this->was_touched_ = false;
TouchPoints_t touches;
for (auto tp : this->touches_) {
touches.push_back(tp.second);
} }
} else {
if (this->first_touch_) { if (this->first_touch_) {
TouchPoint tp = this->touches_.begin()->second; TouchPoint tp = this->touches_.begin()->second;
this->touch_trigger_.trigger(tp, touches); this->touch_trigger_.trigger(tp, touches);
@ -114,12 +141,6 @@ void Touchscreen::send_touches_() {
listener->touch(tp); listener->touch(tp);
} }
} }
if (this->need_update_) {
this->update_trigger_.trigger(touches);
for (auto *listener : this->touch_listeners_) {
listener->update(touches);
}
}
} }
} }

View File

@ -16,6 +16,7 @@ static const uint8_t STATE_RELEASED = 0x00;
static const uint8_t STATE_PRESSED = 0x01; static const uint8_t STATE_PRESSED = 0x01;
static const uint8_t STATE_UPDATED = 0x02; static const uint8_t STATE_UPDATED = 0x02;
static const uint8_t STATE_RELEASING = 0x04; static const uint8_t STATE_RELEASING = 0x04;
static const uint8_t STATE_CALIBRATE = 0x07;
struct TouchPoint { struct TouchPoint {
uint8_t id; uint8_t id;
@ -68,8 +69,6 @@ class Touchscreen : public PollingComponent {
void register_listener(TouchListener *listener) { this->touch_listeners_.push_back(listener); } void register_listener(TouchListener *listener) { this->touch_listeners_.push_back(listener); }
virtual void update_touches() = 0;
optional<TouchPoint> get_touch() { return this->touches_.begin()->second; } optional<TouchPoint> get_touch() { return this->touches_.begin()->second; }
TouchPoints_t get_touches() { TouchPoints_t get_touches() {
@ -82,6 +81,7 @@ class Touchscreen : public PollingComponent {
void update() override; void update() override;
void loop() override; void loop() override;
void call_setup() override;
protected: protected:
/// Call this function to send touch points to the `on_touch` listener and the binary_sensors. /// Call this function to send touch points to the `on_touch` listener and the binary_sensors.
@ -90,17 +90,17 @@ class Touchscreen : public PollingComponent {
void add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw = 0); void add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw = 0);
virtual void update_touches() = 0;
void send_touches_(); void send_touches_();
int16_t normalize_(int16_t val, int16_t min_val, int16_t max_val, bool inverted = false); int16_t normalize_(int16_t val, int16_t min_val, int16_t max_val, bool inverted = false);
uint16_t get_width_() { return this->display_->get_width(); }
uint16_t get_height_() { return this->display_->get_height(); }
display::Display *display_{nullptr}; display::Display *display_{nullptr};
int16_t x_raw_min_{0}, x_raw_max_{0}, y_raw_min_{0}, y_raw_max_{0}; int16_t x_raw_min_{0}, x_raw_max_{0}, y_raw_min_{0}, y_raw_max_{0};
int16_t display_width_{0}, display_height_{0};
uint16_t touch_timeout_{0}; uint16_t touch_timeout_{0};
bool invert_x_{false}, invert_y_{false}, swap_x_y_{false}; bool invert_x_{false}, invert_y_{false}, swap_x_y_{false};
@ -115,6 +115,7 @@ class Touchscreen : public PollingComponent {
bool first_touch_{true}; bool first_touch_{true};
bool need_update_{false}; bool need_update_{false};
bool is_touched_{false}; bool is_touched_{false};
bool was_touched_{false};
bool skip_update_{false}; bool skip_update_{false};
}; };

View File

@ -20,7 +20,7 @@ CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend(
cv.Schema( cv.Schema(
{ {
cv.GenerateID(): cv.declare_id(TT21100Touchscreen), cv.GenerateID(): cv.declare_id(TT21100Touchscreen),
cv.Required(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_schema, cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_schema,
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
} }
).extend(i2c.i2c_device_schema(0x24)) ).extend(i2c.i2c_device_schema(0x24))
@ -32,6 +32,7 @@ async def to_code(config):
await touchscreen.register_touchscreen(var, config) await touchscreen.register_touchscreen(var, config)
await i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)
if CONF_INTERRUPT_PIN in config:
interrupt_pin = await cg.gpio_pin_expression(config[CONF_INTERRUPT_PIN]) interrupt_pin = await cg.gpio_pin_expression(config[CONF_INTERRUPT_PIN])
cg.add(var.set_interrupt_pin(interrupt_pin)) cg.add(var.set_interrupt_pin(interrupt_pin))

View File

@ -50,10 +50,11 @@ void TT21100Touchscreen::setup() {
ESP_LOGCONFIG(TAG, "Setting up TT21100 Touchscreen..."); ESP_LOGCONFIG(TAG, "Setting up TT21100 Touchscreen...");
// Register interrupt pin // Register interrupt pin
if (this->interrupt_pin_ != nullptr) {
this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
this->interrupt_pin_->setup(); this->interrupt_pin_->setup();
this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE); this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE);
}
// Perform reset if necessary // Perform reset if necessary
if (this->reset_pin_ != nullptr) { if (this->reset_pin_ != nullptr) {
@ -62,8 +63,14 @@ void TT21100Touchscreen::setup() {
} }
// Update display dimensions if they were updated during display setup // Update display dimensions if they were updated during display setup
this->x_raw_max_ = this->get_width_(); if (this->display_ != nullptr) {
this->y_raw_max_ = this->get_height_(); if (this->x_raw_max_ == this->x_raw_min_) {
this->x_raw_max_ = this->display_->get_native_width();
}
if (this->y_raw_max_ == this->y_raw_min_) {
this->x_raw_max_ = this->display_->get_native_height();
}
}
// Trigger initial read to activate the interrupt // Trigger initial read to activate the interrupt
this->store_.touched = true; this->store_.touched = true;

View File

@ -15,35 +15,11 @@ XPT2046Component = XPT2046_ns.class_(
spi.SPIDevice, spi.SPIDevice,
) )
CONF_CALIBRATION_X_MIN = "calibration_x_min" CONF_CALIBRATION_X_MIN = "calibration_x_min"
CONF_CALIBRATION_X_MAX = "calibration_x_max" CONF_CALIBRATION_X_MAX = "calibration_x_max"
CONF_CALIBRATION_Y_MIN = "calibration_y_min" CONF_CALIBRATION_Y_MIN = "calibration_y_min"
CONF_CALIBRATION_Y_MAX = "calibration_y_max" CONF_CALIBRATION_Y_MAX = "calibration_y_max"
def validate_xpt2046(config):
if (
abs(
cv.int_(config[CONF_CALIBRATION_X_MAX])
- cv.int_(config[CONF_CALIBRATION_X_MIN])
)
< 1000
):
raise cv.Invalid("Calibration X values difference < 1000")
if (
abs(
cv.int_(config[CONF_CALIBRATION_Y_MAX])
- cv.int_(config[CONF_CALIBRATION_Y_MIN])
)
< 1000
):
raise cv.Invalid("Calibration Y values difference < 1000")
return config
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
touchscreen.TOUCHSCREEN_SCHEMA.extend( touchscreen.TOUCHSCREEN_SCHEMA.extend(
cv.Schema( cv.Schema(
@ -52,42 +28,41 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_INTERRUPT_PIN): cv.All( cv.Optional(CONF_INTERRUPT_PIN): cv.All(
pins.internal_gpio_input_pin_schema pins.internal_gpio_input_pin_schema
), ),
cv.Optional(CONF_CALIBRATION_X_MIN, default=0): cv.int_range(
min=0, max=4095
),
cv.Optional(CONF_CALIBRATION_X_MAX, default=4095): cv.int_range(
min=0, max=4095
),
cv.Optional(CONF_CALIBRATION_Y_MIN, default=0): cv.int_range(
min=0, max=4095
),
cv.Optional(CONF_CALIBRATION_Y_MAX, default=4095): cv.int_range(
min=0, max=4095
),
cv.Optional(CONF_THRESHOLD, default=400): cv.int_range(min=0, max=4095), cv.Optional(CONF_THRESHOLD, default=400): cv.int_range(min=0, max=4095),
cv.Optional(
touchscreen.CONF_CALIBRATION
): touchscreen.calibration_schema(4095),
cv.Optional(CONF_CALIBRATION_X_MIN): cv.invalid(
"Deprecated: use the new 'calibration' configuration variable"
),
cv.Optional(CONF_CALIBRATION_X_MAX): cv.invalid(
"Deprecated: use the new 'calibration' configuration variable"
),
cv.Optional(CONF_CALIBRATION_Y_MIN): cv.invalid(
"Deprecated: use the new 'calibration' configuration variable"
),
cv.Optional(CONF_CALIBRATION_Y_MAX): cv.invalid(
"Deprecated: use the new 'calibration' configuration variable"
),
cv.Optional(CONF_CALIBRATION_Y_MAX): cv.invalid(
"Deprecated: use the new 'calibration' configuration variable"
),
cv.Optional("report_interval"): cv.invalid(
"Deprecated: use the 'update_interval' configuration variable"
),
}, },
) )
).extend(spi.spi_device_schema()), ).extend(spi.spi_device_schema()),
validate_xpt2046,
) )
async def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
await touchscreen.register_touchscreen(var, config)
await spi.register_spi_device(var, config) await spi.register_spi_device(var, config)
await touchscreen.register_touchscreen(var, config)
cg.add(var.set_threshold(config[CONF_THRESHOLD])) cg.add(var.set_threshold(config[CONF_THRESHOLD]))
cg.add(
var.set_calibration(
config[CONF_CALIBRATION_X_MIN],
config[CONF_CALIBRATION_X_MAX],
config[CONF_CALIBRATION_Y_MIN],
config[CONF_CALIBRATION_Y_MAX],
)
)
if CONF_INTERRUPT_PIN in config: if CONF_INTERRUPT_PIN in config:
pin = await cg.gpio_pin_expression(config[CONF_INTERRUPT_PIN]) pin = await cg.gpio_pin_expression(config[CONF_INTERRUPT_PIN])
cg.add(var.set_irq_pin(pin)) cg.add(var.set_irq_pin(pin))

View File

@ -32,9 +32,8 @@ void XPT2046Component::update_touches() {
int16_t touch_pressure_1 = this->read_adc_(0xB1 /* touch_pressure_1 */); int16_t touch_pressure_1 = this->read_adc_(0xB1 /* touch_pressure_1 */);
int16_t touch_pressure_2 = this->read_adc_(0xC1 /* touch_pressure_2 */); int16_t touch_pressure_2 = this->read_adc_(0xC1 /* touch_pressure_2 */);
ESP_LOGVV(TAG, "touch_pressure %d, %d", touch_pressure_1, touch_pressure_2);
z_raw = touch_pressure_1 + 0Xfff - touch_pressure_2; z_raw = touch_pressure_1 + 0Xfff - touch_pressure_2;
ESP_LOGVV(TAG, "Touchscreen Update z = %d", z_raw);
touch = (z_raw >= this->threshold_); touch = (z_raw >= this->threshold_);
if (touch) { if (touch) {
read_adc_(0xD1 /* X */); // dummy Y measure, 1st is always noisy read_adc_(0xD1 /* X */); // dummy Y measure, 1st is always noisy
@ -53,7 +52,7 @@ void XPT2046Component::update_touches() {
x_raw = best_two_avg(data[1], data[3], data[5]); x_raw = best_two_avg(data[1], data[3], data[5]);
y_raw = best_two_avg(data[0], data[2], data[4]); y_raw = best_two_avg(data[0], data[2], data[4]);
ESP_LOGV(TAG, "Touchscreen Update [%d, %d], z = %d", x_raw, y_raw, z_raw); ESP_LOGD(TAG, "Touchscreen Update [%d, %d], z = %d", x_raw, y_raw, z_raw);
this->add_raw_touch_position_(0, x_raw, y_raw, z_raw); this->add_raw_touch_position_(0, x_raw, y_raw, z_raw);
} }
@ -77,7 +76,7 @@ void XPT2046Component::dump_config() {
LOG_UPDATE_INTERVAL(this); LOG_UPDATE_INTERVAL(this);
} }
float XPT2046Component::get_setup_priority() const { return setup_priority::DATA; } // float XPT2046Component::get_setup_priority() const { return setup_priority::DATA; }
int16_t XPT2046Component::best_two_avg(int16_t value1, int16_t value2, int16_t value3) { int16_t XPT2046Component::best_two_avg(int16_t value1, int16_t value2, int16_t value3) {
int16_t delta_a, delta_b, delta_c; int16_t delta_a, delta_b, delta_c;

View File

@ -23,7 +23,7 @@ class XPT2046Component : public Touchscreen,
void setup() override; void setup() override;
void dump_config() override; void dump_config() override;
float get_setup_priority() const override; // float get_setup_priority() const override;
protected: protected:
static int16_t best_two_avg(int16_t value1, int16_t value2, int16_t value3); static int16_t best_two_avg(int16_t value1, int16_t value2, int16_t value3);

View File

@ -0,0 +1,38 @@
spi:
clk_pin: 14
mosi_pin: 13
i2c:
sda: GPIO18
scl: GPIO19
display:
- id: my_display
platform: ili9xxx
dimensions: 480x320
model: ST7796
cs_pin: 15
dc_pin: 21
reset_pin: 22
transform:
swap_xy: true
mirror_x: true
mirror_y: true
auto_clear_enabled: false
touchscreen:
- platform: ft63x6
interrupt_pin: GPIO39
transform:
swap_xy: true
mirror_x: false
mirror_y: true
on_touch:
- logger.log:
format: tp touched
on_update:
- logger.log:
format: to updated
on_release:
- logger.log:
format: to released

View File

@ -0,0 +1,43 @@
i2c:
sda: GPIO8
scl: GPIO18
spi:
clk_pin: 7
mosi_pin: 11
miso_pin: 9
display:
- platform: ili9xxx
id: my_display
model: ili9341
cs_pin: 5
dc_pin: 12
reset_pin: 33
auto_clear_enabled: false
data_rate: 40MHz
dimensions: 320x240
update_interval: never
transform:
mirror_y: false
mirror_x: false
swap_xy: true
touchscreen:
- platform: tt21100
address: 0x24
interrupt_pin: GPIO3
on_touch:
- logger.log: "Touchscreen:: Touched"
binary_sensor:
- platform: tt21100
index: 0
name: "Home"
- platform: touchscreen
name: FanLo
x_min: 0
x_max: 105
y_min: 0
y_max: 80

View File

@ -0,0 +1,37 @@
spi:
clk_pin: 7
mosi_pin: 11
miso_pin: 9
display:
- platform: ili9xxx
id: my_display
model: ili9341
cs_pin: 5
dc_pin: 12
reset_pin: 33
auto_clear_enabled: false
data_rate: 40MHz
dimensions: 320x240
update_interval: never
transform:
mirror_y: false
mirror_x: false
swap_xy: true
touchscreen:
- platform: xpt2046
display: my_display
id: my_toucher
update_interval: 50ms
cs_pin: 18
threshold: 300
calibration:
x_min: 210
x_max: 3890
y_min: 170
y_max: 3730
transform:
mirror_x: false
mirror_y: true
swap_xy: true

View File

@ -976,10 +976,11 @@ touchscreen:
display: inkplate_display display: inkplate_display
update_interval: 50ms update_interval: 50ms
threshold: 400 threshold: 400
calibration_x_min: 3860 calibration:
calibration_x_max: 280 x_min: 3860
calibration_y_min: 340 x_max: 280
calibration_y_max: 3860 y_min: 340
y_max: 3860
on_touch: on_touch:
- logger.log: - logger.log:
format: Touch at (%d, %d) format: Touch at (%d, %d)