#include "wifi_config_portal.h" #include "esp_wifi.h" #include "esp_log.h" #include "esp_event.h" #include "esp_netif.h" #include "nvs_flash.h" #include "esp_timer.h" #include "esp_http_server.h" #include #include #include #include "dns_server.h" #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif static const char *TAG = "WIFI_PORTAL"; static httpd_handle_t server = NULL; static wifi_connected_cb_t g_on_connected = NULL; static esp_timer_handle_t wifi_watchdog_timer = NULL; static bool got_ip = false; // --- WATCHDOG: tenta reconectar se ficar 60 s sem IP --- static void wifi_watchdog_cb(void *arg) { if (!got_ip) { ESP_LOGW(TAG, "⏱️ 60 s sem IP — a tentar reconectar Wi-Fi..."); esp_wifi_disconnect(); vTaskDelay(pdMS_TO_TICKS(1000)); // espera 1 s esp_wifi_connect(); } } // --- EVENTOS WIFI --- static void on_wifi_event(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: ESP_LOGI(TAG, "📡 STA start"); esp_wifi_connect(); break; case WIFI_EVENT_STA_CONNECTED: ESP_LOGI(TAG, "🔗 STA conectado ao AP, aguardando IP..."); break; case WIFI_EVENT_STA_DISCONNECTED: { wifi_event_sta_disconnected_t *disconn = (wifi_event_sta_disconnected_t *) event_data; ESP_LOGW(TAG, "⚠️ STA desconectado (motivo %d)", disconn->reason); got_ip = false; esp_wifi_connect(); break; } default: ESP_LOGI(TAG, "ℹ️ Evento Wi-Fi não tratado: %ld", (long)event_id); break; } } } // --- Task auxiliar para iniciar o app (MQTT, LEDs, etc.) --- static void wifi_start_app_task(void *arg) { if (g_on_connected) g_on_connected(); // chamada do callback da aplicação vTaskDelete(NULL); } // --- EVENTO IP OBTIDO --- static void on_got_ip(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { got_ip = true; ESP_LOGI(TAG, "🌐 STA obteve IP!"); if (wifi_watchdog_timer) esp_timer_stop(wifi_watchdog_timer); // 🧠 executa o callback noutra task (stack própria) xTaskCreate(wifi_start_app_task, "wifi_start_app", 6144, NULL, 5, NULL); } // --- NVS: guardar e ler credenciais --- static bool wifi_load_creds(char *ssid, char *pass) { nvs_handle_t nvs; if (nvs_open("wifi", NVS_READONLY, &nvs) != ESP_OK) return false; size_t len1 = 32, len2 = 64; if (nvs_get_str(nvs, "ssid", ssid, &len1) != ESP_OK) { nvs_close(nvs); return false; } if (nvs_get_str(nvs, "pass", pass, &len2) != ESP_OK) { nvs_close(nvs); return false; } nvs_close(nvs); return true; } static void wifi_save_creds(const char *ssid, const char *pass) { nvs_handle_t nvs; if (nvs_open("wifi", NVS_READWRITE, &nvs) != ESP_OK) return; nvs_set_str(nvs, "ssid", ssid); nvs_set_str(nvs, "pass", pass); nvs_commit(nvs); nvs_close(nvs); } // --- Handlers HTTP --- static esp_err_t handle_root(httpd_req_t *req) { const char *html = "" "" "Configuração Wi-Fi" "" "

Configuração Wi-Fi

" "
" "SSID:

" "Senha:


" "" "
" "" ""; httpd_resp_set_type(req, "text/html; charset=UTF-8"); httpd_resp_set_hdr(req, "Cache-Control", "no-store, no-cache, must-revalidate"); return httpd_resp_send(req, html, HTTPD_RESP_USE_STRLEN); } static esp_err_t handle_save(httpd_req_t *req) { char buf[128]; int ret = httpd_req_recv(req, buf, MIN(req->content_len, sizeof(buf) - 1)); if (ret <= 0) { return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Nada recebido"); } buf[ret] = '\0'; char ssid[32] = {0}, pass[64] = {0}; sscanf(buf, "ssid=%31[^&]&pass=%63s", ssid, pass); wifi_save_creds(ssid, pass); ESP_LOGI(TAG, "💾 Credenciais salvas: SSID=%s", ssid); const char *resp = "" "" "

✅ Credenciais guardadas!

" "

O dispositivo vai reiniciar em 2 segundos...

" ""; httpd_resp_set_type(req, "text/html; charset=UTF-8"); httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN); vTaskDelay(pdMS_TO_TICKS(2000)); esp_restart(); return ESP_OK; } // --- Inicia o servidor HTTP --- static void start_webserver(void) { httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.stack_size = 8192; config.server_port = 80; config.max_uri_handlers = 8; config.uri_match_fn = httpd_uri_match_wildcard; config.recv_wait_timeout = 10; config.send_wait_timeout = 10; config.max_resp_headers = 20; if (httpd_start(&server, &config) == ESP_OK) { httpd_uri_t root = { .uri = "/", .method = HTTP_GET, .handler = handle_root }; httpd_uri_t save = { .uri = "/save", .method = HTTP_POST, .handler = handle_save }; httpd_register_uri_handler(server, &root); httpd_register_uri_handler(server, &save); ESP_LOGI(TAG, "🌐 Servidor HTTP iniciado na porta %d", config.server_port); } else { ESP_LOGE(TAG, "❌ Falha ao iniciar servidor HTTP!"); } } // --- Inicialização principal --- void wifi_config_portal_init(wifi_connected_cb_t cb, bool have_creds) { g_on_connected = cb; esp_netif_init(); esp_event_loop_create_default(); 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_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &on_wifi_event, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &on_got_ip, NULL)); const esp_timer_create_args_t timer_args = { .callback = &wifi_watchdog_cb, .name = "wifi_watchdog" }; esp_timer_create(&timer_args, &wifi_watchdog_timer); esp_timer_start_periodic(wifi_watchdog_timer, 60000000); // 60 s char ssid[32] = {0}, pass[64] = {0}; bool stored = wifi_load_creds(ssid, pass); if (have_creds || stored) { ESP_LOGI(TAG, "🔄 A ligar à rede guardada..."); wifi_config_t wifi_cfg = {0}; strcpy((char *)wifi_cfg.sta.ssid, ssid); strcpy((char *)wifi_cfg.sta.password, pass); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg)); ESP_ERROR_CHECK(esp_wifi_start()); esp_wifi_connect(); } else { ESP_LOGW(TAG, "❌ Sem credenciais — modo AP de configuração..."); esp_netif_create_default_wifi_ap(); wifi_config_t ap_cfg = { .ap = { .ssid_len = 0, .password = "12345678", .max_connection = 4, .authmode = WIFI_AUTH_WPA_WPA2_PSK } }; uint8_t mac[6]; esp_wifi_get_mac(WIFI_IF_AP, mac); snprintf((char *)ap_cfg.ap.ssid, sizeof(ap_cfg.ap.ssid), "ESP32_%02X%02X%02X", mac[3], mac[4], mac[5]); if (strlen((char *)ap_cfg.ap.password) == 0) ap_cfg.ap.authmode = WIFI_AUTH_OPEN; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_cfg)); ESP_ERROR_CHECK(esp_wifi_start()); if (wifi_watchdog_timer) esp_timer_stop(wifi_watchdog_timer); start_dns_server(); start_webserver(); ESP_LOGI(TAG, "🌐 Criado AP SSID=%s Senha=%s", ap_cfg.ap.ssid, ap_cfg.ap.password); } }