#include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "esp_system.h" #include "esp_wifi.h" #include "esp_event.h" #include "esp_log.h" #include "nvs_flash.h" #include "nvs.h" #include "wifi_manager.h" static const char *TAG = "WIFI_MANAGER"; // FreeRTOS event group for WiFi events static EventGroupHandle_t s_wifi_event_group; // Current WiFi state static wifi_state_t s_wifi_state = WIFI_STATE_DISCONNECTED; // Retry counter static int s_retry_num = 0; // Event callback static wifi_event_callback_t s_event_callback = NULL; // Scan mode flag static bool s_scan_mode = false; // NVS namespace #define NVS_NAMESPACE "wifi_creds" static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (event_base == WIFI_EVENT) { switch (event_id) { case WIFI_EVENT_STA_START: if (!s_scan_mode) { // Only connect if not in scan mode esp_wifi_connect(); s_wifi_state = WIFI_STATE_CONNECTING; if (s_event_callback) { s_event_callback(s_wifi_state); } } break; case WIFI_EVENT_STA_DISCONNECTED: if (event_data) { wifi_event_sta_disconnected_t* disconnected = (wifi_event_sta_disconnected_t*) event_data; ESP_LOGE(TAG, "Disconnect reason : %d", disconnected->reason); switch(disconnected->reason) { case WIFI_REASON_AUTH_EXPIRE: ESP_LOGE(TAG, "Authentication expired"); break; case WIFI_REASON_AUTH_FAIL: ESP_LOGE(TAG, "Authentication failed"); break; case WIFI_REASON_NO_AP_FOUND: ESP_LOGE(TAG, "AP not found - check SSID"); break; case WIFI_REASON_HANDSHAKE_TIMEOUT: ESP_LOGE(TAG, "Handshake timeout - check password"); break; default: ESP_LOGE(TAG, "Other reason: %d", disconnected->reason); } } if (s_retry_num < WIFI_MAXIMUM_RETRY) { esp_wifi_connect(); s_retry_num++; ESP_LOGI(TAG, "Retry connecting to AP (%d/%d)", s_retry_num, WIFI_MAXIMUM_RETRY); } else { xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); s_wifi_state = WIFI_STATE_ERROR; ESP_LOGI(TAG, "Failed to connect to AP"); } if (s_event_callback) { s_event_callback(s_wifi_state); } break; default: break; } } 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; ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip)); s_retry_num = 0; xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); s_wifi_state = WIFI_STATE_CONNECTED; if (s_event_callback) { s_event_callback(s_wifi_state); } } } esp_err_t wifi_manager_init(void) { esp_err_t ret = ESP_OK; // Initialize NVS ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); // Create event group s_wifi_event_group = xEventGroupCreate(); if (s_wifi_event_group == NULL) { ESP_LOGE(TAG, "Failed to create event group"); return ESP_FAIL; } // Initialize TCP/IP stack ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_sta(); // Initialize WiFi with default config wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); // Register event handlers ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL)); ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL)); // Set WiFi mode to station ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); // Disable power save mode to ensure WiFi works properly ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); // Set WiFi country (important for proper channel scanning) wifi_country_t wifi_country = { .cc = "US", .schan = 1, .nchan = 11, .max_tx_power = 84, .policy = WIFI_COUNTRY_POLICY_AUTO, }; ESP_ERROR_CHECK(esp_wifi_set_country(&wifi_country)); ESP_LOGI(TAG, "WiFi manager initialized"); return ESP_OK; } esp_err_t wifi_manager_start(void) { char ssid[33] = {0}; char password[65] = {0}; // Try to get credentials from NVS if (wifi_manager_get_credentials(ssid, sizeof(ssid), password, sizeof(password)) == ESP_OK) { ESP_LOGI(TAG, "Starting WiFi with stored credentials"); ESP_LOGI(TAG, "Attempting to connect to SSID: '%s'", ssid); ESP_LOGI(TAG, "SSID length: %d", strlen(ssid)); // Set scan mode to prevent auto-connect s_scan_mode = true; // Start WiFi for scanning ESP_ERROR_CHECK(esp_wifi_start()); // Wait a bit for WiFi to initialize vTaskDelay(100 / portTICK_PERIOD_MS); // Scan for available networks ESP_LOGI(TAG, "Starting WiFi scan..."); wifi_scan_config_t scan_config = { .ssid = NULL, .bssid = NULL, .channel = 0, .show_hidden = true, .scan_type = WIFI_SCAN_TYPE_ACTIVE, .scan_time = { .active = { .min = 100, .max = 300, }, }, }; esp_err_t scan_ret = esp_wifi_scan_start(&scan_config, true); if (scan_ret != ESP_OK) { ESP_LOGE(TAG, "WiFi scan failed with error: %s", esp_err_to_name(scan_ret)); } else { uint16_t ap_count = 0; esp_wifi_scan_get_ap_num(&ap_count); ESP_LOGI(TAG, "WiFi scan done! Found %d access points", ap_count); if (ap_count > 0) { wifi_ap_record_t *ap_records = malloc(sizeof(wifi_ap_record_t) * ap_count); if (ap_records) { esp_wifi_scan_get_ap_records(&ap_count, ap_records); bool target_found = false; for (int i = 0; i < ap_count && i < 20; i++) { ESP_LOGI(TAG, " %d. SSID: '%s', RSSI: %d, Channel: %d, Auth: %d", i + 1, ap_records[i].ssid, ap_records[i].rssi, ap_records[i].primary, ap_records[i].authmode); // Check if our target SSID matches if (strcmp((char*)ap_records[i].ssid, ssid) == 0) { ESP_LOGI(TAG, " *** Target SSID found! Channel: %d, Auth: %d ***", ap_records[i].primary, ap_records[i].authmode); target_found = true; } } if (!target_found) { ESP_LOGW(TAG, "Target SSID '%s' not found in scan results!", ssid); } free(ap_records); } } else { ESP_LOGW(TAG, "No access points found. Possible issues:"); ESP_LOGW(TAG, " - WiFi antenna not connected"); ESP_LOGW(TAG, " - Wrong country code"); ESP_LOGW(TAG, " - Hardware issue"); ESP_LOGW(TAG, " - All APs on 5GHz only"); } } // Clear scan mode s_scan_mode = false; // Configure for connection wifi_config_t wifi_config = { .sta = { .threshold.authmode = WIFI_AUTH_OPEN, // Accept any auth .sae_pwe_h2e = WPA3_SAE_PWE_BOTH, }, }; strlcpy((char*)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid)); strlcpy((char*)wifi_config.sta.password, password, sizeof(wifi_config.sta.password)); ESP_LOGI(TAG, "Setting WiFi configuration..."); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); // Now trigger connection ESP_LOGI(TAG, "Connecting to WiFi..."); esp_wifi_connect(); // Wait for connection EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, 30000 / portTICK_PERIOD_MS); // 30 second timeout if (bits & WIFI_CONNECTED_BIT) { ESP_LOGI(TAG, "Connected to AP"); return ESP_OK; } else if (bits & WIFI_FAIL_BIT) { ESP_LOGE(TAG, "Failed to connect to AP after retries"); return ESP_FAIL; } else { ESP_LOGE(TAG, "WiFi connection timeout"); return ESP_ERR_TIMEOUT; } } else { ESP_LOGE(TAG, "No WiFi credentials found"); return ESP_ERR_NOT_FOUND; } } esp_err_t wifi_manager_stop(void) { esp_err_t ret = esp_wifi_stop(); if (ret == ESP_OK) { s_wifi_state = WIFI_STATE_DISCONNECTED; s_retry_num = 0; xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT); ESP_LOGI(TAG, "WiFi stopped"); } return ret; } bool wifi_manager_is_connected(void) { return s_wifi_state == WIFI_STATE_CONNECTED; } wifi_state_t wifi_manager_get_state(void) { return s_wifi_state; } void wifi_manager_register_callback(wifi_event_callback_t callback) { s_event_callback = callback; } esp_err_t wifi_manager_set_credentials(const char* ssid, const char* password) { nvs_handle_t nvs_handle; esp_err_t ret; ret = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to open NVS namespace"); return ret; } ret = nvs_set_str(nvs_handle, "ssid", ssid); if (ret != ESP_OK) { nvs_close(nvs_handle); return ret; } ret = nvs_set_str(nvs_handle, "password", password); if (ret != ESP_OK) { nvs_close(nvs_handle); return ret; } ret = nvs_commit(nvs_handle); nvs_close(nvs_handle); if (ret == ESP_OK) { ESP_LOGI(TAG, "WiFi credentials saved"); } return ret; } esp_err_t wifi_manager_get_credentials(char* ssid, size_t ssid_len, char* password, size_t pass_len) { nvs_handle_t nvs_handle; esp_err_t ret; ret = nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs_handle); if (ret != ESP_OK) { return ret; } ret = nvs_get_str(nvs_handle, "ssid", ssid, &ssid_len); if (ret != ESP_OK) { nvs_close(nvs_handle); return ret; } ret = nvs_get_str(nvs_handle, "password", password, &pass_len); nvs_close(nvs_handle); return ret; } esp_err_t wifi_manager_clear_credentials(void) { nvs_handle_t nvs_handle; esp_err_t ret; ret = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle); if (ret != ESP_OK) { return ret; } nvs_erase_key(nvs_handle, "ssid"); nvs_erase_key(nvs_handle, "password"); ret = nvs_commit(nvs_handle); nvs_close(nvs_handle); if (ret == ESP_OK) { ESP_LOGI(TAG, "WiFi credentials cleared"); } return ret; }