ESPHome 2026.11.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
9ESPHOME_LOG_TAG(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
27// Reset sequence (Goodix GT911 datasheet): INT held low while RST is released selects address 0x5D, then
28// T3 + T4 pass before INT is handed back to the chip as its interrupt output
29static constexpr uint32_t RESET_LOW_MS = 10;
30static constexpr uint32_t RESET_SETTLE_MS = 5 + 50 + 1;
31
33 if (this->reset_pin_ != nullptr) {
34 this->reset_pin_->setup();
35 this->reset_pin_->digital_write(false);
36 if (this->interrupt_pin_ != nullptr) {
37 this->interrupt_pin_->setup();
39 this->interrupt_pin_->digital_write(false);
40 }
41 this->set_timeout(RESET_LOW_MS, [this] {
42 this->reset_pin_->digital_write(true);
43 this->set_timeout(RESET_SETTLE_MS, [this] { this->setup_internal_(); });
44 });
45 return;
46 }
47 this->setup_internal_();
48}
49
51 // Expander pins must leave output mode too, or the chip cannot drive its interrupt line
52 if (this->interrupt_pin_ != nullptr)
54
55 uint8_t data[4];
56 i2c::ErrorCode err = this->write(GET_SWITCHES, sizeof(GET_SWITCHES));
57 if (err != i2c::ERROR_OK && this->address_ == PRIMARY_ADDRESS) {
58 this->address_ = SECONDARY_ADDRESS;
59 err = this->write(GET_SWITCHES, sizeof(GET_SWITCHES));
60 }
61 if (err == i2c::ERROR_OK) {
62 err = this->read(data, 1);
63 if (err == i2c::ERROR_OK) {
64 ESP_LOGD(TAG, "Switches ADDR: 0x%02X DATA: 0x%02X", this->address_, data[0]);
65
66 // data[0] & 1 == 1 => controller uses falling edge => active-low
67 // data[0] & 1 == 0 => controller uses rising edge => active-high
68 bool active_high = !(data[0] & 1);
69
70 if (this->interrupt_pin_ != nullptr) {
71 if (this->interrupt_pin_->is_internal()) {
72 // Direct MCU pin: attach a hardware interrupt, no polling needed.
73 this->attach_interrupt_(static_cast<InternalGPIOPin *>(this->interrupt_pin_),
75 ESP_LOGD(TAG, "Interrupt pin: hardware interrupt, active %s",
76 active_high ? LOG_STR_LITERAL("HIGH") : LOG_STR_LITERAL("LOW"));
77 } else {
78 // IO expander pin: no hardware interrupt, the touch state is polled.
79 ESP_LOGD(TAG, "Interrupt pin: IO expander polling mode, active %s",
80 active_high ? LOG_STR_LITERAL("HIGH") : LOG_STR_LITERAL("LOW"));
81 }
82 }
83 }
84 }
85
86 if (this->x_raw_max_ == 0 || this->y_raw_max_ == 0) {
87 // no calibration? Attempt to read the max values from the touchscreen.
88 if (err == i2c::ERROR_OK) {
89 err = this->write(GET_MAX_VALUES, sizeof(GET_MAX_VALUES));
90 if (err == i2c::ERROR_OK) {
91 err = this->read(data, sizeof(data));
92 if (err == i2c::ERROR_OK) {
94 this->y_raw_max_ = encode_value_le<uint16_t>(&data[2]);
95 if (this->swap_x_y_)
96 std::swap(this->x_raw_max_, this->y_raw_max_);
97 }
98 }
99 }
100 if (err != i2c::ERROR_OK) {
101 this->mark_failed(LOG_STR("Calibration error"));
102 return;
103 }
104 }
105
106 if (err != i2c::ERROR_OK) {
107 this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
108 return;
109 }
110 this->setup_done_ = true;
111}
112
114 this->skip_update_ = true; // skip send touch events by default, set to false after successful error checks
115 if (!this->setup_done_) {
116 return;
117 }
118
119 i2c::ErrorCode err;
120 uint8_t touch_state = 0;
121 uint8_t data[MAX_TOUCHES + 1][8]; // 8 bytes each for each point, plus extra space for the key byte
122
123 err = this->write(GET_TOUCH_STATE, sizeof(GET_TOUCH_STATE));
124 ERROR_CHECK(err);
125 err = this->read(&touch_state, 1);
126 ERROR_CHECK(err);
127 this->write(CLEAR_TOUCH_STATE, sizeof(CLEAR_TOUCH_STATE));
128 uint8_t num_of_touches = touch_state & 0x07;
129
130 if ((touch_state & 0x80) == 0 || num_of_touches > MAX_TOUCHES) {
131 return;
132 }
133
134 err = this->write(GET_TOUCHES, sizeof(GET_TOUCHES));
135 ERROR_CHECK(err);
136 // num_of_touches is guaranteed to be 0..5. Also read the key data
137 err = this->read(data[0], sizeof(data[0]) * num_of_touches + 1);
138 ERROR_CHECK(err);
139
140 this->skip_update_ = false; // All error checks passed, send touch events
141 for (uint8_t i = 0; i != num_of_touches; i++) {
142 uint16_t id = data[i][0];
143 uint16_t x = encode_uint16(data[i][2], data[i][1]);
144 uint16_t y = encode_uint16(data[i][4], data[i][3]);
145 this->add_raw_touch_position_(id, x, y);
146 }
147 auto keys = data[num_of_touches][0] & ((1 << MAX_BUTTONS) - 1);
148 if (keys != this->button_state_) {
149 this->button_state_ = keys;
150 for (size_t i = 0; i != MAX_BUTTONS; i++) {
151 for (auto *listener : this->button_listeners_)
152 listener->update_button(i, (keys & (1 << i)) != 0);
153 }
154 }
155}
156
158 ESP_LOGCONFIG(TAG, "GT911 Touchscreen:");
159 LOG_I2C_DEVICE(this);
160 LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
161 LOG_PIN(" Reset Pin: ", this->reset_pin_);
162}
163
164} // 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()
Only the platform pin class behind the InternalGPIOPin alias may return true: callers static_cast to ...
Definition gpio_pin.h:87
void setup_internal_()
Perform the internal setup routine for the GT911 touchscreen.
void setup() override
With a reset pin the chip is reset with the interrupt pin held low, which selects address 0x5D,...
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:195
ErrorCode read(uint8_t *data, size_t len) const
reads an array of bytes from the device using an I2CBus
Definition i2c.h:175
uint8_t address_
store the address of the device on the bus
Definition i2c.h:327
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_pin.h:53
@ INTERRUPT_RISING_EDGE
Definition gpio_pin.h:52
ESPHOME_LOG_TAG(TAG, "GT911.binary_sensor")
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
Definition i2c_bus.h:13
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:15
constexpr T encode_value_le(const uint8_t *bytes)
Encode a value from its constituent bytes (from least to most significant) in an array with length si...
Definition helpers.h:1039
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:998
static void uint32_t
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6