diff options
| author | Luke Hoersten <[email protected]> | 2026-06-14 16:41:03 -0500 |
|---|---|---|
| committer | Luke Hoersten <[email protected]> | 2026-06-14 16:41:03 -0500 |
| commit | c6ce4b3f46a1f1adf700306dadc9034a6a2ac0d3 (patch) | |
| tree | 3bd6f0a3c2eea8b0095bd05549cae650fe166b40 /main | |
| parent | 6c1a26bdae63d152caa45023e8255561a22fc0e2 (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.c | 19 | ||||
| -rw-r--r-- | main/button.c | 96 | ||||
| -rw-r--r-- | main/button.h | 8 | ||||
| -rw-r--r-- | main/local_screens.h | 5 | ||||
| -rw-r--r-- | main/touch.c | 103 |
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, ®, 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; |
