ESPHome  2024.11.0
touchscreen.cpp
Go to the documentation of this file.
1 #include "touchscreen.h"
2 
3 #include "esphome/core/log.h"
4 
5 namespace esphome {
6 namespace touchscreen {
7 
8 static const char *const TAG = "touchscreen";
9 
11 
13  irq_pin->attach_interrupt(TouchscreenInterrupt::gpio_intr, &this->store_, type);
14  this->store_.init = true;
15  this->store_.touched = false;
16  ESP_LOGD(TAG, "Attach Touch Interupt");
17 }
18 
20  if (this->display_ != nullptr) {
21  this->display_width_ = this->display_->get_width();
22  this->display_height_ = this->display_->get_height();
23  }
25 }
26 
28  if (!this->store_.init) {
29  this->store_.touched = true;
30  } else {
31  // no need to poll if we have interrupts.
32  ESP_LOGW(TAG, "Touch Polling Stopped. You can safely remove the 'update_interval:' variable from the YAML file.");
33  this->stop_poller();
34  }
35 }
36 
38  if (this->store_.touched) {
39  ESP_LOGVV(TAG, "<< Do Touch loop >>");
40  this->first_touch_ = this->touches_.empty();
41  this->need_update_ = false;
42  this->is_touched_ = false;
43  this->skip_update_ = false;
44  for (auto &tp : this->touches_) {
45  if (tp.second.state == STATE_PRESSED || tp.second.state == STATE_UPDATED) {
46  tp.second.state |= STATE_RELEASING;
47  } else {
48  tp.second.state = STATE_RELEASED;
49  }
50  tp.second.x_prev = tp.second.x;
51  tp.second.y_prev = tp.second.y;
52  }
53  this->update_touches();
54  if (this->skip_update_) {
55  for (auto &tp : this->touches_) {
56  tp.second.state &= ~STATE_RELEASING;
57  }
58  } else {
59  this->store_.touched = false;
60  this->defer([this]() { this->send_touches_(); });
61  if (this->touch_timeout_ > 0) {
62  // Simulate a touch after <this->touch_timeout_> ms. This will reset any existing timeout operation.
63  // This is to detect touch release.
64  if (this->is_touched_) {
65  this->set_timeout(TAG, this->touch_timeout_, [this]() { this->store_.touched = true; });
66  } else {
67  this->cancel_timeout(TAG);
68  }
69  }
70  }
71  }
72 }
73 
74 void Touchscreen::add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw) {
75  TouchPoint tp;
76  uint16_t x, y;
77  if (this->touches_.count(id) == 0) {
78  tp.state = STATE_PRESSED;
79  tp.id = id;
80  } else {
81  tp = this->touches_[id];
82  tp.state = STATE_UPDATED;
83  tp.y_prev = tp.y;
84  tp.x_prev = tp.x;
85  }
86  tp.x_raw = x_raw;
87  tp.y_raw = y_raw;
88  tp.z_raw = z_raw;
89  if (this->x_raw_max_ != this->x_raw_min_ and this->y_raw_max_ != this->y_raw_min_) {
90  x = this->normalize_(x_raw, this->x_raw_min_, this->x_raw_max_, this->invert_x_);
91  y = this->normalize_(y_raw, this->y_raw_min_, this->y_raw_max_, this->invert_y_);
92 
93  if (this->swap_x_y_) {
94  std::swap(x, y);
95  }
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_) {
131  this->release_trigger_.trigger();
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 
148 int16_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 touchscreen
165 } // namespace esphome
std::vector< TouchPoint > TouchPoints_t
Definition: touchscreen.h:30
uint16_t x
Definition: tt21100.cpp:17
static void gpio_intr(TouchscreenInterrupt *store)
Definition: touchscreen.cpp:10
T id(T value)
Helper function to make id(var) known from lambdas work in custom components.
Definition: helpers.h:719
mopeka_std_values val[4]
uint16_t y
Definition: tt21100.cpp:18
const char *const TAG
Definition: spi.cpp:8
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.
Definition: touchscreen.cpp:12
int16_t normalize_(int16_t val, int16_t min_val, int16_t max_val, bool inverted=false)
uint8_t type
void swap(optional< T > &x, optional< T > &y)
Definition: optional.h:209
void call_setup() override
Definition: component.cpp:210
InterruptType
Definition: gpio.h:40
void add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw=0)
Definition: touchscreen.cpp:74
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void attach_interrupt(void(*func)(T *), T *arg, gpio::InterruptType type) const
Definition: gpio.h:81