ESPHome 2026.10.0-dev
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",
73 active_high ? LOG_STR_LITERAL("HIGH") : LOG_STR_LITERAL("LOW"));
74 } else {
75 // IO expander pin: leave as output for configuration only.
76 ESP_LOGD(TAG, "Interrupt pin: IO expander polling mode, active %s",
77 active_high ? LOG_STR_LITERAL("HIGH") : LOG_STR_LITERAL("LOW"));
78 }
79 }
80 }
81 }
82
83 if (this->x_raw_max_ == 0 || this->y_raw_max_ == 0) {
84 // no calibration? Attempt to read the max values from the touchscreen.
85 if (err == i2c::ERROR_OK) {
86 err = this->write(GET_MAX_VALUES, sizeof(GET_MAX_VALUES));
87 if (err == i2c::ERROR_OK) {
88 err = this->read(data, sizeof(data));
89 if (err == i2c::ERROR_OK) {
90 this->x_raw_max_ = encode_uint16(data[1], data[0]);
91 this->y_raw_max_ = encode_uint16(data[3], data[2]);
92 if (this->swap_x_y_)
93 std::swap(this->x_raw_max_, this->y_raw_max_);
94 }
95 }
96 }
97 if (err != i2c::ERROR_OK) {
98 this->mark_failed(LOG_STR("Calibration error"));
99 return;
100 }
101 }
102
103 if (err != i2c::ERROR_OK) {
104 this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
105 return;
106 }
107 this->setup_done_ = true;
108}
109
111 this->skip_update_ = true; // skip send touch events by default, set to false after successful error checks
112 if (!this->setup_done_) {
113 return;
114 }
115
116 i2c::ErrorCode err;
117 uint8_t touch_state = 0;
118 uint8_t data[MAX_TOUCHES + 1][8]; // 8 bytes each for each point, plus extra space for the key byte
119
120 err = this->write(GET_TOUCH_STATE, sizeof(GET_TOUCH_STATE));
121 ERROR_CHECK(err);
122 err = this->read(&touch_state, 1);
123 ERROR_CHECK(err);
124 this->write(CLEAR_TOUCH_STATE, sizeof(CLEAR_TOUCH_STATE));
125 uint8_t num_of_touches = touch_state & 0x07;
126
127 if ((touch_state & 0x80) == 0 || num_of_touches > MAX_TOUCHES) {
128 return;
129 }
130
131 err = this->write(GET_TOUCHES, sizeof(GET_TOUCHES));
132 ERROR_CHECK(err);
133 // num_of_touches is guaranteed to be 0..5. Also read the key data
134 err = this->read(data[0], sizeof(data[0]) * num_of_touches + 1);
135 ERROR_CHECK(err);
136
137 this->skip_update_ = false; // All error checks passed, send touch events
138 for (uint8_t i = 0; i != num_of_touches; i++) {
139 uint16_t id = data[i][0];
140 uint16_t x = encode_uint16(data[i][2], data[i][1]);
141 uint16_t y = encode_uint16(data[i][4], data[i][3]);
142 this->add_raw_touch_position_(id, x, y);
143 }
144 auto keys = data[num_of_touches][0] & ((1 << MAX_BUTTONS) - 1);
145 if (keys != this->button_state_) {
146 this->button_state_ = keys;
147 for (size_t i = 0; i != MAX_BUTTONS; i++) {
148 for (auto *listener : this->button_listeners_)
149 listener->update_button(i, (keys & (1 << i)) != 0);
150 }
151 }
152}
153
155 ESP_LOGCONFIG(TAG, "GT911 Touchscreen:");
156 LOG_I2C_DEVICE(this);
157 LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
158 LOG_PIN(" Reset Pin: ", this->reset_pin_);
159}
160
161} // namespace esphome::gt911
void mark_failed()
Mark this component as failed.
void set_timeout(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a const char* name.
Definition component.cpp:96
virtual void pin_mode(gpio::Flags flags)=0
virtual void setup()=0
virtual void digital_write(bool value)=0
virtual bool is_internal()
Definition gpio.h:81
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:49
@ INTERRUPT_RISING_EDGE
Definition gpio.h:48
@ FLAG_OUTPUT
Definition gpio.h:26
@ FLAG_INPUT
Definition gpio.h:25
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:884
void HOT delay(uint32_t ms)
Definition hal.cpp:85
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6