-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add TT21100 touchscreen component (#4793)
Co-authored-by: Rajan Patel <rpatel3001@gmail.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
- Loading branch information
1 parent
7a55108
commit 5f531ac
Showing
11 changed files
with
392 additions
and
2 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import esphome.codegen as cg | ||
|
||
CODEOWNERS = ["@kroimon"] | ||
|
||
tt21100_ns = cg.esphome_ns.namespace("tt21100") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import esphome.codegen as cg | ||
import esphome.config_validation as cv | ||
from esphome.components import binary_sensor | ||
from esphome.const import CONF_INDEX | ||
|
||
from .. import tt21100_ns | ||
from ..touchscreen import TT21100Touchscreen, TT21100ButtonListener | ||
|
||
CONF_TT21100_ID = "tt21100_id" | ||
|
||
TT21100Button = tt21100_ns.class_( | ||
"TT21100Button", | ||
binary_sensor.BinarySensor, | ||
cg.Component, | ||
TT21100ButtonListener, | ||
cg.Parented.template(TT21100Touchscreen), | ||
) | ||
|
||
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(TT21100Button).extend( | ||
{ | ||
cv.GenerateID(CONF_TT21100_ID): cv.use_id(TT21100Touchscreen), | ||
cv.Required(CONF_INDEX): cv.int_range(min=0, max=3), | ||
} | ||
) | ||
|
||
|
||
async def to_code(config): | ||
var = await binary_sensor.new_binary_sensor(config) | ||
await cg.register_component(var, config) | ||
await cg.register_parented(var, config[CONF_TT21100_ID]) | ||
cg.add(var.set_index(config[CONF_INDEX])) |
27 changes: 27 additions & 0 deletions
27
esphome/components/tt21100/binary_sensor/tt21100_button.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#include "tt21100_button.h" | ||
#include "esphome/core/log.h" | ||
|
||
namespace esphome { | ||
namespace tt21100 { | ||
|
||
static const char *const TAG = "tt21100.binary_sensor"; | ||
|
||
void TT21100Button::setup() { | ||
this->parent_->register_button_listener(this); | ||
this->publish_initial_state(false); | ||
} | ||
|
||
void TT21100Button::dump_config() { | ||
LOG_BINARY_SENSOR("", "TT21100 Button", this); | ||
ESP_LOGCONFIG(TAG, " Index: %u", this->index_); | ||
} | ||
|
||
void TT21100Button::update_button(uint8_t index, uint16_t state) { | ||
if (index != this->index_) | ||
return; | ||
|
||
this->publish_state(state > 0); | ||
} | ||
|
||
} // namespace tt21100 | ||
} // namespace esphome |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#pragma once | ||
|
||
#include "esphome/components/binary_sensor/binary_sensor.h" | ||
#include "esphome/components/tt21100/touchscreen/tt21100.h" | ||
#include "esphome/core/component.h" | ||
#include "esphome/core/helpers.h" | ||
|
||
namespace esphome { | ||
namespace tt21100 { | ||
|
||
class TT21100Button : public binary_sensor::BinarySensor, | ||
public Component, | ||
public TT21100ButtonListener, | ||
public Parented<TT21100Touchscreen> { | ||
public: | ||
void setup() override; | ||
void dump_config() override; | ||
|
||
void set_index(uint8_t index) { this->index_ = index; } | ||
|
||
void update_button(uint8_t index, uint16_t state) override; | ||
|
||
protected: | ||
uint8_t index_; | ||
}; | ||
|
||
} // namespace tt21100 | ||
} // namespace esphome |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import esphome.codegen as cg | ||
import esphome.config_validation as cv | ||
|
||
from esphome import pins | ||
from esphome.components import i2c, touchscreen | ||
from esphome.const import CONF_ID, CONF_INTERRUPT_PIN, CONF_RESET_PIN | ||
|
||
from .. import tt21100_ns | ||
|
||
DEPENDENCIES = ["i2c"] | ||
|
||
TT21100Touchscreen = tt21100_ns.class_( | ||
"TT21100Touchscreen", | ||
touchscreen.Touchscreen, | ||
cg.Component, | ||
i2c.I2CDevice, | ||
) | ||
TT21100ButtonListener = tt21100_ns.class_("TT21100ButtonListener") | ||
|
||
CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend( | ||
cv.Schema( | ||
{ | ||
cv.GenerateID(): cv.declare_id(TT21100Touchscreen), | ||
cv.Required(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_schema, | ||
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, | ||
} | ||
) | ||
.extend(i2c.i2c_device_schema(0x24)) | ||
.extend(cv.COMPONENT_SCHEMA) | ||
) | ||
|
||
|
||
async def to_code(config): | ||
var = cg.new_Pvariable(config[CONF_ID]) | ||
await cg.register_component(var, config) | ||
await i2c.register_i2c_device(var, config) | ||
await touchscreen.register_touchscreen(var, config) | ||
|
||
interrupt_pin = await cg.gpio_pin_expression(config[CONF_INTERRUPT_PIN]) | ||
cg.add(var.set_interrupt_pin(interrupt_pin)) | ||
|
||
if CONF_RESET_PIN in config: | ||
rts_pin = await cg.gpio_pin_expression(config[CONF_RESET_PIN]) | ||
cg.add(var.set_reset_pin(rts_pin)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
#include "tt21100.h" | ||
#include "esphome/core/log.h" | ||
|
||
namespace esphome { | ||
namespace tt21100 { | ||
|
||
static const char *const TAG = "tt21100"; | ||
|
||
static const uint8_t MAX_BUTTONS = 4; | ||
static const uint8_t MAX_TOUCH_POINTS = 5; | ||
static const uint8_t MAX_DATA_LEN = (7 + MAX_TOUCH_POINTS * 10); // 7 Header + (Points * 10 data bytes) | ||
|
||
struct TT21100ButtonReport { | ||
uint16_t length; // Always 14 (0x000E) | ||
uint8_t report_id; // Always 0x03 | ||
uint16_t timestamp; // Number in units of 100 us | ||
uint8_t btn_value; // Only use bit 0..3 | ||
uint16_t btn_signal[MAX_BUTTONS]; | ||
} __attribute__((packed)); | ||
|
||
struct TT21100TouchRecord { | ||
uint8_t : 5; | ||
uint8_t touch_type : 3; | ||
uint8_t tip : 1; | ||
uint8_t event_id : 2; | ||
uint8_t touch_id : 5; | ||
uint16_t x; | ||
uint16_t y; | ||
uint8_t pressure; | ||
uint16_t major_axis_length; | ||
uint8_t orientation; | ||
} __attribute__((packed)); | ||
|
||
struct TT21100TouchReport { | ||
uint16_t length; | ||
uint8_t report_id; | ||
uint16_t timestamp; | ||
uint8_t : 2; | ||
uint8_t large_object : 1; | ||
uint8_t record_num : 5; | ||
uint8_t report_counter : 2; | ||
uint8_t : 3; | ||
uint8_t noise_effect : 3; | ||
TT21100TouchRecord touch_record[MAX_TOUCH_POINTS]; | ||
} __attribute__((packed)); | ||
|
||
void TT21100TouchscreenStore::gpio_intr(TT21100TouchscreenStore *store) { store->touch = true; } | ||
|
||
float TT21100Touchscreen::get_setup_priority() const { return setup_priority::HARDWARE - 1.0f; } | ||
|
||
void TT21100Touchscreen::setup() { | ||
ESP_LOGCONFIG(TAG, "Setting up TT21100 Touchscreen..."); | ||
|
||
// Register interrupt pin | ||
this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); | ||
this->interrupt_pin_->setup(); | ||
this->store_.pin = this->interrupt_pin_->to_isr(); | ||
this->interrupt_pin_->attach_interrupt(TT21100TouchscreenStore::gpio_intr, &this->store_, | ||
gpio::INTERRUPT_FALLING_EDGE); | ||
|
||
// Perform reset if necessary | ||
if (this->reset_pin_ != nullptr) { | ||
this->reset_pin_->setup(); | ||
this->reset_(); | ||
} | ||
|
||
// Update display dimensions if they were updated during display setup | ||
this->display_width_ = this->display_->get_width(); | ||
this->display_height_ = this->display_->get_height(); | ||
this->rotation_ = static_cast<TouchRotation>(this->display_->get_rotation()); | ||
|
||
// Trigger initial read to activate the interrupt | ||
this->store_.touch = true; | ||
} | ||
|
||
void TT21100Touchscreen::loop() { | ||
if (!this->store_.touch) | ||
return; | ||
this->store_.touch = false; | ||
|
||
// Read report length | ||
uint16_t data_len; | ||
this->read((uint8_t *) &data_len, sizeof(data_len)); | ||
|
||
// Read report data | ||
uint8_t data[MAX_DATA_LEN]; | ||
if (data_len > 0 && data_len < sizeof(data)) { | ||
this->read(data, data_len); | ||
|
||
if (data_len == 14) { | ||
// Button event | ||
auto *report = (TT21100ButtonReport *) data; | ||
|
||
ESP_LOGV(TAG, "Button report: Len=%d, ID=%d, Time=%5u, Value=[%u], Signal=[%04X][%04X][%04X][%04X]", | ||
report->length, report->report_id, report->timestamp, report->btn_value, report->btn_signal[0], | ||
report->btn_signal[1], report->btn_signal[2], report->btn_signal[3]); | ||
|
||
for (uint8_t i = 0; i < 4; i++) { | ||
for (auto *listener : this->button_listeners_) | ||
listener->update_button(i, report->btn_signal[i]); | ||
} | ||
|
||
} else if (data_len >= 7) { | ||
// Touch point event | ||
auto *report = (TT21100TouchReport *) data; | ||
|
||
ESP_LOGV(TAG, | ||
"Touch report: Len=%d, ID=%d, Time=%5u, LargeObject=%u, RecordNum=%u, RecordCounter=%u, NoiseEffect=%u", | ||
report->length, report->report_id, report->timestamp, report->large_object, report->record_num, | ||
report->report_counter, report->noise_effect); | ||
|
||
uint8_t touch_count = (data_len - (sizeof(*report) - sizeof(report->touch_record))) / sizeof(TT21100TouchRecord); | ||
|
||
if (touch_count == 0) { | ||
for (auto *listener : this->touch_listeners_) | ||
listener->release(); | ||
return; | ||
} | ||
|
||
for (int i = 0; i < touch_count; i++) { | ||
auto *touch = &report->touch_record[i]; | ||
|
||
ESP_LOGV(TAG, | ||
"Touch %d: Type=%u, Tip=%u, EventId=%u, TouchId=%u, X=%u, Y=%u, Pressure=%u, MajorAxisLen=%u, " | ||
"Orientation=%u", | ||
i, touch->touch_type, touch->tip, touch->event_id, touch->touch_id, touch->x, touch->y, | ||
touch->pressure, touch->major_axis_length, touch->orientation); | ||
|
||
TouchPoint tp; | ||
switch (this->rotation_) { | ||
case ROTATE_0_DEGREES: | ||
// Origin is top right, so mirror X by default | ||
tp.x = this->display_width_ - touch->x; | ||
tp.y = touch->y; | ||
break; | ||
case ROTATE_90_DEGREES: | ||
tp.x = touch->y; | ||
tp.y = touch->x; | ||
break; | ||
case ROTATE_180_DEGREES: | ||
tp.x = touch->x; | ||
tp.y = this->display_height_ - touch->y; | ||
break; | ||
case ROTATE_270_DEGREES: | ||
tp.x = this->display_height_ - touch->y; | ||
tp.y = this->display_width_ - touch->x; | ||
break; | ||
} | ||
tp.id = touch->tip; | ||
tp.state = touch->pressure; | ||
|
||
this->defer([this, tp]() { this->send_touch_(tp); }); | ||
} | ||
} | ||
} | ||
} | ||
|
||
void TT21100Touchscreen::reset_() { | ||
if (this->reset_pin_ != nullptr) { | ||
this->reset_pin_->digital_write(false); | ||
delay(10); | ||
this->reset_pin_->digital_write(true); | ||
delay(10); | ||
} | ||
} | ||
|
||
void TT21100Touchscreen::dump_config() { | ||
ESP_LOGCONFIG(TAG, "TT21100 Touchscreen:"); | ||
LOG_I2C_DEVICE(this); | ||
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); | ||
LOG_PIN(" Reset Pin: ", this->reset_pin_); | ||
} | ||
|
||
} // namespace tt21100 | ||
} // namespace esphome |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
#pragma once | ||
|
||
#include "esphome/components/i2c/i2c.h" | ||
#include "esphome/components/touchscreen/touchscreen.h" | ||
#include "esphome/core/component.h" | ||
#include "esphome/core/hal.h" | ||
|
||
namespace esphome { | ||
namespace tt21100 { | ||
|
||
using namespace touchscreen; | ||
|
||
struct TT21100TouchscreenStore { | ||
volatile bool touch; | ||
ISRInternalGPIOPin pin; | ||
|
||
static void gpio_intr(TT21100TouchscreenStore *store); | ||
}; | ||
|
||
class TT21100ButtonListener { | ||
public: | ||
virtual void update_button(uint8_t index, uint16_t state) = 0; | ||
}; | ||
|
||
class TT21100Touchscreen : public Touchscreen, public Component, public i2c::I2CDevice { | ||
public: | ||
void setup() override; | ||
void loop() override; | ||
void dump_config() override; | ||
float get_setup_priority() const override; | ||
|
||
void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; } | ||
void set_reset_pin(GPIOPin *pin) { this->reset_pin_ = pin; } | ||
|
||
void register_button_listener(TT21100ButtonListener *listener) { this->button_listeners_.push_back(listener); } | ||
|
||
protected: | ||
void reset_(); | ||
|
||
TT21100TouchscreenStore store_; | ||
|
||
InternalGPIOPin *interrupt_pin_; | ||
GPIOPin *reset_pin_{nullptr}; | ||
|
||
std::vector<TT21100ButtonListener *> button_listeners_; | ||
}; | ||
|
||
} // namespace tt21100 | ||
} // namespace esphome |
Oops, something went wrong.