src.nth.io/

summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorLuke Hoersten <[email protected]>2026-06-14 20:11:21 -0500
committerLuke Hoersten <[email protected]>2026-06-14 20:11:21 -0500
commit865c4859d710870245ae7954e729edcaf442a921 (patch)
treec22cb7d60fb8ea3bba6e9e85b45779d0dfa87d4f /main
parent8e2de89efb540fd6a3dce2086216d3afc49e6831 (diff)
Default name = MAC; brief wake on boot; drop dead touch defences
- Seed viewport_name with the full base MAC, colons stripped (e8:f6:0a:e0:90:94 → "e8f60ae09094"). Stable across reboots, globally unique, 12 alphanumeric chars — well inside the mDNS hostname limit even with the "viewport-" prefix. - Add a mac_str field to viewport_state and expose it as the new "mac" key in GET /state and as a "mac" line on the info screen. - "Configured" no longer requires a viewport_name (it always has the MAC default); the scrypted URL alone gates outbound POSTs and the loading-vs-info screen choice. - Strip viewport_name[0] fallbacks in http_api / mdns / local_screens now that the field is never empty. - Replace the wake-on-boot-stays-on behaviour with a ~600 ms flash followed by sleep — long enough for the FT5426 touch IC to come out of its initial unresponsive state, short enough that an idle device doesn't burn the backlight. - Drop the touch defensive cruft added when we were chasing a PoE-power theory: the DEV_MODE=0x00 write at init, the touch_reset_pulse() call at init, and the runtime wedge-self-heal in the polling loop didn't actually fix anything — the wake-on-boot flash did. Also delete the display_touch_reset_pulse() helper since it now has zero callers.
Diffstat (limited to 'main')
-rw-r--r--main/app_main.c13
-rw-r--r--main/display.c16
-rw-r--r--main/display.h5
-rw-r--r--main/http_api.c16
-rw-r--r--main/local_screens.c9
-rw-r--r--main/mdns_service.c13
-rw-r--r--main/nvs_config.c26
-rw-r--r--main/touch.c56
-rw-r--r--main/viewport_state.c20
-rw-r--r--main/viewport_state.h3
10 files changed, 70 insertions, 107 deletions
diff --git a/main/app_main.c b/main/app_main.c
index c411c46..a60496d 100644
--- a/main/app_main.c
+++ b/main/app_main.c
@@ -103,13 +103,16 @@ 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.
+ // Briefly wake the display on boot so the FT5426 touch IC can come
+ // out of its initial unresponsive state — empirically it only
+ // reports contacts when the LCD has been actively streaming with
+ // the backlight on at least once since power-up. A 600 ms flash is
+ // enough; we then sleep so an idle device doesn't burn the
+ // backlight until the idle timer fires.
if (dsp_err == ESP_OK) {
state_machine_set(VIEWPORT_STATE_AWAKE);
+ vTaskDelay(pdMS_TO_TICKS(600));
+ state_machine_set(VIEWPORT_STATE_ASLEEP);
}
vTaskDelete(NULL);
diff --git a/main/display.c b/main/display.c
index 8c5ffb2..a704891 100644
--- a/main/display.c
+++ b/main/display.c
@@ -218,22 +218,6 @@ static esp_err_t touch_release_reset(void)
PC_LED_EN | PC_RST_TP_N | PC_RST_LCD_N | PC_RST_BRIDGE_N);
}
-// Hard-reset the touch IC: drop PC_RST_TP_N for ~20 ms, then release.
-// touch_init() calls this if its dev_mode read keeps returning 0xff —
-// usually unwedges a stuck FT5426 without needing a power cycle.
-esp_err_t display_touch_reset_pulse(void)
-{
- if (!s_panel_mcu) return ESP_ERR_INVALID_STATE;
- // Hold touch in reset (clear PC_RST_TP_N), keep everything else as-is.
- esp_err_t err = mcu_write_u8(REG_PORTC,
- PC_LED_EN | PC_RST_LCD_N | PC_RST_BRIDGE_N);
- if (err != ESP_OK) return err;
- vTaskDelay(pdMS_TO_TICKS(20));
- err = touch_release_reset();
- vTaskDelay(pdMS_TO_TICKS(50));
- return err;
-}
-
// ============================================================================
// TC358762 bridge configuration (DSI Generic Long Write packets)
// Sequence mirrors Linux drivers/gpu/drm/bridge/tc358762.c tc358762_init().
diff --git a/main/display.h b/main/display.h
index cb2b913..9092efb 100644
--- a/main/display.h
+++ b/main/display.h
@@ -27,11 +27,6 @@ esp_err_t display_set_brightness(uint8_t brightness_0_100);
esp_err_t display_sleep(void);
esp_err_t display_wake(void);
-// Pulse the touch IC's reset line (PC_RST_TP_N) via the panel MCU. Used
-// by touch_init() when the FT5426 boots into a wedged state and refuses
-// to leave dev_mode=0xff.
-esp_err_t display_touch_reset_pulse(void);
-
// Blit an RGB565 source image to the panel, applying the current
// orientation. Source dimensions must match the effective resolution:
// portrait -> src is 480x800 (rotated 90° CW into the 800x480 panel)
diff --git a/main/http_api.c b/main/http_api.c
index 028d3a7..2c5ce87 100644
--- a/main/http_api.c
+++ b/main/http_api.c
@@ -39,9 +39,8 @@ static esp_err_t state_get_handler(httpd_req_t *req)
: (int64_t)((now_us - (uint64_t)st->last_frame_us) / 1000);
cJSON *root = cJSON_CreateObject();
- cJSON_AddItemToObject(root, "name",
- st->viewport_name[0] ? cJSON_CreateString(st->viewport_name)
- : cJSON_CreateNull());
+ cJSON_AddStringToObject(root, "name", st->viewport_name);
+ cJSON_AddStringToObject(root, "mac", st->mac_str);
cJSON_AddStringToObject(root, "version", VIEWPORT_VERSION);
cJSON_AddBoolToObject (root, "configured", st->configured);
cJSON_AddStringToObject(root, "state",
@@ -81,9 +80,7 @@ static esp_err_t config_get_handler(httpd_req_t *req)
viewport_state_t *st = viewport_state_get();
cJSON *root = cJSON_CreateObject();
- cJSON_AddItemToObject(root, "viewport",
- st->viewport_name[0] ? cJSON_CreateString(st->viewport_name)
- : cJSON_CreateNull());
+ cJSON_AddStringToObject(root, "viewport", st->viewport_name);
cJSON_AddItemToObject(root, "scrypted",
st->scrypted_url[0] ? cJSON_CreateString(st->scrypted_url)
: cJSON_CreateNull());
@@ -233,8 +230,11 @@ static esp_err_t config_post_handler(httpd_req_t *req)
st->brightness = bright;
}
- // A configured device has both a viewport name and a scrypted URL.
- st->configured = (st->viewport_name[0] && st->scrypted_url[0]);
+ // "Configured" means a scrypted URL has been registered — that's
+ // what gates outbound POSTs and the loading-vs-info screen content
+ // choice. The viewport_name always has a value (MAC-derived default
+ // until POST /config overrides), so it doesn't factor in here.
+ st->configured = (st->scrypted_url[0] != '\0');
viewport_state_unlock();
diff --git a/main/local_screens.c b/main/local_screens.c
index f25754d..5c72f26 100644
--- a/main/local_screens.c
+++ b/main/local_screens.c
@@ -199,6 +199,7 @@ esp_err_t local_screens_show_info(void)
// is on a 3 KiB stack and can't carry ~1.6 KiB of locals here. Single
// caller protected by display ownership, so static is fine.
static char vp_name[64];
+ static char mac_str[18];
static char scrypt[256];
static char lines[INFO_MAX_LINES][INFO_LINE_BYTES];
bool configured;
@@ -212,8 +213,10 @@ esp_err_t local_screens_show_info(void)
viewport_state_lock();
viewport_state_t *st = viewport_state_get();
strncpy(vp_name, st->viewport_name, sizeof(vp_name));
+ strncpy(mac_str, st->mac_str, sizeof(mac_str));
strncpy(scrypt, st->scrypted_url, sizeof(scrypt));
vp_name[sizeof(vp_name) - 1] = '\0';
+ mac_str[sizeof(mac_str) - 1] = '\0';
scrypt[sizeof(scrypt) - 1] = '\0';
configured = st->configured;
state = st->state;
@@ -237,8 +240,7 @@ esp_err_t local_screens_show_info(void)
// 8-char label is prefixed. Long names get visibly truncated; ok for
// an info dump on a 480-wide screen.
char host[32];
- if (vp_name[0]) snprintf(host, sizeof(host), "viewport-%.15s.local", vp_name);
- else snprintf(host, sizeof(host), "viewport.local");
+ snprintf(host, sizeof(host), "viewport-%.15s.local", vp_name);
char scrypt_short[32];
if (!scrypt[0]) {
@@ -292,7 +294,8 @@ esp_err_t local_screens_show_info(void)
snprintf(lines[n_lines++], INFO_LINE_BYTES, fmt, ##__VA_ARGS__); \
} while (0)
- ADD("name %s", vp_name[0] ? vp_name : "unset");
+ ADD("name %s", vp_name);
+ ADD("mac %s", mac_str);
ADD("host %s", host);
ADD("ip %s", (ip && ip[0]) ? ip : "no network");
ADD("state %s", state_str);
diff --git a/main/mdns_service.c b/main/mdns_service.c
index 21cd464..05d8c23 100644
--- a/main/mdns_service.c
+++ b/main/mdns_service.c
@@ -17,6 +17,8 @@ static const uint16_t PORT = 80;
// Pull hostname + TXT values from viewport_state under one lock acquisition,
// then push them at the mDNS service. mdns_service_add() rejects with
// INVALID_ARG if the hostname isn't set, so order matters on first apply.
+// viewport_name is always populated (MAC-derived default) so the hostname
+// is always `viewport-<name>`.
static esp_err_t apply_state(bool include_hostname)
{
char hostname[80];
@@ -25,11 +27,8 @@ static esp_err_t apply_state(bool include_hostname)
viewport_state_lock();
viewport_state_t *st = viewport_state_get();
- snprintf(hostname, sizeof(hostname), "viewport%s%s",
- st->viewport_name[0] ? "-" : "",
- st->viewport_name[0] ? st->viewport_name : "");
- snprintf(name, sizeof(name), "%s",
- st->viewport_name[0] ? st->viewport_name : "");
+ snprintf(hostname, sizeof(hostname), "viewport-%s", st->viewport_name);
+ snprintf(name, sizeof(name), "%s", st->viewport_name);
resolution = viewport_state_resolution_str();
orientation = (st->orientation == VIEWPORT_ORIENTATION_PORTRAIT)
? "portrait" : "landscape";
@@ -53,9 +52,7 @@ esp_err_t mdns_service_start(void)
char hostname[80];
viewport_state_lock();
viewport_state_t *st = viewport_state_get();
- snprintf(hostname, sizeof(hostname), "viewport%s%s",
- st->viewport_name[0] ? "-" : "",
- st->viewport_name[0] ? st->viewport_name : "");
+ snprintf(hostname, sizeof(hostname), "viewport-%s", st->viewport_name);
viewport_state_unlock();
ESP_RETURN_ON_ERROR(mdns_init(), TAG, "mdns_init");
diff --git a/main/nvs_config.c b/main/nvs_config.c
index 7f7586c..8ba2078 100644
--- a/main/nvs_config.c
+++ b/main/nvs_config.c
@@ -58,20 +58,18 @@ esp_err_t nvs_config_load(void)
err = ESP_OK;
- // A device is "configured" only once both name and Scrypted URL are set.
- if (st->viewport_name[0] && st->scrypted_url[0]) {
- st->configured = true;
- st->state = VIEWPORT_STATE_ASLEEP; // configured devices boot asleep
- ESP_LOGI(TAG, "loaded config: viewport=%s scrypted=%s "
- "idle_ms=%u orient=%s bright=%u",
- st->viewport_name, st->scrypted_url,
- (unsigned)st->idle_timeout_ms,
- st->orientation == VIEWPORT_ORIENTATION_LANDSCAPE
- ? "landscape" : "portrait",
- st->brightness);
- } else {
- ESP_LOGI(TAG, "partial config in NVS — viewport or scrypted URL still missing");
- }
+ // "Configured" = a scrypted URL has been registered. viewport_name
+ // always has a value (MAC-derived default seeded in viewport_state_init).
+ st->configured = (st->scrypted_url[0] != '\0');
+ ESP_LOGI(TAG, "loaded config: viewport=%s scrypted=%s "
+ "idle_ms=%u orient=%s bright=%u (%s)",
+ st->viewport_name,
+ st->scrypted_url[0] ? st->scrypted_url : "(none)",
+ (unsigned)st->idle_timeout_ms,
+ st->orientation == VIEWPORT_ORIENTATION_LANDSCAPE
+ ? "landscape" : "portrait",
+ st->brightness,
+ st->configured ? "configured" : "no scrypted URL");
done:
viewport_state_unlock();
diff --git a/main/touch.c b/main/touch.c
index 4615f45..b470536 100644
--- a/main/touch.c
+++ b/main/touch.c
@@ -54,13 +54,6 @@ 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;
@@ -68,35 +61,16 @@ 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 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;
+ // FT5x06 supports max 5 simultaneous points; anything above that
+ // is a bogus reading (chip parking 0xff during a transient).
+ if (touches > 5) continue;
+ uint64_t now = (uint64_t)esp_timer_get_time();
if (touches > 0) {
if (!was_down) {
@@ -141,23 +115,11 @@ esp_err_t touch_init(void)
ESP_RETURN_ON_ERROR(i2c_master_bus_add_device(bus, &cfg, &s_dev),
TAG, "i2c add FT5426");
- // 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);
- }
-
- // 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();
+ // No dev_mode read here: the FT5426 starts reporting contacts only
+ // after the LCD has streamed with the backlight on at least once
+ // (handled by the wake-on-boot flash in app_main). Any read before
+ // that returns 0xff, which the polling loop's >5 sanity filter
+ // already drops.
ESP_LOGI(TAG, "FT5426 polling started "
"(tap=toggle wake/sleep, %dms hold=info overlay)",
LONG_PRESS_MS);
diff --git a/main/viewport_state.c b/main/viewport_state.c
index 7ddf737..a67745a 100644
--- a/main/viewport_state.c
+++ b/main/viewport_state.c
@@ -1,7 +1,9 @@
#include "viewport_state.h"
+#include <stdio.h>
#include <string.h>
+#include "esp_mac.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
@@ -9,6 +11,22 @@
static viewport_state_t s_state;
static SemaphoreHandle_t s_mutex;
+// Populate viewport_name with the full base MAC, colons stripped
+// (e.g. e8:f6:0a:e0:90:94 → "e8f60ae09094"). Stable across reboots,
+// globally unique, and 12 alphanumeric chars — fits comfortably under
+// the 32-char mDNS hostname limit with the "viewport-" prefix.
+// POST /config can override it with a friendlier name.
+static void seed_mac_and_name(char *mac_str, size_t mac_cap,
+ char *name, size_t name_cap)
+{
+ uint8_t mac[6] = {0};
+ esp_read_mac(mac, ESP_MAC_BASE);
+ snprintf(mac_str, mac_cap, "%02x:%02x:%02x:%02x:%02x:%02x",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ snprintf(name, name_cap, "%02x%02x%02x%02x%02x%02x",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+}
+
void viewport_state_init(void)
{
memset(&s_state, 0, sizeof(s_state));
@@ -19,6 +37,8 @@ void viewport_state_init(void)
s_state.orientation = VIEWPORT_ORIENTATION_PORTRAIT;
s_state.boot_us = (uint64_t)esp_timer_get_time();
s_state.last_frame_us = -1;
+ seed_mac_and_name(s_state.mac_str, sizeof(s_state.mac_str),
+ s_state.viewport_name, sizeof(s_state.viewport_name));
s_mutex = xSemaphoreCreateMutex();
}
diff --git a/main/viewport_state.h b/main/viewport_state.h
index 4e6fbd6..264d07d 100644
--- a/main/viewport_state.h
+++ b/main/viewport_state.h
@@ -20,7 +20,8 @@ typedef enum {
// Read-mostly snapshot of device state used by /state. Counters are
// updated under a mutex by the modules that own them.
typedef struct {
- char viewport_name[64]; // empty before /config
+ char mac_str[18]; // "aa:bb:cc:dd:ee:ff" — populated at init
+ char viewport_name[64]; // MAC-derived default; can be overridden via /config
char scrypted_url[256]; // empty before /config
bool configured;
viewport_run_state_t state;