#include "driver/gpio.h" #include "esp_log.h" #include "esp_sleep.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "wifi_provisioning.h" #include "combo_button.h" static const char *TAG = "combo_button"; #if CONFIG_FRAME_COMBO_BUTTON_GPIO >= 0 #define COMBO_BUTTON_GPIO ((gpio_num_t)CONFIG_FRAME_COMBO_BUTTON_GPIO) #define COMBO_BUTTON_POLL_MS 100 void combo_button_init(void) { /* See next_button.c's next_button_init() for why this has to run * before gpio_config() -- a deep sleep with this pin armed as a * wakeup source leaves it "held," and nothing un-holds it on wake * except explicitly asking. */ gpio_hold_dis(COMBO_BUTTON_GPIO); gpio_config_t io_conf = { .pin_bit_mask = 1ULL << COMBO_BUTTON_GPIO, .mode = GPIO_MODE_INPUT, .pull_up_en = GPIO_PULLUP_ENABLE, }; gpio_config(&io_conf); /* See next_button.c for why this API (not ext1) -- it manages the * pull resistor across the sleep transition itself, so the pin * doesn't float and wake the device spuriously. */ esp_sleep_enable_gpio_wakeup_on_hp_periph_powerdown(1ULL << COMBO_BUTTON_GPIO, ESP_GPIO_WAKEUP_GPIO_LOW); } bool combo_button_check(void) { /* A quick tap can easily release before we get here (~tens to * hundreds of ms into boot, same issue the other buttons already * hit and fixed -- confirmed on hardware: a fast press was being * missed entirely with just a live level check here). The wakeup * status register is latched at the moment of waking and isn't * cleared until the next sleep entry, so it still confirms this pin * caused the wake even if it's since been released -- in which case * the poll loop below simply measures 0ms held, correctly resolving * to a quick press rather than "not pressed at all." */ bool caused_wake = esp_sleep_get_gpio_wakeup_status() & (1ULL << COMBO_BUTTON_GPIO); if (!caused_wake && gpio_get_level(COMBO_BUTTON_GPIO) != 0) { return false; /* not pressed, and didn't cause this wake either */ } ESP_LOGI(TAG, "Combo button held -- quick press for menu, %dms for soft reset, %dms for factory reset", CONFIG_FRAME_COMBO_SOFT_RESET_HOLD_MS, CONFIG_FRAME_COMBO_FACTORY_RESET_HOLD_MS); int elapsed_ms = 0; while (gpio_get_level(COMBO_BUTTON_GPIO) == 0) { vTaskDelay(pdMS_TO_TICKS(COMBO_BUTTON_POLL_MS)); elapsed_ms += COMBO_BUTTON_POLL_MS; if (elapsed_ms >= CONFIG_FRAME_COMBO_FACTORY_RESET_HOLD_MS) { /* Fires immediately, doesn't wait for release -- same * convention as the old dedicated reset button. */ ESP_LOGW(TAG, "Held %dms, clearing config and restarting into provisioning", CONFIG_FRAME_COMBO_FACTORY_RESET_HOLD_MS); frame_config_clear(); esp_restart(); } } if (elapsed_ms >= CONFIG_FRAME_COMBO_SOFT_RESET_HOLD_MS) { ESP_LOGW(TAG, "Held %dms and released, soft-restarting (config kept)", elapsed_ms); esp_restart(); } ESP_LOGI(TAG, "Quick press (%dms), showing management menu", elapsed_ms); return true; } bool combo_button_is_pressed(void) { return gpio_get_level(COMBO_BUTTON_GPIO) == 0; } #else void combo_button_init(void) {} bool combo_button_check(void) { return false; } bool combo_button_is_pressed(void) { return false; } #endif