Files
MeshCore/variants/sensecap_indicator-espnow/IndicatorTouch.h
T
mikecarper 9d57a3a4f8 feat: complete Cascade Indicator companion support
Add native-resolution Indicator UI, touch navigation, SD-backed pixel and emoji fonts, LoRa radio support, dual-slot WiFi Companion provisioning, and mesh clock fallback. Harden radio recovery and build defaults, normalize ESP32 setup-AP protocols, and document/test the operator workflows.
2026-08-25 18:37:17 -07:00

92 lines
2.9 KiB
C++

#pragma once
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#include <string.h>
// This FT5x06-compatible controller exposes valid point data at register 0x02,
// but some units return zero for all optional identity registers. Probe the
// point register instead of rejecting those units by vendor ID.
class IndicatorTouch : public lgfx::Touch_FT5x06 {
static constexpr size_t POINT_DATA_SIZE = 5;
bool readPointData(uint8_t* data, size_t size) {
const uint8_t point_count_reg = 0x02;
return lgfx::i2c::transactionWriteRead(
_cfg.i2c_port, _cfg.i2c_addr, &point_count_reg, 1,
data, size, _cfg.freq)
.has_value();
}
bool readStablePointData(uint8_t* data) {
uint8_t samples[2][POINT_DATA_SIZE] = {};
if (!readPointData(samples[0], sizeof(samples[0]))) return false;
if ((samples[0][0] & 0x0F) == 0) {
memcpy(data, samples[0], POINT_DATA_SIZE);
return true;
}
// Coordinates may change while the controller is being read. Match two
// consecutive snapshots, as the standard driver does, so X/Y bytes from
// different positions cannot reverse a gesture. If the finger keeps
// moving through every retry, use the newest complete snapshot.
uint8_t current = 0;
for (uint8_t attempt = 0; attempt < 5; ++attempt) {
const uint8_t next = current ^ 1U;
if (!readPointData(samples[next], sizeof(samples[next]))) return false;
if (memcmp(samples[current], samples[next], POINT_DATA_SIZE) == 0) {
memcpy(data, samples[next], POINT_DATA_SIZE);
return true;
}
current = next;
}
memcpy(data, samples[current], POINT_DATA_SIZE);
return true;
}
public:
bool init() override {
_inited = false;
if (!lgfx::i2c::init(_cfg.i2c_port, _cfg.pin_sda, _cfg.pin_scl)
.has_value()) {
return false;
}
uint8_t point_count = 0;
if (!readPointData(&point_count, 1)) {
return false;
}
// Select normal operation and polling mode, matching the generic driver.
if (!lgfx::i2c::writeRegister8(
_cfg.i2c_port, _cfg.i2c_addr, 0x00, 0x00, 0, _cfg.freq)
.has_value()
|| !lgfx::i2c::writeRegister8(
_cfg.i2c_port, _cfg.i2c_addr, 0xA4, 0x00, 0, _cfg.freq)
.has_value()) {
return false;
}
_inited = true;
return true;
}
uint_fast8_t getTouchRaw(lgfx::touch_point_t* point,
uint_fast8_t count) override {
if (point == nullptr || count == 0 || (!_inited && !init())) return 0;
uint8_t data[POINT_DATA_SIZE] = {};
if (!readStablePointData(data)) {
_inited = false;
return 0;
}
if ((data[0] & 0x0F) == 0) return 0;
point[0].x = ((data[1] & 0x0F) << 8) | data[2];
point[0].y = ((data[3] & 0x0F) << 8) | data[4];
point[0].size = 1;
point[0].id = data[3] >> 4;
return 1;
}
};