src.nth.io/

summaryrefslogtreecommitdiff
path: root/main/button.c
blob: 26e7cff261c5f89b93ebc3350ddb31d2eed2034a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#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;
}