#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_http_server.h"

// 配置参数
#define EXAMPLE_ESP_WIFI_SSID      "ESP321_Config"  // 热点名称
#define EXAMPLE_ESP_WIFI_PASS      "12345678"      // 热点密码
#define EXAMPLE_ESP_WIFI_CHANNEL   6                // 热点信道
#define MAX_STA_CONN              4                // 最大连接数
#define CONFIG_EXAMPLE_WEB_MOUNT_POINT "/www"      // Web挂载点

static const char *TAG = "wifi_config";

// 宏定义
#define MIN(a, b) ((a) < (b) ? (a) : (b))

// 函数前置声明
static void wifi_init_softap(void);
static void wifi_stop_ap(void);
static void wifi_connect_sta(const char *ssid, const char *password);
static bool check_saved_wifi_config(void);
static void start_webserver(void);
static void stop_webserver(void);
static void save_wifi_config(const char *ssid, const char *password);
static void url_decode(char *str);

static httpd_handle_t server = NULL;

/* 配置页面的HTML */
static const char* CONFIG_HTML = 
"<!DOCTYPE html>"
"<html>"
"<head>"
"<meta charset=\"UTF-8\">"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"<title>WiFi Configuration</title>"
"<style>"
"body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; }"
".container { max-width: 400px; margin: 0 auto; background: white; padding: 20px; border-radius: 5px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }"
"h1 { text-align: center; color: #333; }"
"form { display: flex; flex-direction: column; }"
"label { margin-top: 10px; font-weight: bold; }"
"input { padding: 10px; margin-top: 5px; border: 1px solid #ddd; border-radius: 4px; }"
"button { margin-top: 20px; padding: 10px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }"
"button:hover { background-color: #45a049; }"
"</style>"
"</head>"
"<body>"
"<div class=\"container\">"
"<h1>WiFi Configuration</h1>"
"<form action=\"/configure\" method=\"post\">"
"<label for=\"ssid\">WiFi SSID:</label>"
"<input type=\"text\" id=\"ssid\" name=\"ssid\" required>"
"<label for=\"password\">WiFi Password:</label>"
"<input type=\"password\" id=\"password\" name=\"password\">"
"<button type=\"submit\">Connect</button>"
"</form>"
"</div>"
"</body>"
"</html>";

/* 成功页面的HTML */
static const char* SUCCESS_HTML = 
"<!DOCTYPE html>"
"<html>"
"<head>"
"<meta charset=\"UTF-8\">"
"<title>Configuration Successful</title>"
"<style>"
"body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }"
".success { color: #4CAF50; font-size: 24px; }"
"</style>"
"</head>"
"<body>"
"<div class=\"success\">Configuration Successful!</div>"
"<p>ESP32 is now connecting to your WiFi network.</p>"
"</body>"
"</html>";

/* 处理根路径请求 */
static esp_err_t root_get_handler(httpd_req_t *req)
{
    httpd_resp_set_type(req, "text/html");
    return httpd_resp_send(req, CONFIG_HTML, strlen(CONFIG_HTML));
}

/* URL解码 */
static void url_decode(char *str)
{
    char *p = str;
    char decoded;
    while (*p) {
        if (*p == '%') {
            sscanf(p + 1, "%2hhx", &decoded);
            *str++ = decoded;
            p += 3;
        } else if (*p == '+') {
            *str++ = ' ';
            p++;
        } else {
            *str++ = *p++;
        }
    }
    *str = '\0';
}

/* 处理配置提交 */
static esp_err_t configure_post_handler(httpd_req_t *req)
{
    char buf[100];
    int ret, remaining = req->content_len;
    char ssid[32] = {0};
    char password[64] = {0};
    
    while (remaining > 0) {
        if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)))) <= 0) {
            if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
                continue;
            }
            return ESP_FAIL;
        }
        remaining -= ret;
        
        // 简单的表单解析
        char *p = buf;
        while (*p) {
            if (strncmp(p, "ssid=", 5) == 0) {
                p += 5;
                char *end = strchr(p, '&');
                if (end) {
                    strncpy(ssid, p, MIN(end - p, sizeof(ssid) - 1));
                } else {
                    strncpy(ssid, p, sizeof(ssid) - 1);
                }
                url_decode(ssid);
            } else if (strncmp(p, "password=", 9) == 0) {
                p += 9;
                char *end = strchr(p, '&');
                if (end) {
                    strncpy(password, p, MIN(end - p, sizeof(password) - 1));
                } else {
                    strncpy(password, p, sizeof(password) - 1);
                }
                url_decode(password);
            }
            p++;
        }
    }
    
    ESP_LOGI(TAG, "Received WiFi configuration: SSID=%s, Password=%s", ssid, password);
    
    // 保存配置到NVS
    save_wifi_config(ssid, password);
    
    // 返回成功页面
    httpd_resp_set_type(req, "text/html");
    httpd_resp_send(req, SUCCESS_HTML, strlen(SUCCESS_HTML));
    
    // 延迟后关闭服务器并连接WiFi
    vTaskDelay(2000 / portTICK_PERIOD_MS);
    stop_webserver();
    wifi_stop_ap();
    wifi_connect_sta(ssid, password);
    
    return ESP_OK;
}

/* 保存WiFi配置到NVS */
static void save_wifi_config(const char *ssid, const char *password)
{
    nvs_handle_t nvs;
    esp_err_t err;
    
    err = nvs_open("storage", NVS_READWRITE, &nvs);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Error opening NVS: %s", esp_err_to_name(err));
        return;
    }
    
    err = nvs_set_str(nvs, "ssid", ssid);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Error saving SSID: %s", esp_err_to_name(err));
    }
    
    err = nvs_set_str(nvs, "password", password);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Error saving password: %s", esp_err_to_name(err));
    }
    
    err = nvs_commit(nvs);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Error committing NVS: %s", esp_err_to_name(err));
    }
    
    nvs_close(nvs);
}

/* 启动HTTP服务器 */
static void start_webserver(void)
{
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    config.server_port = 80;
    
    ESP_LOGI(TAG, "Starting HTTP server on port: %d", config.server_port);
    
    if (httpd_start(&server, &config) == ESP_OK) {
        httpd_uri_t root = {
            .uri = "/",
            .method = HTTP_GET,
            .handler = root_get_handler,
            .user_ctx = NULL
        };
        
        httpd_uri_t configure = {
            .uri = "/configure",
            .method = HTTP_POST,
            .handler = configure_post_handler,
            .user_ctx = NULL
        };
        
        httpd_register_uri_handler(server, &root);
        httpd_register_uri_handler(server, &configure);
    }
}

/* 停止HTTP服务器 */
static void stop_webserver(void)
{
    if (server) {
        httpd_stop(server);
        server = NULL;
    }
}

/* 初始化并启动WiFi热点 */
static void wifi_init_softap(void)
{
    esp_netif_init();
    esp_event_loop_create_default();
    esp_netif_create_default_wifi_ap();
    
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    
    wifi_config_t wifi_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
            .channel = EXAMPLE_ESP_WIFI_CHANNEL,
            .password = EXAMPLE_ESP_WIFI_PASS,
            .max_connection = MAX_STA_CONN,
            .authmode = WIFI_AUTH_WPA_WPA2_PSK
        },
    };
    
    if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) {
        wifi_config.ap.authmode = WIFI_AUTH_OPEN;
    }
    
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
    
    ESP_LOGI(TAG, "WiFi AP started. SSID: %s Password: %s Channel: %d",
             EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
}

/* 停止WiFi热点 */
static void wifi_stop_ap(void)
{
    ESP_ERROR_CHECK(esp_wifi_stop());
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL));
    ESP_LOGI(TAG, "WiFi AP stopped");
}

/* 连接到指定的WiFi网络 */
static void wifi_connect_sta(const char *ssid, const char *password)
{
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);
    
    wifi_config_t wifi_config = {
        .sta = {
            .threshold.authmode = WIFI_AUTH_WPA2_PSK,
        },
    };
    
    strncpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid) - 1);
    strncpy((char *)wifi_config.sta.password, password, sizeof(wifi_config.sta.password) - 1);
    
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
    ESP_ERROR_CHECK(esp_wifi_connect());
    
    ESP_LOGI(TAG, "Connecting to WiFi SSID: %s", ssid);
}

/* 检查是否已有保存的WiFi配置 */
static bool check_saved_wifi_config(void)
{
    nvs_handle_t nvs;
    esp_err_t err;
    char ssid[32] = {0};
    char password[64] = {0};
    size_t required_size;
    
    err = nvs_open("storage", NVS_READONLY, &nvs);
    if (err != ESP_OK) {
        return false;
    }
    
    // 获取SSID长度
    err = nvs_get_str(nvs, "ssid", NULL, &required_size);
    if (err != ESP_OK || required_size == 0) {
        nvs_close(nvs);
        return false;
    }
    
    // 获取SSID
    err = nvs_get_str(nvs, "ssid", ssid, &required_size);
    if (err != ESP_OK) {
        nvs_close(nvs);
        return false;
    }
    
    // 获取密码
    nvs_get_str(nvs, "password", password, &required_size);
    
    nvs_close(nvs);
    
    if (strlen(ssid) > 0) {
        ESP_LOGI(TAG, "Found saved WiFi config: SSID=%s", ssid);

        // 初始化WiFi
        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));

        wifi_connect_sta(ssid, password);
        return true;
    }
    
    return false;
}

void app_main(void)
{
    // 初始化NVS
    esp_err_t 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);

    // 清除保存的WiFi配置
    nvs_handle_t nvs;
    ret = nvs_open("storage", NVS_READWRITE, &nvs);
    if (ret == ESP_OK) {
        nvs_erase_key(nvs, "ssid");
        nvs_erase_key(nvs, "password");
        nvs_commit(nvs);
        nvs_close(nvs);
    }

    // 检查是否有保存的WiFi配置
    if (!check_saved_wifi_config()) {
        // 没有保存的配置,启动热点和Web服务器
        wifi_init_softap();
        start_webserver();
    }
}
最后修改:2025 年 05 月 16 日 09 : 19 PM
如果觉得我的文章对你有用,请随意赞赏