ESPHome 2026.5.0b1
Loading...
Searching...
No Matches
touchscreen.cpp
Go to the documentation of this file.
1#include "touchscreen.h"
2
3#include "esphome/core/log.h"
4
5namespace esphome::touchscreen {
6
7static const char *const TAG = "touchscreen";
8
10
13 this->store_.init = true;
14 this->store_.touched = false;
15 ESP_LOGD(TAG, "Attach Touch Interupt");
16}
17
19 if (this->display_ != nullptr) {
20 this->display_width_ = this->display_->get_width();
21 this->display_height_ = this->display_->get_height();
22 }
24}
25
27 if (!this->store_.init) {
28 this->store_.touched = true;
29 } else {
30 // no need to poll if we have interrupts.
31 ESP_LOGW(TAG, "Touch Polling Stopped. You can safely remove the 'update_interval:' variable from the YAML file.");
32 this->stop_poller();
33 }
34}
35
37 if (this->store_.touched) {
38 ESP_LOGVV(TAG, "<< Do Touch loop >>");
39 this->first_touch_ = this->touches_.empty();
40 this->need_update_ = false;
41 this->is_touched_ = false;
42 this->skip_update_ = false;
43 for (auto &tp : this->touches_) {
44 if (tp.second.state == STATE_PRESSED || tp.second.state == STATE_UPDATED) {
45 tp.second.state |= STATE_RELEASING;
46 } else {
47 tp.second.state = STATE_RELEASED;
48 }
49 tp.second.x_prev = tp.second.x;
50 tp.second.y_prev = tp.second.y;
51 }
52 // The interrupt flag must be reset BEFORE calling update_touches, otherwise we might miss an interrupt that was
53 // triggered while we were reading touch data.
54 this->store_.touched = false;
55 this->update_touches();
56 if (this->skip_update_) {
57 for (auto &tp : this->touches_) {
58 tp.second.state &= ~STATE_RELEASING;
59 }
60 } else {
61 this->defer([this]() { this->send_touches_(); });
62 if (this->touch_timeout_ > 0) {
63 // Simulate a touch after <this->touch_timeout_> ms. This will reset any existing timeout operation.
64 // This is to detect touch release.
65 if (this->is_touched_) {
66 this->set_timeout(TAG, this->touch_timeout_, [this]() { this->store_.touched = true; });
67 } else {
68 this->cancel_timeout(TAG);
69 }
70 }
71 }
72 }
73}
74
75void Touchscreen::add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw) {
76 TouchPoint tp;
77 uint16_t x, y;
78 if (this->swap_x_y_) {
79 std::swap(x_raw, y_raw);
80 }
81 if (this->touches_.count(id) == 0) {
82 tp.state = STATE_PRESSED;
83 tp.id = id;
84 } else {
85 tp = this->touches_[id];
86 tp.state = STATE_UPDATED;
87 tp.y_prev = tp.y;
88 tp.x_prev = tp.x;
89 }
90 tp.x_raw = x_raw;
91 tp.y_raw = y_raw;
92 tp.z_raw = z_raw;
93 if (this->x_raw_max_ != this->x_raw_min_ and this->y_raw_max_ != this->y_raw_min_) {
94 x = this->normalize_(x_raw, this->x_raw_min_, this->x_raw_max_, this->invert_x_);
95 y = this->normalize_(y_raw, this->y_raw_min_, this->y_raw_max_, this->invert_y_);
96
97 tp.x = (uint16_t) ((int) x * this->display_width_ / 0x1000);
98 tp.y = (uint16_t) ((int) y * this->display_height_ / 0x1000);
99 } else {
100 tp.state |= STATE_CALIBRATE;
101 }
102 if (tp.state == STATE_PRESSED) {
103 tp.x_org = tp.x;
104 tp.y_org = tp.y;
105 }
106
107 this->touches_[id] = tp;
108
109 this->is_touched_ = true;
110 if ((tp.x != tp.x_prev) || (tp.y != tp.y_prev)) {
111 this->need_update_ = true;
112 }
113}
114
116 TouchPoints_t touches;
117 ESP_LOGV(TAG, "Touch status: is_touched=%d, was_touched=%d", this->is_touched_, this->was_touched_);
118 for (auto tp : this->touches_) {
119 ESP_LOGV(TAG, "Touch status: %d/%d: raw:(%4d,%4d,%4d) calc:(%3d,%4d)", tp.second.id, tp.second.state,
120 tp.second.x_raw, tp.second.y_raw, tp.second.z_raw, tp.second.x, tp.second.y);
121 touches.push_back(tp.second);
122 }
123 if (this->need_update_ || (!this->is_touched_ && this->was_touched_)) {
124 this->update_trigger_.trigger(touches);
125 for (auto *listener : this->touch_listeners_) {
126 listener->update(touches);
127 }
128 }
129 if (!this->is_touched_) {
130 if (this->was_touched_) {
132 for (auto *listener : this->touch_listeners_)
133 listener->release();
134 this->touches_.clear();
135 }
136 } else {
137 if (this->first_touch_) {
138 TouchPoint tp = this->touches_.begin()->second;
139 this->touch_trigger_.trigger(tp, touches);
140 for (auto *listener : this->touch_listeners_) {
141 listener->touch(tp);
142 }
143 }
144 }
145 this->was_touched_ = this->is_touched_;
146}
147
148int16_t Touchscreen::normalize_(int16_t val, int16_t min_val, int16_t max_val, bool inverted) {
149 int16_t ret;
150
151 if (val <= min_val) {
152 ret = 0;
153 } else if (val >= max_val) {
154 ret = 0xfff;
155 } else {
156 ret = (int16_t) ((int) 0xfff * (val - min_val) / (max_val - min_val));
157 }
158
159 ret = (inverted) ? 0xfff - ret : ret;
160
161 return ret;
162}
163
164} // namespace esphome::touchscreen
ESPDEPRECATED("Use const char* overload instead. Removed in 2026.7.0", "2026.1.0") void defer(const std voi defer)(const char *name, std::function< void()> &&f)
Defer a callback to the next loop() call.
Definition component.h:560
ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") void set_timeout(const std voi set_timeout)(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition component.h:510
ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") bool cancel_timeout(const std boo cancel_timeout)(const char *name)
Cancel a timeout function.
Definition component.h:532
void attach_interrupt(void(*func)(T *), T *arg, gpio::InterruptType type) const
Definition gpio.h:107
void call_setup() override
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Inform the parent automation that the event has triggered.
Definition automation.h:482
virtual int get_height()
Get the calculated height of the display in pixels with rotation applied.
Definition display.h:327
virtual int get_width()
Get the calculated width of the display in pixels with rotation applied.
Definition display.h:325
std::map< uint8_t, TouchPoint > touches_
int16_t normalize_(int16_t val, int16_t min_val, int16_t max_val, bool inverted=false)
void attach_interrupt_(InternalGPIOPin *irq_pin, esphome::gpio::InterruptType type)
Call this function to send touch points to the on_touch listener and the binary_sensors.
std::vector< TouchListener * > touch_listeners_
Trigger< const TouchPoints_t & > update_trigger_
Trigger< TouchPoint, const TouchPoints_t & > touch_trigger_
void add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw=0)
uint16_t type
uint16_t id
mopeka_std_values val[3]
const char *const TAG
Definition spi.cpp:7
std::vector< TouchPoint > TouchPoints_t
Definition touchscreen.h:29
static void gpio_intr(TouchscreenInterrupt *store)
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6