diff --git a/main/wifi/wifi_manager.c b/main/wifi/wifi_manager.c new file mode 100644 index 0000000..97d9db7 --- /dev/null +++ b/main/wifi/wifi_manager.c @@ -0,0 +1,141 @@ +#include "wifi_manager.h" +#include "mimi_config.h" + +#include +#include +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_netif.h" +#include "nvs_flash.h" +#include "nvs.h" + +static const char *TAG = "wifi"; + +static EventGroupHandle_t s_wifi_event_group; +static int s_retry_count = 0; +static char s_ip_str[16] = "0.0.0.0"; +static bool s_connected = false; + +static void event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + esp_wifi_connect(); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + s_connected = false; + if (s_retry_count < MIMI_WIFI_MAX_RETRY) { + /* Exponential backoff: 1s, 2s, 4s, 8s, ... capped at 30s */ + uint32_t delay_ms = MIMI_WIFI_RETRY_BASE_MS << s_retry_count; + if (delay_ms > MIMI_WIFI_RETRY_MAX_MS) { + delay_ms = MIMI_WIFI_RETRY_MAX_MS; + } + ESP_LOGW(TAG, "Disconnected, retry %d/%d in %" PRIu32 "ms", + s_retry_count + 1, MIMI_WIFI_MAX_RETRY, delay_ms); + vTaskDelay(pdMS_TO_TICKS(delay_ms)); + esp_wifi_connect(); + s_retry_count++; + } else { + ESP_LOGE(TAG, "Failed to connect after %d retries", MIMI_WIFI_MAX_RETRY); + xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); + } + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + snprintf(s_ip_str, sizeof(s_ip_str), IPSTR, IP2STR(&event->ip_info.ip)); + ESP_LOGI(TAG, "Connected! IP: %s", s_ip_str); + s_retry_count = 0; + s_connected = true; + xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + } +} + +esp_err_t wifi_manager_init(void) +{ + s_wifi_event_group = xEventGroupCreate(); + + ESP_ERROR_CHECK(esp_netif_init()); + esp_netif_create_default_wifi_sta(); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_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)); + + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + + ESP_LOGI(TAG, "WiFi manager initialized"); + return ESP_OK; +} + +esp_err_t wifi_manager_start(void) +{ + /* Read credentials from NVS */ + nvs_handle_t nvs; + esp_err_t err = nvs_open(MIMI_NVS_WIFI, NVS_READONLY, &nvs); + if (err != ESP_OK) { + ESP_LOGW(TAG, "No WiFi credentials in NVS. Use CLI: wifi_set "); + return ESP_ERR_NOT_FOUND; + } + + wifi_config_t wifi_cfg = {0}; + size_t len = sizeof(wifi_cfg.sta.ssid); + err = nvs_get_str(nvs, MIMI_NVS_KEY_SSID, (char *)wifi_cfg.sta.ssid, &len); + if (err != ESP_OK) { + nvs_close(nvs); + ESP_LOGW(TAG, "SSID not found in NVS"); + return ESP_ERR_NOT_FOUND; + } + + len = sizeof(wifi_cfg.sta.password); + nvs_get_str(nvs, MIMI_NVS_KEY_PASS, (char *)wifi_cfg.sta.password, &len); + nvs_close(nvs); + + ESP_LOGI(TAG, "Connecting to SSID: %s", wifi_cfg.sta.ssid); + + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg)); + ESP_ERROR_CHECK(esp_wifi_start()); + + return ESP_OK; +} + +esp_err_t wifi_manager_wait_connected(uint32_t timeout_ms) +{ + TickType_t ticks = (timeout_ms == UINT32_MAX) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms); + EventBits_t bits = xEventGroupWaitBits( + s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, + pdFALSE, pdFALSE, ticks); + + if (bits & WIFI_CONNECTED_BIT) { + return ESP_OK; + } + return ESP_ERR_TIMEOUT; +} + +bool wifi_manager_is_connected(void) +{ + return s_connected; +} + +esp_err_t wifi_manager_set_credentials(const char *ssid, const char *password) +{ + nvs_handle_t nvs; + ESP_ERROR_CHECK(nvs_open(MIMI_NVS_WIFI, NVS_READWRITE, &nvs)); + ESP_ERROR_CHECK(nvs_set_str(nvs, MIMI_NVS_KEY_SSID, ssid)); + ESP_ERROR_CHECK(nvs_set_str(nvs, MIMI_NVS_KEY_PASS, password)); + ESP_ERROR_CHECK(nvs_commit(nvs)); + nvs_close(nvs); + ESP_LOGI(TAG, "WiFi credentials saved for SSID: %s", ssid); + return ESP_OK; +} + +const char *wifi_manager_get_ip(void) +{ + return s_ip_str; +} + +EventGroupHandle_t wifi_manager_get_event_group(void) +{ + return s_wifi_event_group; +} diff --git a/main/wifi/wifi_manager.h b/main/wifi/wifi_manager.h new file mode 100644 index 0000000..79657e8 --- /dev/null +++ b/main/wifi/wifi_manager.h @@ -0,0 +1,47 @@ +#pragma once + +#include "esp_err.h" +#include "esp_event.h" +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" + +#define WIFI_CONNECTED_BIT BIT0 +#define WIFI_FAIL_BIT BIT1 + +/** + * Initialize WiFi subsystem (STA mode). + * Reads SSID/password from NVS. If not set, waits for serial configuration. + */ +esp_err_t wifi_manager_init(void); + +/** + * Start WiFi connection. Non-blocking, fires events. + */ +esp_err_t wifi_manager_start(void); + +/** + * Block until WiFi is connected or failed. + * @param timeout_ms Max time to wait (portMAX_DELAY for forever) + * @return ESP_OK if connected, ESP_ERR_TIMEOUT otherwise + */ +esp_err_t wifi_manager_wait_connected(uint32_t timeout_ms); + +/** + * Check if WiFi is currently connected. + */ +bool wifi_manager_is_connected(void); + +/** + * Save WiFi credentials to NVS. + */ +esp_err_t wifi_manager_set_credentials(const char *ssid, const char *password); + +/** + * Get the current IP address string (or "0.0.0.0" if not connected). + */ +const char *wifi_manager_get_ip(void); + +/** + * Get the event group for WiFi state (WIFI_CONNECTED_BIT / WIFI_FAIL_BIT). + */ +EventGroupHandle_t wifi_manager_get_event_group(void);