ESP32 Web配网项目深度拆解:从SoftAP、DNS劫持到前端页面,一次讲清

张开发
2026/4/8 10:20:09 15 分钟阅读

分享文章

ESP32 Web配网项目深度拆解:从SoftAP、DNS劫持到前端页面,一次讲清
ESP32 Web配网项目深度拆解从SoftAP、DNS劫持到前端页面一次讲清当你第一次拿到ESP32开发板时可能最头疼的就是如何配置WiFi。传统的配网方式要么需要硬编码SSID和密码要么依赖手机APP都不够优雅。而Web配网技术让用户只需打开浏览器就能完成配置这种无感体验正成为智能硬件的标配。今天我们就来彻底拆解这个看似简单实则精妙的Web配网系统。1. 双模网络Station与SoftAP的共生之道ESP32最强大的特性之一就是能同时工作在Station(STA)和SoftAP(AP)模式。这种双模运行不是简单的功能叠加而是需要精心设计的资源协调。STAAP模式初始化流程// 关键初始化代码示例 wifi_config_t sta_config { .sta { .ssid CONFIG_ESP_WIFI_SSID, .password CONFIG_ESP_WIFI_PASSWORD, .scan_method WIFI_ALL_CHANNEL_SCAN, .sort_method WIFI_CONNECT_AP_BY_SIGNAL, } }; wifi_config_t ap_config { .ap { .ssid ESP32_Config, .password 12345678, .max_connection 4, .authmode WIFI_AUTH_WPA2_PSK } }; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA)); ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, sta_config)); ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, ap_config)); ESP_ERROR_CHECK(esp_wifi_start());实际项目中需要特别注意内存管理双模运行时内存占用会增加约30%建议将WiFi缓冲区设置为最小够用值信道冲突AP和STA最好使用相同信道避免频繁切换事件处理需要同时处理WIFI_EVENT_STA_CONNECTED和WIFI_EVENT_AP_STACONNECTED等事件经验分享在实测中发现当STA模式连接路由器的信号较弱时AP模式的吞吐量会下降50%以上。建议在配网完成后自动关闭AP模式以节省资源。2. DNS劫持的魔法所有道路通向配置页传统Web配网需要用户手动输入IP地址而高级的实现会让任何域名访问都跳转到配置页面这就是DNS劫持技术的魅力。DNS响应包构造原理监听UDP端口53的DNS请求解析查询的域名(通常截获所有A记录请求)返回指向ESP32本地IP(如192.168.4.1)的响应关键代码结构void handle_dns_request(void *arg) { uint8_t *data (uint8_t *)arg; // 解析查询类型 uint16_t qtype data[12] 8 | data[13]; if(qtype 0x0001) { // A记录查询 // 构造响应包头 memcpy(response, data, 12); response[2] 0x81; // 设置响应标志 response[3] 0x80; // 填充应答部分 uint8_t *answer response[12]; // ... 省略具体构造逻辑 sendto(dns_socket, response, len, 0, (struct sockaddr *)client_addr, addr_len); } }实测中会遇到的有趣现象Android设备会先尝试访问www.google.com来检测网络iOS设备偏好使用captive.apple.comWindows则钟爱www.msftconnecttest.com不同系统的探测域名对比系统类型探测域名预期响应Androidconnectivitycheck.gstatic.com204 No ContentiOScaptive.apple.com200 OK with特定页面Windowswww.msftconnecttest.com302重定向3. 前后端交互HTML与C的跨界对话现代Web配网页面已经不再是简单的表单而是需要动态获取WiFi列表、实时反馈连接状态的交互式应用。典型的数据流架构浏览器请求/获取HTML页面页面JS定时请求/wifi/list获取扫描结果用户提交表单POST/wifi/config后端保存到NVS并尝试连接前端代码关键片段function refreshWifiList() { fetch(/wifi/list) .then(response response.json()) .then(data { let html ; data.forEach(ap { html div classap-item onclickselectAP(${ap.ssid}) span${ap.ssid}/span span classrssi${ap.rssi}dBm/span /div; }); document.getElementById(ap-list).innerHTML html; }); } // 每5秒刷新一次 setInterval(refreshWifiList, 5000);后端处理逻辑要点// WiFi列表JSON生成 esp_err_t wifi_list_get_handler(httpd_req_t *req) { wifi_ap_record_t *ap_list malloc(sizeof(wifi_ap_record_t) * MAX_APs); uint16_t ap_count 0; ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(ap_count, ap_list)); char json_buf[1024]; sprintf(json_buf, [); for(int i0; iap_count; i) { char ap_json[128]; sprintf(ap_json, {\ssid\:\%s\,\rssi\:%d}%s, ap_list[i].ssid, ap_list[i].rssi, i ap_count-1 ? , : ); strcat(json_buf, ap_json); } strcat(json_buf, ]); httpd_resp_set_type(req, application/json); httpd_resp_send(req, json_buf, strlen(json_buf)); free(ap_list); return ESP_OK; }4. NVS存储配置数据的保险箱NVS(Non-Volatile Storage)是ESP32的持久化存储方案相比传统的EEPROM它提供了更可靠的键值存储和磨损均衡。WiFi配置存储的最佳实践使用单独的名称空间区分不同数据类型对敏感信息(如密码)进行基本混淆处理添加版本号字段便于后续升级兼容典型存储结构typedef struct { uint8_t version; // 当前为1 char ssid[32]; char password[64]; uint8_t checksum; // 简单校验和 } wifi_config_t; void save_wifi_config(const char *ssid, const char *pass) { nvs_handle_t handle; ESP_ERROR_CHECK(nvs_open(wifi_cfg, NVS_READWRITE, handle)); wifi_config_t cfg { .version 1, .checksum 0 }; strncpy(cfg.ssid, ssid, sizeof(cfg.ssid)-1); strncpy(cfg.password, pass, sizeof(cfg.password)-1); // 简单校验和计算 for(int i0; cfg.ssid[i]; i) cfg.checksum ^ cfg.ssid[i]; for(int i0; cfg.password[i]; i) cfg.checksum ^ cfg.password[i]; ESP_ERROR_CHECK(nvs_set_blob(handle, config, cfg, sizeof(cfg))); ESP_ERROR_CHECK(nvs_commit(handle)); nvs_close(handle); }NVS与文件系统对比特性NVSSPIFFS/LittleFS存取速度快(直接闪存访问)慢(需要文件系统层)存储结构键值对文件目录树适合场景配置参数大容量数据(如网页资源)磨损均衡自动处理需要手动管理在项目中巧妙结合这两种存储方式NVS保存关键配置文件系统存储网页资源可以达到最佳性能和可靠性平衡。

更多文章