A knob turn still wakes the chip (the RTC can't tell gestures apart), but the firmware now stays dark and checks the button: held while turning it boots at once; otherwise the ring glows for 2.5 s waiting for a click and goes back to deep sleep if none comes. Screen and Wi-Fi never start for an accidental nudge.
90 lines
3.1 KiB
C
90 lines
3.1 KiB
C
#include "power.h"
|
|
#include "board.h"
|
|
#include "display.h"
|
|
#include "leds.h"
|
|
#include "ui.h"
|
|
#include "driver/gpio.h"
|
|
#include "driver/rtc_io.h"
|
|
#include "esp_log.h"
|
|
#include "esp_sleep.h"
|
|
#include "esp_timer.h"
|
|
#include "esp_wifi.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
|
|
static const char *TAG = "power";
|
|
|
|
// Arm ext1 wake on the encoder pins. The S3 has one level mode for all selected
|
|
// pins, so pick it from the knob's resting state: pins that are high wake on
|
|
// low; if both rest low, wake on high. A single detent toggles both pins.
|
|
static void arm_knob_wakeup(void)
|
|
{
|
|
int pins[2] = {board->enc_a, board->enc_b};
|
|
uint64_t high = 0, low = 0;
|
|
for (int i = 0; i < 2; i++) {
|
|
rtc_gpio_init(pins[i]);
|
|
rtc_gpio_set_direction(pins[i], RTC_GPIO_MODE_INPUT_ONLY);
|
|
rtc_gpio_pulldown_dis(pins[i]);
|
|
rtc_gpio_pullup_en(pins[i]);
|
|
}
|
|
vTaskDelay(pdMS_TO_TICKS(5));
|
|
for (int i = 0; i < 2; i++) {
|
|
if (rtc_gpio_get_level(pins[i])) high |= 1ULL << pins[i];
|
|
else low |= 1ULL << pins[i];
|
|
}
|
|
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); // keeps the RTC pull-ups alive
|
|
if (high) esp_sleep_enable_ext1_wakeup_io(high, ESP_EXT1_WAKEUP_ANY_LOW);
|
|
else esp_sleep_enable_ext1_wakeup_io(low, ESP_EXT1_WAKEUP_ANY_HIGH);
|
|
ESP_LOGI(TAG, "wake on knob: A%d=%d B%d=%d", board->enc_a, !!(high & (1ULL << board->enc_a)),
|
|
board->enc_b, !!(high & (1ULL << board->enc_b)));
|
|
}
|
|
|
|
void power_off(void)
|
|
{
|
|
ESP_LOGI(TAG, "powering off");
|
|
ui_status("Off", "turn the knob to wake");
|
|
vTaskDelay(pdMS_TO_TICKS(700));
|
|
leds_set_mode(LED_OFF, 0);
|
|
vTaskDelay(pdMS_TO_TICKS(80)); // let the ring task push the dark frame
|
|
display_off();
|
|
esp_wifi_stop();
|
|
gpio_set_level(board->pwr_en, 0); // peripheral rail off (screen, ring, codec)
|
|
arm_knob_wakeup();
|
|
esp_deep_sleep_start();
|
|
}
|
|
|
|
#define CONFIRM_WINDOW_MS 2500
|
|
|
|
void power_confirm_wake(void)
|
|
{
|
|
if (esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_EXT1) return;
|
|
gpio_config_t btn = {.pin_bit_mask = 1ULL << board->enc_btn, .mode = GPIO_MODE_INPUT, .pull_up_en = 1};
|
|
gpio_config(&btn);
|
|
if (!gpio_get_level(board->enc_btn)) {
|
|
ESP_LOGI(TAG, "wake confirmed: button held while turning");
|
|
return;
|
|
}
|
|
leds_set_mode(LED_BREATHE, 0x302838); // soft hint: "click to wake"
|
|
int64_t deadline = esp_timer_get_time() / 1000 + CONFIRM_WINDOW_MS;
|
|
while (esp_timer_get_time() / 1000 < deadline) {
|
|
if (!gpio_get_level(board->enc_btn)) {
|
|
ESP_LOGI(TAG, "wake confirmed: click");
|
|
return;
|
|
}
|
|
vTaskDelay(pdMS_TO_TICKS(20));
|
|
}
|
|
ESP_LOGI(TAG, "knob nudge without the button; back to sleep");
|
|
leds_set_mode(LED_OFF, 0);
|
|
vTaskDelay(pdMS_TO_TICKS(80));
|
|
gpio_set_level(board->pwr_en, 0);
|
|
arm_knob_wakeup();
|
|
esp_deep_sleep_start();
|
|
}
|
|
|
|
void power_report_wakeup(void)
|
|
{
|
|
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
|
|
if (cause == ESP_SLEEP_WAKEUP_EXT1) ESP_LOGI(TAG, "woken by the knob");
|
|
else if (cause != ESP_SLEEP_WAKEUP_UNDEFINED) ESP_LOGI(TAG, "wakeup cause %d", cause);
|
|
}
|