#include #include #include #include #include #include #include #include #include #include #include "eos.h" #include "net.h" #include "cell.h" #include "power.h" #define POWER_GPIO_BTN 0 #define POWER_GPIO_NET 5 #define POWER_GPIO_UART 35 #define POWER_ETYPE_BTN 1 #define POWER_ETYPE_SLEEP1 2 #define POWER_ETYPE_SLEEP2 3 #define POWER_ETYPE_WAKE 4 #define POWER_ETYPE_NETRDY 5 typedef struct { uint8_t type; union { uint8_t source; uint8_t level; }; } power_event_t; static esp_timer_handle_t timer; static esp_pm_lock_handle_t power_lock_cpu_freq; static esp_pm_lock_handle_t power_lock_apb_freq; static esp_pm_lock_handle_t power_lock_no_sleep; static const char *TAG = "EOS POWER"; static QueueHandle_t power_queue; static volatile int init_done = 0; static volatile int wake_done = 0; static void IRAM_ATTR btn_handler(void *arg) { power_event_t evt; evt.type = POWER_ETYPE_BTN; evt.level = gpio_get_level(POWER_GPIO_BTN); xQueueSendFromISR(power_queue, &evt, NULL); } static void IRAM_ATTR btn_wake_handler(void *arg) { power_event_t evt; gpio_intr_disable(POWER_GPIO_BTN); evt.type = POWER_ETYPE_WAKE; evt.source = EOS_PWR_WAKE_BTN; xQueueSendFromISR(power_queue, &evt, NULL); } static void IRAM_ATTR net_wake_handler(void *arg) { power_event_t evt; gpio_intr_disable(POWER_GPIO_NET); evt.type = POWER_ETYPE_WAKE; evt.source = EOS_PWR_WAKE_NET; xQueueSendFromISR(power_queue, &evt, NULL); } static void IRAM_ATTR uart_wake_handler(void *arg) { power_event_t evt; gpio_intr_disable(POWER_GPIO_UART); evt.type = POWER_ETYPE_WAKE; evt.source = EOS_PWR_WAKE_UART; xQueueSendFromISR(power_queue, &evt, NULL); } static void timer_handler(void *arg) { power_event_t evt; evt.type = POWER_ETYPE_SLEEP2; xQueueSendFromISR(power_queue, &evt, NULL); } void power_sleep_stage1(void) { gpio_config_t io_conf = {}; eos_modem_sleep(); eos_net_sleep(); io_conf.intr_type = GPIO_INTR_DISABLE; io_conf.mode = GPIO_MODE_INPUT; io_conf.pin_bit_mask = ((uint64_t)1 << POWER_GPIO_NET); io_conf.pull_up_en = 0; io_conf.pull_down_en = 0; gpio_config(&io_conf); gpio_isr_handler_add(POWER_GPIO_BTN, btn_wake_handler, NULL); gpio_isr_handler_add(POWER_GPIO_NET, net_wake_handler, NULL); gpio_isr_handler_add(POWER_GPIO_UART, uart_wake_handler, NULL); esp_sleep_enable_gpio_wakeup(); gpio_wakeup_enable(POWER_GPIO_BTN, GPIO_INTR_LOW_LEVEL); gpio_wakeup_enable(POWER_GPIO_NET, GPIO_INTR_LOW_LEVEL); gpio_wakeup_enable(POWER_GPIO_UART, GPIO_INTR_LOW_LEVEL); esp_timer_start_once(timer, 10 * 1000000); esp_pm_lock_release(power_lock_cpu_freq); esp_pm_lock_release(power_lock_apb_freq); esp_pm_lock_release(power_lock_no_sleep); } void power_sleep_stage2(uint8_t mode) { switch (mode) { case EOS_PWR_SMODE_LIGHT: { ESP_LOGI(TAG, "LIGHT SLEEP"); esp_light_sleep_start(); break; } case EOS_PWR_SMODE_DEEP: { esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); gpio_deep_sleep_hold_en(); esp_sleep_enable_ext0_wakeup(POWER_GPIO_BTN, 0); // esp_sleep_enable_ext1_wakeup((uint64_t)1 << POWER_GPIO_UART, ESP_EXT1_WAKEUP_ALL_LOW); ESP_LOGI(TAG, "DEEP SLEEP"); eos_modem_deep_sleep(); esp_deep_sleep_start(); break; } } } void power_wake_stage1(uint8_t source, uint8_t mode) { if (mode == EOS_PWR_SMODE_LIGHT) { gpio_config_t io_conf = {}; esp_pm_lock_acquire(power_lock_cpu_freq); esp_pm_lock_acquire(power_lock_apb_freq); esp_pm_lock_acquire(power_lock_no_sleep); gpio_wakeup_disable(POWER_GPIO_BTN); gpio_wakeup_disable(POWER_GPIO_NET); gpio_wakeup_disable(POWER_GPIO_UART); esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); gpio_isr_handler_remove(POWER_GPIO_NET); io_conf.intr_type = GPIO_INTR_DISABLE; io_conf.mode = GPIO_MODE_DISABLE; io_conf.pin_bit_mask = ((uint64_t)1 << POWER_GPIO_NET); io_conf.pull_up_en = 0; io_conf.pull_down_en = 0; gpio_config(&io_conf); } gpio_intr_disable(POWER_GPIO_BTN); if ((source != EOS_PWR_WAKE_BTN) && (source != EOS_PWR_WAKE_NET)) { gpio_set_level(POWER_GPIO_BTN, 0); gpio_set_direction(POWER_GPIO_BTN, GPIO_MODE_OUTPUT); vTaskDelay(200 / portTICK_PERIOD_MS); gpio_set_direction(POWER_GPIO_BTN, GPIO_MODE_INPUT); } eos_net_wake(source, mode); } void power_wake_stage2(uint8_t source, uint8_t mode) { eos_modem_wake(source, mode); gpio_set_intr_type(POWER_GPIO_BTN, GPIO_INTR_ANYEDGE); gpio_isr_handler_add(POWER_GPIO_BTN, btn_handler, NULL); ESP_LOGI(TAG, "WAKE"); wake_done = 1; } static void power_event_task(void *pvParameters) { power_event_t evt; uint8_t source; uint8_t wakeup_cause; uint8_t mode; int sleep1, sleep2; mode = 0; source = 0; wakeup_cause = eos_power_wakeup_cause(); if (wakeup_cause != EOS_PWR_WAKE_RST) { mode = EOS_PWR_SMODE_DEEP; sleep1 = 1; } else { sleep1 = 0; } sleep2 = 0; while (1) { if (xQueueReceive(power_queue, &evt, portMAX_DELAY)) { switch (evt.type) { case POWER_ETYPE_SLEEP1: { if (!sleep1) { mode = EOS_PWR_SMODE_LIGHT; power_sleep_stage1(); sleep1 = 1; sleep2 = 1; } break; } case POWER_ETYPE_SLEEP2: { if (sleep2) { mode = EOS_PWR_SMODE_DEEP; power_sleep_stage2(mode); sleep2 = 0; } break; } case POWER_ETYPE_WAKE: { if (sleep1) { source = evt.source; power_wake_stage1(source, mode); if (sleep2) { esp_timer_stop(timer); sleep2 = 0; } } break; } case POWER_ETYPE_NETRDY: { power_wake_stage2(source, mode); sleep1 = 0; source = 0; break; } case POWER_ETYPE_BTN: { unsigned char *buf; buf = eos_net_alloc(); buf[0] = EOS_PWR_MTYPE_BUTTON; buf[1] = evt.level; eos_net_send(EOS_NET_MTYPE_POWER, buf, 2); break; } default: break; } } } vTaskDelete(NULL); } void eos_power_init(void) { esp_err_t ret; gpio_config_t io_conf = {}; esp_timer_create_args_t timer_args = {}; esp_pm_config_esp32_t pwr_conf; uint8_t wakeup_cause; io_conf.intr_type = GPIO_INTR_ANYEDGE; io_conf.mode = GPIO_MODE_INPUT; io_conf.pin_bit_mask = ((uint64_t)1 << POWER_GPIO_BTN); io_conf.pull_up_en = 1; io_conf.pull_down_en = 0; gpio_config(&io_conf); gpio_isr_handler_add(POWER_GPIO_BTN, btn_handler, NULL); timer_args.callback = timer_handler, timer_args.arg = NULL; timer_args.name = "sleep"; ret = esp_timer_create(&timer_args, &timer); assert(ret == ESP_OK); /* ret = esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); assert(ret == ESP_OK); ret = esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_ON); assert(ret == ESP_OK); ret = esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON); assert(ret == ESP_OK); */ ret = esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, NULL, &power_lock_cpu_freq); assert(ret == ESP_OK); ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, NULL, &power_lock_apb_freq); assert(ret == ESP_OK); ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, NULL, &power_lock_no_sleep); assert(ret == ESP_OK); ret = esp_pm_lock_acquire(power_lock_cpu_freq); assert(ret == ESP_OK); ret = esp_pm_lock_acquire(power_lock_apb_freq); assert(ret == ESP_OK); ret = esp_pm_lock_acquire(power_lock_no_sleep); assert(ret == ESP_OK); pwr_conf.max_freq_mhz = 160; pwr_conf.min_freq_mhz = 80; pwr_conf.light_sleep_enable = 1; ret = esp_pm_configure(&pwr_conf); assert(ret == ESP_OK); power_queue = xQueueCreate(4, sizeof(power_event_t)); xTaskCreate(power_event_task, "power_event", EOS_TASK_SSIZE_PWR, NULL, EOS_TASK_PRIORITY_PWR, NULL); wakeup_cause = eos_power_wakeup_cause(); if (wakeup_cause != EOS_PWR_WAKE_RST) { eos_power_wake(wakeup_cause); } init_done = 1; ESP_LOGI(TAG, "INIT"); } void eos_power_wait4init(void) { while (!init_done); } void eos_power_wait4wake(void) { if (eos_power_wakeup_cause() == EOS_PWR_WAKE_RST) return; while (!wake_done); } uint8_t eos_power_wakeup_cause(void) { esp_reset_reason_t reset_cause = esp_reset_reason(); esp_sleep_wakeup_cause_t wakeup_cause = esp_sleep_get_wakeup_cause(); if (reset_cause == ESP_RST_DEEPSLEEP) { switch (wakeup_cause) { case ESP_SLEEP_WAKEUP_EXT0: return EOS_PWR_WAKE_BTN; case ESP_SLEEP_WAKEUP_EXT1: return EOS_PWR_WAKE_UART; default: return EOS_PWR_WAKE_UNDEF; } } else { return EOS_PWR_WAKE_RST; } } void eos_power_sleep(void) { power_event_t evt; evt.type = POWER_ETYPE_SLEEP1; evt.source = 0; xQueueSend(power_queue, &evt, portMAX_DELAY); } void eos_power_wake(uint8_t source) { power_event_t evt; evt.type = POWER_ETYPE_WAKE; evt.source = source; xQueueSend(power_queue, &evt, portMAX_DELAY); } void eos_power_net_ready(void) { power_event_t evt; evt.type = POWER_ETYPE_NETRDY; evt.source = 0; xQueueSend(power_queue, &evt, portMAX_DELAY); }