src.nth.io/

summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorLuke Hoersten <[email protected]>2026-06-14 16:41:03 -0500
committerLuke Hoersten <[email protected]>2026-06-14 16:41:03 -0500
commitc6ce4b3f46a1f1adf700306dadc9034a6a2ac0d3 (patch)
tree3bd6f0a3c2eea8b0095bd05549cae650fe166b40 /main
parent6c1a26bdae63d152caa45023e8255561a22fc0e2 (diff)
M3 done: drop boot test, fold BOOT button into touch long-press
The Waveshare ESP32-P4-ETH board exposes BOOT on GPIO 35, but at runtime that pin is owned by the EMAC TXD1 signal. No usable GPIO is wired to a separate user button, so the BOOT-button task could never fire. Move both BOOT-button behaviours onto the touch panel: - ≥1.5s hold → 15s identity overlay (was BOOT short-press) - ≥5s hold → factory reset (was BOOT long-hold) Short tap (<500ms) still toggles wake/sleep. Long-press fires while the finger is still down so the user gets immediate feedback at each threshold. Also strip the R/G/B 6 s boot test sequence — the panel now renders correctly, so it's no longer useful diagnostically. Boot subsystem flags drop the trailing B column: [EMHDJT].
Diffstat (limited to 'main')
-rw-r--r--main/app_main.c19
-rw-r--r--main/button.c96
-rw-r--r--main/button.h8
-rw-r--r--main/local_screens.h5
-rw-r--r--main/touch.c103
5 files changed, 66 insertions, 165 deletions
diff --git a/main/app_main.c b/main/app_main.c
index cc4c063..2cb395e 100644
--- a/main/app_main.c
+++ b/main/app_main.c
@@ -1,4 +1,3 @@
-#include "button.h"
#include "display.h"
#include "http_api.h"
#include "jpeg_decoder.h"
@@ -47,7 +46,7 @@ void app_main(void)
// don't get a DHCP lease. mDNS + HTTP advertise / bind anyway and will
// start serving the moment the link comes up.
// ------------------------------------------------------------------
- char flags[8] = { '-','-','-','-','-','-','-', 0 }; // E M H D J T B
+ char flags[7] = { '-','-','-','-','-','-', 0 }; // E M H D J T
esp_err_t eth_err = net_eth_init();
mark(eth_err, 'E', &flags[0]);
@@ -73,10 +72,10 @@ void app_main(void)
// config is even slightly off; keeping that off the main task means
// a misconfigured panel can't take down networking + /state. The
// task also brings up the JPEG decoder and touch (touch shares the
- // panel I²C bus).
+ // panel I²C bus and also handles long-press → identity overlay and
+ // very-long-press → factory reset, since the board's BOOT button is
+ // wired to a strap pin (GPIO35) that the EMAC owns at runtime).
// ------------------------------------------------------------------
- mark(button_init(), 'B', &flags[6]);
-
ESP_LOGI(TAG, "boot complete — net subsystems [%s] ip=%s; "
"display init deferred to dsp_init task",
flags, got_ip ? net_eth_get_ip_str() : "(no link)");
@@ -92,16 +91,6 @@ static void display_setup_task(void *arg)
if (dsp_err == ESP_OK) {
local_screens_init();
- // Show solid-color test sequence for ~6 s so we can characterize the
- // garble: solid red, then solid green, then solid blue, each for 2s.
- // If the panel paints a single uniform color (or close to it) for
- // each, the issue is the text rendering only. If even solid colors
- // are garbled, it's a stride / pixel-format bug deeper down.
- display_wake();
- display_fill(0xF800); vTaskDelay(pdMS_TO_TICKS(2000)); // red
- display_fill(0x07E0); vTaskDelay(pdMS_TO_TICKS(2000)); // green
- display_fill(0x001F); vTaskDelay(pdMS_TO_TICKS(2000)); // blue
- display_sleep();
}
mark(jpeg_decoder_init(), 'J', &flags[1]);
diff --git a/main/button.c b/main/button.c
deleted file mode 100644
index 26e7cff..0000000
--- a/main/button.c
+++ /dev/null
@@ -1,96 +0,0 @@
-#include "button.h"
-
-#include "driver/gpio.h"
-#include "esp_log.h"
-#include "esp_system.h"
-#include "esp_timer.h"
-#include "freertos/FreeRTOS.h"
-#include "freertos/task.h"
-
-#include "display.h"
-#include "local_screens.h"
-#include "nvs_config.h"
-
-static const char *TAG = "button";
-
-// TODO confirm against the Waveshare ESP32-P4-ETH schematic. ESP32-P4's
-// strapping pin is GPIO35 but that's owned by RMII TXD1 at runtime. Most
-// Waveshare ESP32-P4 boards expose an additional user/BOOT button on a
-// free GPIO; GPIO0 is the conventional default if a separate button isn't
-// available. The button task fail-soft: if the pin reads stuck-high (no
-// button), it just never fires.
-#define PIN_BOOT_BUTTON 0
-
-#define POLL_PERIOD_MS 30
-#define LONG_HOLD_MS 5000
-#define OVERLAY_MS 15000
-
-static void start_overlay(void)
-{
- if (!display_is_up()) return;
- local_screens_overlay(OVERLAY_MS);
- ESP_LOGI(TAG, "BOOT short-press → identity overlay for %dms", OVERLAY_MS);
-}
-
-static void factory_reset(void)
-{
- ESP_LOGW(TAG, "BOOT held %dms → factory reset", LONG_HOLD_MS);
- nvs_config_reset();
- vTaskDelay(pdMS_TO_TICKS(200)); // let log flush
- esp_restart();
-}
-
-static void button_task(void *arg)
-{
- bool was_pressed = false;
- uint64_t press_started = 0;
- bool long_fired = false;
-
- for (;;) {
- vTaskDelay(pdMS_TO_TICKS(POLL_PERIOD_MS));
-
- bool pressed = (gpio_get_level(PIN_BOOT_BUTTON) == 0); // active-low
-
- if (pressed && !was_pressed) {
- press_started = (uint64_t)esp_timer_get_time();
- long_fired = false;
- was_pressed = true;
- } else if (pressed && was_pressed) {
- uint64_t held_ms = ((uint64_t)esp_timer_get_time() - press_started) / 1000ULL;
- if (!long_fired && held_ms >= LONG_HOLD_MS) {
- long_fired = true;
- factory_reset(); // does not return
- }
- } else if (!pressed && was_pressed) {
- uint64_t held_ms = ((uint64_t)esp_timer_get_time() - press_started) / 1000ULL;
- was_pressed = false;
- if (!long_fired && held_ms < LONG_HOLD_MS) {
- start_overlay();
- }
- }
- }
-}
-
-esp_err_t button_init(void)
-{
- gpio_config_t cfg = {
- .pin_bit_mask = 1ULL << PIN_BOOT_BUTTON,
- .mode = GPIO_MODE_INPUT,
- .pull_up_en = GPIO_PULLUP_ENABLE,
- .pull_down_en = GPIO_PULLDOWN_DISABLE,
- .intr_type = GPIO_INTR_DISABLE,
- };
- esp_err_t err = gpio_config(&cfg);
- if (err != ESP_OK) {
- ESP_LOGW(TAG, "gpio_config GPIO%d failed: %s — button disabled",
- PIN_BOOT_BUTTON, esp_err_to_name(err));
- return err;
- }
-
- BaseType_t ok = xTaskCreate(button_task, "boot_btn", 3072, NULL, 3, NULL);
- if (ok != pdPASS) return ESP_FAIL;
-
- ESP_LOGI(TAG, "BOOT button on GPIO%d (short=IP overlay 15s, hold 5s=factory reset)",
- PIN_BOOT_BUTTON);
- return ESP_OK;
-}
diff --git a/main/button.h b/main/button.h
deleted file mode 100644
index a6d1c80..0000000
--- a/main/button.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#pragma once
-
-#include "esp_err.h"
-
-// BOOT button polling task. Pin is hard-coded in button.c and may need
-// to be adjusted for the Waveshare ESP32-P4-ETH (see TODO in button.c).
-// Returns OK if the task started; the firmware keeps running either way.
-esp_err_t button_init(void);
diff --git a/main/local_screens.h b/main/local_screens.h
index f1e4876..e2d6ca1 100644
--- a/main/local_screens.h
+++ b/main/local_screens.h
@@ -15,7 +15,7 @@ esp_err_t local_screens_init(void);
// <current IP> ("no network" if no DHCP lease)
// <state> ("awake" / "asleep" / "unconfigured")
// Font scale is auto-picked to fit the longest line within 90% of width.
-// Shown on first boot, after factory reset, and as a 15s BOOT-button overlay.
+// Shown on first boot, after factory reset, and as a 15s touch-long-press overlay.
esp_err_t local_screens_show_ip(void);
// Render the loading screen — centered "Loading…" — shown on every wake
@@ -23,8 +23,7 @@ esp_err_t local_screens_show_ip(void);
esp_err_t local_screens_show_loading(void);
// Show the identity ("who am I") screen for `duration_ms`, then drop the
-// backlight off. Used by the BOOT button short-press (any state) and by
-// the touch handler when the device is unconfigured.
+// backlight off. Triggered by a ≥1.5s touch long-press in any state.
esp_err_t local_screens_overlay(uint32_t duration_ms);
// True while the identity overlay is currently shown (timer armed).
diff --git a/main/touch.c b/main/touch.c
index 4d8003f..6b4e035 100644
--- a/main/touch.c
+++ b/main/touch.c
@@ -5,12 +5,14 @@
#include "driver/i2c_master.h"
#include "esp_check.h"
#include "esp_log.h"
+#include "esp_system.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "display.h"
#include "local_screens.h"
+#include "nvs_config.h"
#include "state_machine.h"
#include "viewport_state.h"
@@ -19,18 +21,15 @@ static const char *TAG = "touch";
#define FT5426_ADDR 0x38
#define FT5426_I2C_FREQ_HZ 400000
-// FT5x06/FT5426 register map (subset).
#define FT_REG_DEV_MODE 0x00
-#define FT_REG_GEST_ID 0x01
-#define FT_REG_TD_STATUS 0x02 // low 4 bits = touch count
-#define FT_REG_P1_XH 0x03 // X high (bits 0..3) + event flag (bits 6..7)
-#define FT_REG_P1_XL 0x04
-#define FT_REG_P1_YH 0x05
-#define FT_REG_P1_YL 0x06
+#define FT_REG_TD_STATUS 0x02
+#define FT_REG_P1_XH 0x03
#define POLL_PERIOD_MS 30
-#define TAP_MAX_MS 500
-#define TAP_MAX_MOVE_PX 25
+#define TAP_MAX_MS 500 // < this on release = short tap (toggle)
+#define LONG_PRESS_MS 1500 // ≥ this while held = identity overlay
+#define FACTORY_RESET_MS 5000 // ≥ this while held = factory reset
+#define OVERLAY_MS 15000
#define TAP_DEBOUNCE_MS 150
static i2c_master_dev_handle_t s_dev;
@@ -41,12 +40,37 @@ static esp_err_t ft_read(uint8_t reg, uint8_t *buf, size_t n)
return i2c_master_transmit_receive(s_dev, &reg, 1, buf, n, 50);
}
+static void on_short_tap(void)
+{
+ if (local_screens_overlay_active()) local_screens_overlay_dismiss();
+ viewport_run_state_t target =
+ (state_machine_current() == VIEWPORT_STATE_AWAKE)
+ ? VIEWPORT_STATE_ASLEEP
+ : VIEWPORT_STATE_AWAKE;
+ ESP_LOGI(TAG, "tap -> %s", target == VIEWPORT_STATE_AWAKE ? "wake" : "sleep");
+ state_machine_set_local(target);
+}
+
+static void on_long_press(void)
+{
+ ESP_LOGI(TAG, "long-press → identity overlay for %dms", OVERLAY_MS);
+ if (display_is_up()) local_screens_overlay(OVERLAY_MS);
+}
+
+static void on_factory_reset(void)
+{
+ ESP_LOGW(TAG, "very-long-press %dms → factory reset", FACTORY_RESET_MS);
+ nvs_config_reset();
+ vTaskDelay(pdMS_TO_TICKS(200));
+ esp_restart();
+}
+
static void touch_task(void *arg)
{
bool was_down = false;
uint64_t down_us = 0;
- uint16_t down_x = 0;
- uint16_t down_y = 0;
+ bool long_fired = false;
+ bool reset_fired = false;
uint64_t last_tap_us = 0;
uint8_t buf[7];
@@ -54,43 +78,36 @@ static void touch_task(void *arg)
vTaskDelay(pdMS_TO_TICKS(POLL_PERIOD_MS));
if (ft_read(FT_REG_DEV_MODE, buf, sizeof(buf)) != ESP_OK) continue;
-
uint8_t touches = buf[FT_REG_TD_STATUS] & 0x0F;
+ uint64_t now = (uint64_t)esp_timer_get_time();
+
if (touches > 0) {
- // Always capture P1; we only care about taps, not multi-touch.
- uint16_t x = ((buf[FT_REG_P1_XH] & 0x0F) << 8) | buf[FT_REG_P1_XL];
- uint16_t y = ((buf[FT_REG_P1_YH] & 0x0F) << 8) | buf[FT_REG_P1_YL];
if (!was_down) {
- was_down = true;
- down_us = (uint64_t)esp_timer_get_time();
- down_x = x;
- down_y = y;
+ was_down = true;
+ down_us = now;
+ long_fired = false;
+ reset_fired = false;
+ } else {
+ uint64_t held_ms = (now - down_us) / 1000ULL;
+ if (!long_fired && held_ms >= LONG_PRESS_MS) {
+ long_fired = true;
+ on_long_press();
+ }
+ if (!reset_fired && held_ms >= FACTORY_RESET_MS) {
+ reset_fired = true;
+ on_factory_reset(); // does not return
+ }
}
} else if (was_down) {
- // Release: was-down → up.
was_down = false;
- uint64_t now = (uint64_t)esp_timer_get_time();
-
+ uint64_t held_ms = (now - down_us) / 1000ULL;
if (now - last_tap_us < (uint64_t)TAP_DEBOUNCE_MS * 1000ULL) continue;
- uint64_t dt_ms = (now - down_us) / 1000ULL;
- // No reliable last position from the burst; use down coordinates
- // for the duration check, ignore movement check beyond the down
- // capture. Good enough for tap-vs-press.
- (void)down_x; (void)down_y; (void)TAP_MAX_MOVE_PX;
-
- if (dt_ms > 0 && dt_ms <= TAP_MAX_MS) {
+ // Short tap only fires on release; long-press already fired
+ // while the finger was still down.
+ if (!long_fired && held_ms > 0 && held_ms <= TAP_MAX_MS) {
last_tap_us = now;
- // Cancel any BOOT-button identity overlay so a tap-to-sleep
- // doesn't fight the overlay timer trying to restore content.
- if (local_screens_overlay_active()) local_screens_overlay_dismiss();
- viewport_run_state_t target =
- (state_machine_current() == VIEWPORT_STATE_AWAKE)
- ? VIEWPORT_STATE_ASLEEP
- : VIEWPORT_STATE_AWAKE;
- ESP_LOGI(TAG, "tap (%lu ms) -> %s", (unsigned long)dt_ms,
- target == VIEWPORT_STATE_AWAKE ? "wake" : "sleep");
- state_machine_set_local(target);
+ on_short_tap();
}
}
}
@@ -112,15 +129,15 @@ esp_err_t touch_init(void)
ESP_RETURN_ON_ERROR(i2c_master_bus_add_device(bus, &cfg, &s_dev),
TAG, "i2c add FT5426");
- // Sanity poll: read device mode.
uint8_t dev_mode = 0;
esp_err_t err = ft_read(FT_REG_DEV_MODE, &dev_mode, 1);
if (err != ESP_OK) {
- ESP_LOGE(TAG, "FT5426 @0x38 unreachable (%s) — check INT jumper",
- esp_err_to_name(err));
+ ESP_LOGE(TAG, "FT5426 @0x38 unreachable (%s)", esp_err_to_name(err));
return err;
}
- ESP_LOGI(TAG, "FT5426 ack'd (dev_mode=0x%02x)", dev_mode);
+ ESP_LOGI(TAG, "FT5426 ack'd (dev_mode=0x%02x); "
+ "tap=toggle, %dms hold=ID overlay, %dms hold=factory reset",
+ dev_mode, LONG_PRESS_MS, FACTORY_RESET_MS);
BaseType_t ok = xTaskCreate(touch_task, "touch", 3072, NULL, 4, &s_task);
return (ok == pdPASS) ? ESP_OK : ESP_FAIL;