#include "wifi_manager.h" #include #include #include #include "app_config.h" #include "esp_event.h" #include "esp_log.h" #include "esp_netif.h" #include "esp_netif_sntp.h" #include "esp_timer.h" #include "esp_wifi.h" #include "freertos/FreeRTOS.h" #include "freertos/event_groups.h" static const char *TAG = "wifi"; #define CONNECTED_BIT BIT0 #define RETRY_MIN_MS 1000 #define RETRY_MAX_MS 30000 static EventGroupHandle_t s_events; static volatile bool s_connected; static int s_retry_ms = RETRY_MIN_MS; static bool s_sntp_started; static esp_timer_handle_t s_reconnect_timer; static void reconnect_timer_cb(void *arg) { (void)arg; esp_wifi_connect(); } /* * Schedule the retry on a timer rather than sleeping here. This runs on the * shared system event task — a vTaskDelay would stall every queued WiFi/IP * event behind it (up to 30s at full backoff) and can overflow the default * event queue during an outage. */ static void schedule_reconnect(void) { ESP_LOGW(TAG, "reconnecting in %d ms", s_retry_ms); esp_timer_stop(s_reconnect_timer); /* no-op if not running */ esp_timer_start_once(s_reconnect_timer, (uint64_t)s_retry_ms * 1000); s_retry_ms *= 2; if (s_retry_ms > RETRY_MAX_MS) { s_retry_ms = RETRY_MAX_MS; } } static void event_handler(void *arg, esp_event_base_t base, int32_t id, void *data) { (void)arg; if (base == WIFI_EVENT && id == WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (base == WIFI_EVENT && id == WIFI_EVENT_STA_DISCONNECTED) { s_connected = false; xEventGroupClearBits(s_events, CONNECTED_BIT); schedule_reconnect(); } else if (base == IP_EVENT && id == IP_EVENT_STA_GOT_IP) { ip_event_got_ip_t *e = data; ESP_LOGI(TAG, "connected, ip " IPSTR, IP2STR(&e->ip_info.ip)); s_connected = true; s_retry_ms = RETRY_MIN_MS; xEventGroupSetBits(s_events, CONNECTED_BIT); /* Start SNTP once we first have an address. Schedule mode needs a * real clock, and control.c refuses to act on one that looks like * 1970. */ if (!s_sntp_started) { esp_sntp_config_t cfg = ESP_NETIF_SNTP_DEFAULT_CONFIG(APP_SNTP_SERVER); if (esp_netif_sntp_init(&cfg) == ESP_OK) { s_sntp_started = true; ESP_LOGI(TAG, "sntp started (%s)", APP_SNTP_SERVER); } } } } esp_err_t wifi_manager_init(void) { if (s_events) { return ESP_OK; } s_events = xEventGroupCreate(); if (!s_events) { return ESP_ERR_NO_MEM; } setenv("TZ", APP_TIMEZONE, 1); tzset(); const esp_timer_create_args_t timer_args = { .callback = reconnect_timer_cb, .name = "wifi_retry", }; ESP_ERROR_CHECK(esp_timer_create(&timer_args, &s_reconnect_timer)); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_sta(); wifi_init_config_t init_cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&init_cfg)); ESP_ERROR_CHECK(esp_event_handler_instance_register( WIFI_EVENT, ESP_EVENT_ANY_ID, event_handler, NULL, NULL)); ESP_ERROR_CHECK(esp_event_handler_instance_register( IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler, NULL, NULL)); wifi_config_t wifi_cfg = {0}; strlcpy((char *)wifi_cfg.sta.ssid, APP_WIFI_SSID, sizeof(wifi_cfg.sta.ssid)); strlcpy((char *)wifi_cfg.sta.password, APP_WIFI_PASS, sizeof(wifi_cfg.sta.password)); wifi_cfg.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg)); /* CONFIG_ESP_WIFI_NVS_ENABLED=n, so nothing here touches flash. */ ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); ESP_ERROR_CHECK(esp_wifi_start()); ESP_LOGI(TAG, "started, joining \"%s\"", APP_WIFI_SSID); return ESP_OK; } bool wifi_manager_is_connected(void) { return s_connected; } bool wifi_manager_wait_connected(int timeout_ms) { if (!s_events) { return false; } EventBits_t bits = xEventGroupWaitBits(s_events, CONNECTED_BIT, pdFALSE, pdFALSE, pdMS_TO_TICKS(timeout_ms)); return (bits & CONNECTED_BIT) != 0; }