ESPHome 2026.5.1
Loading...
Searching...
No Matches
gt911_touchscreen.cpp
Go to the documentation of this file.
1#include "gt911_touchscreen.h"
2
4#include "esphome/core/log.h"
5#include "esphome/core/gpio.h"
6
7namespace esphome::gt911 {
8
9static const char *const TAG = "gt911.touchscreen";
10
11static const uint8_t PRIMARY_ADDRESS = 0x5D; // default I2C address for GT911
12static const uint8_t SECONDARY_ADDRESS = 0x14; // secondary I2C address for GT911
13static const uint8_t GET_TOUCH_STATE[2] = {0x81, 0x4E};
14static const uint8_t CLEAR_TOUCH_STATE[3] = {0x81, 0x4E, 0x00};
15static const uint8_t GET_TOUCHES[2] = {0x81, 0x4F};
16static const uint8_t GET_SWITCHES[2] = {0x80, 0x4D};
17static const uint8_t GET_MAX_VALUES[2] = {0x80, 0x48};
18static const size_t MAX_TOUCHES = 5; // max number of possible touches reported
19static const size_t MAX_BUTTONS = 4; // max number of buttons scanned
20
21#define ERROR_CHECK(err) \
22 if ((err) != i2c::ERROR_OK) { \
23 this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); \
24 return; \
25 }
26
28 if (this->reset_pin_ != nullptr) {
29 // temporarily set the interrupt pin to output to control address selection
30 this->reset_pin_->setup();
31 this->reset_pin_->digital_write(false);
32 if (this->interrupt_pin_ != nullptr) {
33 this->interrupt_pin_->setup();
35 this->interrupt_pin_->digital_write(false);
36 }
37 delay(2);
38 this->reset_pin_->digital_write(true);
39 // wait at least T3+T4 ms as per the datasheet
40 this->set_timeout(5 + 50 + 1, [this] { this->setup_internal_(); });
41 return;
42 }
43 this->setup_internal_();
44}
45
47 if (this->interrupt_pin_ != nullptr) {
48 if (this->interrupt_pin_->is_internal())
50 }
51
52 uint8_t data[4];
53 i2c::ErrorCode err = this->write(GET_SWITCHES, sizeof(GET_SWITCHES));
54 if (err != i2c::ERROR_OK && this->address_ == PRIMARY_ADDRESS) {
55 this->address_ = SECONDARY_ADDRESS;
56 err = this->write(GET_SWITCHES, sizeof(GET_SWITCHES));
57 }
58 if (err == i2c::ERROR_OK) {
59 err = this->read(data, 1);
60 if (err == i2c::ERROR_OK) {
61 ESP_LOGD(TAG, "Switches ADDR: 0x%02X DATA: 0x%02X", this->address_, data[0]);
62
63 // data[0] & 1 == 1 => controller uses falling edge => active-low
64 // data[0] & 1 == 0 => controller uses rising edge => active-high
65 bool active_high = !(data[0] & 1);
66
67 if (this->interrupt_pin_ != nullptr) {
68 if (this->interrupt_pin_->is_internal()) {
69 // Direct MCU pin: attach a hardware interrupt, no polling needed.
70 this->attach_interrupt_(static_cast<InternalGPIOPin *>(this->interrupt_pin_),
72 ESP_LOGD(TAG, "Interrupt pin: hardware interrupt, active %s", active_high ? "HIGH" : "LOW");
73 } else {
74 // IO expander pin: leave as output for configuration only.
75 ESP_LOGD(TAG, "Interrupt pin: IO expander polling mode, active %s", active_high ? "HIGH" : "LOW");
76 }
77 }
78 }
79 }
80
81 if (this->x_raw_max_ == 0 || this->y_raw_max_ == 0) {
82 // no calibration? Attempt to read the max values from the touchscreen.
83 if (err == i2c::ERROR_OK) {
84 err = this->write(GET_MAX_VALUES, sizeof(GET_MAX_VALUES));
85 if (err == i2c::ERROR_OK) {
86 err = this->read(data, sizeof(data));
87 if (err == i2c::ERROR_OK) {
88 this->x_raw_max_ = encode_uint16(data[1], data[0]);
89 this->y_raw_max_ = encode_uint16(data[3], data[2]);
90 if (this->swap_x_y_)
91 std::swap(this->x_raw_max_, this->y_raw_max_);
92 }
93 }
94 }
95 if (err != i2c::ERROR_OK) {
96 this->mark_failed(LOG_STR("Calibration error"));
97 return;
98 }
99 }
100
101 if (err != i2c::ERROR_OK) {
102 this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
103 return;
104 }
105 this->setup_done_ = true;
106}
107
109 this->skip_update_ = true; // skip send touch events by default, set to false after successful error checks
110 if (!this->setup_done_) {
111 return;
112 }
113
114 i2c::ErrorCode err;
115 uint8_t touch_state = 0;
116 uint8_t data[MAX_TOUCHES + 1][8]; // 8 bytes each for each point, plus extra space for the key byte
117
118 err = this->write(GET_TOUCH_STATE, sizeof(GET_TOUCH_STATE));
119 ERROR_CHECK(err);
120 err = this->read(&touch_state, 1);
121 ERROR_CHECK(err);
122 this->write(CLEAR_TOUCH_STATE, sizeof(CLEAR_TOUCH_STATE));
123 uint8_t num_of_touches = touch_state & 0x07;
124
125 if ((touch_state & 0x80) == 0 || num_of_touches > MAX_TOUCHES) {
126 return;
127 }
128
129 err = this->write(GET_TOUCHES, sizeof(GET_TOUCHES));
130 ERROR_CHECK(err);
131 // num_of_touches is guaranteed to be 0..5. Also read the key data
132 err = this->read(data[0], sizeof(data[0]) * num_of_touches + 1);
133 ERROR_CHECK(err);
134
135 this->skip_update_ = false; // All error checks passed, send touch events
136 for (uint8_t i = 0; i != num_of_touches; i++) {
137 uint16_t id = data[i][0];
138 uint16_t x = encode_uint16(data[i][2], data[i][1]);
139 uint16_t y = encode_uint16(data[i][4], data[i][3]);
140 this->add_raw_touch_position_(id, x, y);
141 }
142 auto keys = data[num_of_touches][0] & ((1 << MAX_BUTTONS) - 1);
143 if (keys != this->button_state_) {
144 this->button_state_ = keys;
145 for (size_t i = 0; i != MAX_BUTTONS; i++) {
146 for (auto *listener : this->button_listeners_)
147 listener->update_button(i, (keys & (1 << i)) != 0);
148 }
149 }
150}
151
153 ESP_LOGCONFIG(TAG, "GT911 Touchscreen:");
154 LOG_I2C_DEVICE(this);
155 LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
156 LOG_PIN(" Reset Pin: ", this->reset_pin_);
157}
158
159} // namespace esphome::gt911
void mark_failed()
Mark this component as failed.
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
virtual void pin_mode(gpio::Flags flags)=0
virtual void setup()=0
virtual void digital_write(bool value)=0
virtual ESPDEPRECATED("Override dump_summary(char*, size_t) instead. Will be removed in 2026.7.0.", "2026.1.0") virtual std boo is_internal)()
Get a summary of this pin as a string.
Definition gpio.h:88
void setup_internal_()
Perform the internal setup routine for the GT911 touchscreen.
void setup() override
Initialize the GT911 touchscreen.
std::vector< GT911ButtonListener * > button_listeners_
bool setup_done_
True if the touchscreen setup has completed successfully.
ErrorCode write(const uint8_t *data, size_t len) const
writes an array of bytes to a device using an I2CBus
Definition i2c.h:183
ErrorCode read(uint8_t *data, size_t len) const
reads an array of bytes from the device using an I2CBus
Definition i2c.h:163
uint8_t address_
store the address of the device on the bus
Definition i2c.h:270
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.
void add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw=0)
@ INTERRUPT_FALLING_EDGE
Definition gpio.h:51
@ INTERRUPT_RISING_EDGE
Definition gpio.h:50
@ FLAG_OUTPUT
Definition gpio.h:28
@ FLAG_INPUT
Definition gpio.h:27
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
Definition i2c_bus.h:12
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:14
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition helpers.h:859
void HOT delay(uint32_t ms)
Definition hal.cpp:85
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6