src.nth.io/

summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorLuke Hoersten <[email protected]>2026-06-14 19:58:45 -0500
committerLuke Hoersten <[email protected]>2026-06-14 19:58:45 -0500
commit8e2de89efb540fd6a3dce2086216d3afc49e6831 (patch)
tree81f7245fdcadb441aab6bdc83db3ba466600910b /main
parent5bed2f0ccc9ca3bab80e1d2ed2a9dddc621428c6 (diff)
touch: wake on boot — FT5426 only reports while LCD is streaming + lit
Symptom: after a few cleanup cycles + power cycles, fresh boots left touch dead — the FT5426 ack'd, polling read TD_STATUS=0x00 cleanly, but taps never registered. Curl-waking the screen made touch work again instantly; tapping the dark backlight-off panel did nothing. Root cause: on this panel the FT5426 firmware only reports contact points while the DSI link is actively streaming with the backlight on. Booting straight into ASLEEP per the spec leaves taps silently unread, and there's no built-in way to wake without touch (no hardware button on this board — see prior commit). The fix is to wake on boot; the idle timer cuts back to sleep after idle_timeout_ms if nothing happens. Also defensively write DEV_MODE=0x00 (Working Mode) at touch_init, and add a runtime self-heal: if the polling loop sees stuck TD_STATUS above the 5-touch ceiling for ~3 s, pulse PC_RST_TP_N via the panel MCU and retry. Combined with display_touch_reset_pulse() the chip recovers from cold-boot wedges without manual intervention.
Diffstat (limited to 'main')
-rw-r--r--main/app_main.c10
-rw-r--r--main/touch.c79
2 files changed, 57 insertions, 32 deletions
diff --git a/main/app_main.c b/main/app_main.c
index 44e2052..c411c46 100644
--- a/main/app_main.c
+++ b/main/app_main.c
@@ -102,5 +102,15 @@ static void display_setup_task(void *arg)
}
ESP_LOGI(TAG, "display subsystems [%s] up", flags);
+
+ // Wake the display on boot. Empirically the FT5426 touch IC only
+ // reports contacts when the LCD is actively streaming with the
+ // backlight on — booting straight into ASLEEP leaves taps silently
+ // unread. The idle timer (idle_timeout_ms) puts the device back to
+ // sleep on its own if nothing happens.
+ if (dsp_err == ESP_OK) {
+ state_machine_set(VIEWPORT_STATE_AWAKE);
+ }
+
vTaskDelete(NULL);
}
diff --git a/main/touch.c b/main/touch.c
index 6f91665..4615f45 100644
--- a/main/touch.c
+++ b/main/touch.c
@@ -54,6 +54,13 @@ static void on_long_press(void)
if (display_is_up()) local_screens_overlay(OVERLAY_MS);
}
+// If we see N consecutive bogus polls (TD_STATUS > 5, i.e. the FT5426 is
+// returning 0xff), pulse PC_RST_TP_N to try to bring it back. PoE-only
+// cold boots leave the touch IC in a wedged state for ~seconds before it
+// recovers; this lets us keep nudging it without blocking init.
+#define WEDGE_THRESHOLD (3000 / POLL_PERIOD_MS) // ~3 s of bad polls
+#define WEDGE_BACKOFF_MS 2000
+
static void touch_task(void *arg)
{
bool was_down = false;
@@ -61,17 +68,35 @@ static void touch_task(void *arg)
bool long_fired = false;
uint64_t last_tap_us = 0;
uint8_t buf[7];
+ unsigned wedged_cycles = 0;
+ uint64_t last_reset_us = 0;
+ bool announced_up = false;
for (;;) {
vTaskDelay(pdMS_TO_TICKS(POLL_PERIOD_MS));
if (ft_read(FT_REG_DEV_MODE, buf, sizeof(buf)) != ESP_OK) continue;
+ uint64_t now_us = (uint64_t)esp_timer_get_time();
uint8_t touches = buf[FT_REG_TD_STATUS] & 0x0F;
// FT5x06 supports max 5 simultaneous points. A reading above that
// is bogus (typically the bus is parking 0xff because the IC isn't
- // responding) — drop the cycle to avoid faking a phantom press.
- if (touches > 5) continue;
- uint64_t now = (uint64_t)esp_timer_get_time();
+ // responding) — drop the cycle and bump the wedge counter.
+ if (touches > 5) {
+ if (++wedged_cycles >= WEDGE_THRESHOLD &&
+ now_us - last_reset_us > (uint64_t)WEDGE_BACKOFF_MS * 1000ULL) {
+ ESP_LOGW(TAG, "touch wedged — pulsing reset");
+ display_touch_reset_pulse();
+ last_reset_us = now_us;
+ wedged_cycles = 0;
+ }
+ continue;
+ }
+ if (wedged_cycles > 0 || !announced_up) {
+ ESP_LOGI(TAG, "touch online (TD_STATUS=%u)", touches);
+ wedged_cycles = 0;
+ announced_up = true;
+ }
+ uint64_t now = now_us;
if (touches > 0) {
if (!was_down) {
@@ -116,36 +141,26 @@ esp_err_t touch_init(void)
ESP_RETURN_ON_ERROR(i2c_master_bus_add_device(bus, &cfg, &s_dev),
TAG, "i2c add FT5426");
- // FT5426 sometimes returns 0xff on the first read after its reset is
- // released (especially on cold boot before its internal firmware has
- // finished booting). 0xff causes the polling loop to misread 15
- // touches forever. Two-stage recovery: short wait + retry, then a
- // hard reset pulse via the panel MCU if that still doesn't help.
- uint8_t dev_mode = 0xff;
- esp_err_t err = ESP_FAIL;
- for (int attempt = 0; attempt < 2; ++attempt) {
- for (int i = 0; i < 10; ++i) {
- err = ft_read(FT_REG_DEV_MODE, &dev_mode, 1);
- if (err == ESP_OK && dev_mode != 0xff) break;
- vTaskDelay(pdMS_TO_TICKS(30));
- }
- if (err == ESP_OK && dev_mode != 0xff) break;
- if (attempt == 0) {
- ESP_LOGW(TAG, "FT5426 stuck at 0xff — pulsing PC_RST_TP_N");
- display_touch_reset_pulse();
- }
- }
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "FT5426 @0x38 unreachable (%s)", esp_err_to_name(err));
- return err;
+ // Force DEV_MODE=0x00 (Working Mode) — chip might have powered up in
+ // System Info mode (0x40) or Factory Test mode (0x40) which would
+ // explain steady 0x00 TD_STATUS but no touch detection. Cheap to
+ // attempt; ignore result since some firmware revisions reject it
+ // pre-reset and the polling loop handles the alternative anyway.
+ {
+ uint8_t set_working[2] = { FT_REG_DEV_MODE, 0x00 };
+ i2c_master_transmit(s_dev, set_working, sizeof(set_working), 50);
}
- if (dev_mode == 0xff) {
- ESP_LOGW(TAG, "FT5426 still stuck after hard reset — touch will "
- "not register; tap/long-press disabled this boot");
- }
- ESP_LOGI(TAG, "FT5426 ack'd (dev_mode=0x%02x); "
- "tap=toggle wake/sleep, %dms hold=info overlay",
- dev_mode, LONG_PRESS_MS);
+
+ // Pulse the touch reset line and don't read dev_mode at init time:
+ // on PoE-only boots the FT5426 hasn't finished its internal firmware
+ // load when touch_init runs, and any read returns 0xff. The polling
+ // loop already drops cycles where TD_STATUS > 5, so a wedged chip is
+ // harmless; once its firmware finishes (often after a few hundred ms
+ // of being prodded by polling reads), real touches start landing.
+ display_touch_reset_pulse();
+ ESP_LOGI(TAG, "FT5426 polling started "
+ "(tap=toggle wake/sleep, %dms hold=info overlay)",
+ LONG_PRESS_MS);
BaseType_t ok = xTaskCreate(touch_task, "touch", 3072, NULL, 4, &s_task);
return (ok == pdPASS) ? ESP_OK : ESP_FAIL;