LED_shit/main/wifi_config_portal.c
2025-11-04 21:33:28 +00:00

253 lines
8.9 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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 <string.h>
#include <stdio.h>
#include <stdlib.h>
#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 =
"<!DOCTYPE html><html lang='pt'><head>"
"<meta charset='UTF-8'>"
"<title>Configuração Wi-Fi</title>"
"<style>"
"body{font-family:Arial,sans-serif;background:#eef2f3;text-align:center;}"
"form{background:#fff;padding:20px;margin:50px auto;width:360px;"
"border-radius:10px;box-shadow:0 0 10px rgba(0,0,0,0.2);}"
"input{width:90%;padding:10px;margin:10px;font-size:16px;}"
"button{background:#007bff;color:#fff;border:none;padding:10px 20px;"
"border-radius:6px;font-size:16px;cursor:pointer;}"
"button:hover{background:#0056b3;}"
"</style></head><body>"
"<h2>Configuração Wi-Fi</h2>"
"<form id='wifiForm' action='/save' method='POST'>"
"SSID:<br><input name='ssid' maxlength='31' required><br>"
"Senha:<br><input name='pass' type='password' maxlength='63' required><br><br>"
"<button type='submit'>Guardar</button>"
"</form>"
"<script>"
"document.getElementById('wifiForm').onsubmit=function(e){"
"e.preventDefault();"
"fetch('/save',{method:'POST',body:new URLSearchParams(new FormData(this))})"
".then(()=>alert('Credenciais enviadas! Reinicie o dispositivo.'));"
"};"
"</script>"
"</body></html>";
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 =
"<!DOCTYPE html><html lang='pt'><meta charset='UTF-8'>"
"<body style='font-family:sans-serif;text-align:center;margin-top:50px;'>"
"<h2>✅ Credenciais guardadas!</h2>"
"<p>O dispositivo vai reiniciar em 2 segundos...</p>"
"</body></html>";
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);
}
}