ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ESP8266/ESP32 WiFi模块Web配网:从原理到代码实现与优化

ESP8266/ESP32 WiFi模块Web配网:从原理到代码实现与优化

1. 项目概述:为什么WiFi模块的Web配置是“通用好使”的

搞嵌入式开发或者物联网项目的朋友,对WiFi模块肯定不陌生。无论是ESP8266、ESP32,还是其他厂家的串口WiFi模块,拿到手第一件事就是配置它联网。早年大家可能都经历过:打开串口调试助手,对着AT指令手册,一条一条敲命令,设置SSID、密码、工作模式。这个过程不仅繁琐,而且一旦指令敲错一个字母,或者波特率没对上,就得从头再来,调试效率很低。

后来,很多模块厂商提供了手机App进行配置,这确实方便了一些。但App往往有平台限制(比如只支持安卓),或者需要下载特定软件,在工业现场、批量生产或给最终用户部署时,依然不够灵活。这时候,基于Web的配置方式就显现出它的“通用”优势了。所谓Web配置,就是让WiFi模块在启动后,自身先作为一个WiFi接入点(AP),我们用手边任何一台设备(手机、电脑、平板)连接上这个AP,然后在浏览器里打开一个特定的IP地址(通常是192.168.4.1),就能看到一个配置页面。在这个页面上,我们可以像设置家用路由器一样,通过表单选择或输入目标WiFi的网络名(SSID)、密码、工作模式(STA/AP/STA+AP)等参数,提交后模块会自动重启并尝试连接。

这个方法为什么“绝对通用好使”?第一,它不依赖任何特定操作系统或额外软件,只要设备有浏览器和WiFi功能就能用,覆盖了从手机到电脑的所有智能设备。第二,它对用户极其友好,图形化界面避免了记忆和输入复杂AT指令的麻烦。第三,对于开发者而言,一套Web配置代码可以固化在模块的固件中,适配各种不同的主控芯片(STM32、Arduino、树莓派等),只要它们通过串口与WiFi模块通信即可,实现了跨平台的“一次开发,到处配置”。今天,我就结合自己多次在项目中实现Web配网的经验,把这套方案的里里外外、从原理到代码实现细节,以及那些容易踩坑的地方,给大家彻底讲透。

2. 核心原理与方案选型:从AT指令到Web服务器的跨越

要理解Web配置,得先知道没有它的时候我们是怎么做的。最原始的方式就是串口发送AT指令。模块上电后,主控MCU通过串口发送像AT+CWMODE=1\r\n(设置STA模式)、AT+CWJAP="SSID","password"\r\n(连接AP)这样的指令。这种方式直接、底层,但所有逻辑和交互都压在MCU的程序里,不灵活。

Web配置的本质,是在WiFi模块内部(或与MCU配合)运行了一个微型的Web服务器。这个服务器通常只处理少数几个页面和请求,资源占用极小。整个流程可以拆解为以下几个核心阶段:

2.1 模块启动与SoftAP模式建立

模块上电后,首先执行初始化程序,将自身设置为AP模式(Access Point,即无线接入点)。此时,它会广播一个特定的SSID,比如“ESP8266_Config”或“Device_Setup_XXXX”。同时,它会启动一个DHCP服务器,为连接上来的设备(如你的手机)自动分配一个局域网IP地址(如192.168.4.100),它自己的IP通常固定为192.168.4.1。这个阶段,模块与世界的关系是:它自己创造了一个微型的、封闭的无线网络环境。

2.2 嵌入式Web服务器响应

在这个微型网络中,模块内运行的Web服务器开始监听80端口(HTTP默认端口)。当你用手机连接上“ESP8266_Config”这个WiFi后,在浏览器输入http://192.168.4.1,浏览器就会向这个地址的80端口发起一个HTTP GET请求。模块的Web服务器收到请求后,会返回一个HTML页面。这个页面就是我们看到的配置界面。它可能包含表单、下拉菜单、输入框等元素。

2.3 配置信息的提交与处理

你在网页上填写好目标WiFi的SSID、密码,选择工作模式(通常是STA模式,即让模块作为客户端去连接家里的路由器),点击“提交”或“连接”按钮。这时,浏览器会向服务器(模块)发起一个HTTP POST请求,请求体内就携带了你填写的所有表单数据(格式通常是application/x-www-form-urlencoded)。

模块的Web服务器程序需要解析这个POST请求,从中提取出SSID、密码等关键信息。然后,它需要做两件最重要的事:第一,将这些网络配置参数保存到非易失性存储器中,例如ESP系列常用的SPIFFS文件系统或Preferences库,或者模块的EEPROM。确保掉电后信息不丢失。第二,根据新的配置,重启网络服务。这意味着模块需要先断开当前的AP模式,然后根据保存的参数,切换到STA模式,并主动去连接你指定的那个路由器。

2.4 连接反馈与模式切换

连接尝试会有成功或失败两种结果。优秀的Web配置方案会提供反馈机制。一种常见做法是:模块在尝试连接路由器时,同时开启一个TCP服务器或者继续短暂提供Web服务,让手机能在一定时间内查询连接状态。更通用的做法是,模块连接成功后,可以通过串口向主MCU发送状态信息;或者,如果模块自身有LED,可以用不同的闪烁模式来指示状态(例如,快闪表示正在连接,常亮表示连接成功,慢闪表示连接失败)。

方案选型上,对于像ESP8266/ESP32这类功能强大的芯片,我们可以直接在其上开发完整的Web服务器逻辑,使用Arduino框架的ESP8266WebServer或ESP32的WebServer库非常方便。对于更简单的、仅支持AT指令的串口WiFi模块(如某些厂家的透传模块),则需要主MCU来扮演Web服务器的角色:MCU需要实现一个简单的TCP/IP协议栈(可以使用轻量级库如lwIP),并解析HTTP协议,生成HTML页面。后一种方案对MCU的资源要求较高,但优点是能适配几乎所有串口WiFi模块。本文讨论的重点,是前一种方案,即在ESP8266/ESP32这类片上系统(SoC)上实现,这也是目前最主流、最高效的方式。

3. 环境准备与核心库依赖

动手之前,我们需要把开发环境搭好。这里以最常见的Arduino IDE开发ESP8266为例,ESP32的过程几乎完全一样。

3.1 硬件准备

  • WiFi模块:NodeMCU ESP8266开发板、Wemos D1 mini或ESP32开发板一块。建议初学者从NodeMCU开始,引脚丰富,自带USB转串口。
  • USB数据线:用于供电和程序下载。
  • 一台无线路由器:用于提供最终要连接的目标网络。
  • 电脑和智能手机:电脑用于编程和串口监控,手机用于测试Web配置界面。

3.2 软件环境配置

  1. 安装Arduino IDE:从官网下载并安装最新版。
  2. 添加开发板支持:打开Arduino IDE,进入“文件”->“首选项”,在“附加开发板管理器网址”中输入:http://arduino.esp8266.com/stable/package_esp8266com_index.json(对于ESP8266) 对于ESP32,则需要添加:https://espressif.github.io/arduino-esp32/package_esp32_index.json
  3. 安装开发板:打开“工具”->“开发板”->“开发板管理器”,搜索“esp8266”或“esp32”,找到由Espressif Systems提供的包,点击安装。
  4. 安装核心库:我们主要依赖两个库,它们通常随开发板包一起安装,但最好确认一下。
    • ESP8266WiFi(或 ESP32的WiFi库): 提供WiFi连接的核心功能。
    • ESP8266WebServer(或 WebServer for ESP32): 这是我们实现Web配置服务器的核心。它允许我们定义处理特定URL路径(如“/”、“/config”、“/save”)的函数。

注意:在Arduino IDE的“库管理器”中搜索“ESP8266WebServer”或“WebServer”确认其已安装。有时网络问题可能导致安装不完整,如果编译时报错找不到相关头文件,可以尝试通过库管理器重新安装。

3.3 项目代码结构规划在开始写代码前,脑子里先有个清晰的蓝图。一个典型的Web配置项目,其逻辑流程和代码结构大致如下:

  1. 初始化阶段:上电后,首先尝试从存储区(如EEPROM或文件系统)读取之前保存的WiFi配置。
  2. 网络尝试阶段:如果存在保存的配置,则用这些配置尝试连接路由器(STA模式)。设置一个超时时间(例如10-15秒)。
  3. 配网模式判断
    • 成功:连接成功,打印IP地址,主Web服务器(提供设备实际功能)启动。
    • 失败/无配置:连接失败或首次使用。则启动SoftAP模式,创建配置网络,并启动配网专用的Web服务器。
  4. 配网服务器阶段:配网Web服务器提供几个简单的页面和接口:
    • GET /: 返回配置主页(HTML表单)。
    • GET /scan: 返回一个JSON,包含当前可扫描到的所有WiFi网络列表,用于前端页面动态填充SSID下拉框。
    • POST /save: 接收前端提交的表单数据,保存配置,重启模块。
  5. 配置保存与重启:接收到新配置后,将其存入非易失存储,然后调用ESP.restart()重启模块,让新配置生效。

这个流程实现了“智能连接”:有配置且有效则直连,无效或没有则进入配网状态。用户体验非常顺畅。

4. 代码实现详解:从AP启动到配置保存

接下来,我们分步实现核心代码。我会先给出关键代码片段,然后解释其作用和注意事项。

4.1 定义全局变量与引入库

#include <ESP8266WiFi.h> // ESP8266核心WiFi库 #include <ESP8266WebServer.h> // ESP8266 Web服务器库 #include <EEPROM.h> // 用于存储配置(简单方案) // 定义Web服务器对象,监听80端口 ESP8266WebServer server(80); // 定义存储WiFi配置的结构体 struct WiFiConfig { char ssid[32]; char password[64]; }; WiFiConfig config; // 软AP的配置 const char* apSSID = "ESP8266_Config"; // 配网时的AP名称 const char* apPassword = NULL; // 配网AP密码,设为NULL则开放网络

这里我们使用EEPROM来存储配置,对于简单的SSID和密码足够用。对于更复杂的配置项,建议使用Preferences库(ESP32)或SPIFFS/LittleFS文件系统。

4.2 初始化与读取保存的配置setup()函数开始部分:

void setup() { Serial.begin(115200); delay(1000); // 初始化EEPROM,我们打算使用512字节空间 EEPROM.begin(512); loadConfig(); // 从EEPROM加载配置 // 尝试用保存的配置连接WiFi WiFi.mode(WIFI_STA); // 先设置为STA模式 WiFi.begin(config.ssid, config.password); Serial.print("Connecting to "); Serial.println(config.ssid); // 等待连接,设置超时 unsigned long startAttemptTime = millis(); const unsigned long timeout = 15000; // 15秒超时 while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < timeout) { delay(500); Serial.print("."); } Serial.println(); // 判断连接结果 if (WiFi.status() == WL_CONNECTED) { // 连接成功,进入正常工作模式 Serial.println("WiFi connected!"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); startMainServer(); // 启动设备的主功能服务器 return; // 跳出,不进入配网模式 } else { // 连接失败或超时,进入配网模式 Serial.println("Failed to connect. Starting configuration AP..."); startConfigAP(); } }

loadConfig()函数负责从EEPROM的特定地址读取数据到config结构体。这里的关键是超时机制。不能让模块无限期地尝试连接一个可能不存在的网络,15-30秒是一个合理的超时时间。连接成功后,我们调用startMainServer(),这里可以放置你设备真正的业务逻辑,比如传感器数据上报、控制接口等。连接失败,则进入startConfigAP()

4.3 启动配置AP和Web服务器

void startConfigAP() { // 设置为AP+STA模式,有时需要AP模式提供Web服务的同时扫描网络 WiFi.mode(WIFI_AP_STA); // 启动软AP WiFi.softAP(apSSID, apPassword); Serial.print("Configuration AP started. SSID: "); Serial.println(apSSID); Serial.print("AP IP address: "); Serial.println(WiFi.softAPIP()); // 通常是 192.168.4.1 // 设置Web服务器路由 server.on("/", HTTP_GET, handleRoot); // 主页 server.on("/scan", HTTP_GET, handleScan); // 扫描WiFi server.on("/save", HTTP_POST, handleSave); // 保存配置 server.onNotFound(handleNotFound); // 404处理 // 启动Web服务器 server.begin(); Serial.println("HTTP server started"); } void loop() { // 如果处于配网模式,则需要处理客户端请求 server.handleClient(); // 这里可以添加配网状态下的其他逻辑,如LED闪烁提示 }

WiFi.mode(WIFI_AP_STA)是一个实用技巧。纯AP模式(WIFI_AP)下,模块无法主动扫描周围的WiFi网络。而WIFI_AP_STA模式允许模块同时作为AP(提供配置页面)和STA客户端(执行扫描任务),这样我们才能在配置页面上动态列出可用的WiFi列表。

4.4 实现Web请求处理器这是Web配置的核心,我们实现三个主要的处理器。

4.4.1 主页处理器 (handleRoot)这个函数返回一个HTML页面。为了代码简洁,我们可以用C++的原始字符串字面量(R字符串)来嵌入HTML。

void handleRoot() { String html = R"rawliteral( <!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; margin: 40px; } .container { max-width: 400px; margin: auto; padding: 20px; border: 1px solid #ccc; border-radius: 10px; } input, select, button { width: 100%; padding: 10px; margin: 8px 0; box-sizing: border-box; } button { background-color: #4CAF50; color: white; border: none; cursor: pointer; } button:hover { background-color: #45a049; } </style> </head> <body> <div class="container"> <h2>WiFi Configuration</h2> <form action="/save" method="post"> <label for="ssid">Network SSID:</label> <select id="ssid" name="ssid" required> <option value="">-- Click Scan First --</option> </select> <button type="button" onclick="scanWiFi()">Scan Networks</button> <br> <label for="password">Password:</label> <input type="password" id="password" name="password" placeholder="WiFi password"> <br> <button type="submit">Save & Connect</button> </form> <p id="message"></p> </div> <script> function scanWiFi() { fetch('/scan') .then(response => response.json()) .then(data => { const select = document.getElementById('ssid'); select.innerHTML = '<option value="">-- Select a network --</option>'; data.networks.forEach(net => { const option = document.createElement('option'); option.value = net.ssid; option.textContent = net.ssid + ' (' + net.rssi + ' dBm)'; select.appendChild(option); }); document.getElementById('message').textContent = 'Scan complete.'; }) .catch(err => { console.error('Scan error:', err); document.getElementById('message').textContent = 'Scan failed.'; }); } </script> </body> </html> )rawliteral"; server.send(200, "text/html", html); }

这个页面包含一个表单,一个用于触发扫描的按钮,以及一个显示扫描结果的下拉框。注意,SSID列表是通过JavaScript调用/scan接口动态获取的,这比写死在下拉框里要好得多,因为用户环境中的WiFi是变化的。

4.4.2 扫描处理器 (handleScan)这个接口返回一个JSON格式的WiFi列表。

void handleScan() { Serial.println("Scanning networks..."); int n = WiFi.scanNetworks(false, true); // 参数:async=false, show_hidden=false String json = "{\"networks\":["; for (int i = 0; i < n; ++i) { if(i) json += ","; json += "{\"ssid\":\"" + WiFi.SSID(i) + "\","; json += "\"rssi\":" + String(WiFi.RSSI(i)) + "}"; // 注意:WiFi.encryptionType(i) 可以获取加密类型,可根据需要添加 } json += "]}"; server.send(200, "application/json", json); // 扫描完成后及时清理缓存,防止内存占用 WiFi.scanDelete(); }

重要提示:WiFi.scanNetworks()是一个阻塞操作,在网络环境复杂时可能需要几秒钟。在此期间,Web服务器无法处理其他请求。因此,在实际产品中,可以考虑异步扫描,或者缓存扫描结果一段时间,避免频繁扫描。

4.4.3 配置保存处理器 (handleSave)这是最后一步,也是最关键的一步。

void handleSave() { // 检查是否接收到了必要的参数 if (!server.hasArg("ssid") || server.arg("ssid") == "") { server.send(400, "text/plain", "Error: SSID is required"); return; } // 获取表单数据 String newSSID = server.arg("ssid"); String newPassword = server.arg("password"); // 密码可能为空(开放网络) // 将新配置存入结构体 newSSID.toCharArray(config.ssid, sizeof(config.ssid)); newPassword.toCharArray(config.password, sizeof(config.password)); // 保存到EEPROM saveConfig(); // 返回成功页面,提示设备将重启 String html = R"rawliteral( <!DOCTYPE html> <html> <head><meta charset="UTF-8"><title>Configuration Saved</title></head> <body> <h2>Configuration Saved!</h2> <p>SSID: )rawliteral" + newSSID + R"rawliteral(</p> <p>The device will restart and try to connect to the new network. Please reconnect your device to your target WiFi and access the device via its new IP address.</p> <p><em>This page will close in 5 seconds...</em></p> <script> setTimeout(function(){ window.close(); }, 5000); </script> </body> </html> )rawliteral"; server.send(200, "text/html", html); // 延迟一段时间让响应发送完毕,然后重启 delay(3000); ESP.restart(); }

saveConfig()函数负责将config结构体写入EEPROM,并调用EEPROM.commit()确保数据物理写入。这里有几个关键细节

  1. 参数验证:必须检查ssid参数是否存在且非空。
  2. 密码处理:密码字段可能为空(如果目标网络是开放的)。我们的代码需要能处理这种情况,WiFi.begin()函数允许密码为空字符串。
  3. 字符串拷贝安全:使用toCharArray()并指定目标缓冲区大小,防止缓冲区溢出,这是嵌入式编程的安全基石。
  4. 用户反馈与重启:保存成功后,必须给用户明确的反馈。然后延迟几秒(确保HTTP响应已完全发送给客户端),再执行重启。立即重启可能导致TCP连接中断,浏览器收不到完整的成功页面。

5. 进阶优化与生产级考量

上面的代码实现了一个可用的基础版本。但要用于实际项目,还需要考虑更多细节。

5.1 配置存储的健壮性EEPROM有擦写次数限制(通常10万次)。频繁保存相同配置会导致不必要的磨损。可以在保存前,先与已存储的配置比较,只有发生变化时才写入。

void saveConfigIfChanged() { WiFiConfig oldConfig; // 先读取当前存储的配置到oldConfig EEPROM.get(0, oldConfig); // 比较新旧配置 if (strcmp(oldConfig.ssid, config.ssid) != 0 || strcmp(oldConfig.password, config.password) != 0) { Serial.println("Configuration changed, saving to EEPROM..."); saveConfig(); // 真正的保存函数 } else { Serial.println("Configuration unchanged, skip saving."); } }

对于ESP32,强烈推荐使用Preferences库,它内部处理了磨损均衡和掉电保护,比直接操作EEPROM更安全方便。

5.2 连接状态反馈与超时重置设备连接路由器成功后,如何让用户知道?除了串口打印,还可以:

  • LED指示:在loop()中,根据WiFi.status()控制LED。例如,常亮表示已连AP,慢闪表示正在连接,快闪表示配网模式。
  • 智能超时重置:如果设备在STA模式下长时间(如30分钟)无法连接到任何网络(可能是路由器关机或移动了位置),可以自动重启并进入配网模式。这需要记录连接失败的开始时间。
    unsigned long lastConnectedTime = 0; void loop() { server.handleClient(); if (WiFi.status() == WL_CONNECTED) { lastConnectedTime = millis(); // 重置计时器 } else { // 如果断开连接超过30分钟 if (millis() - lastConnectedTime > 30 * 60 * 1000UL) { Serial.println("Long time disconnect, reset to config mode..."); // 可以清空配置或直接重启 ESP.restart(); } } }

5.3 增强Web界面与用户体验

  • 显示信号强度:在/scan返回的JSON中加入rssi,并在前端页面上以图形化方式(如信号条)或文字显示,帮助用户选择信号最好的网络。
  • 隐藏密码显示:在密码输入框旁边增加一个“显示/隐藏”密码的小图标,提升易用性。
  • 保存多个网络配置:高级功能,可以保存多个SSID和密码,设备按优先级尝试连接。
  • 配置设备本身信息:在Web页面上增加设备名称、静态IP设置等选项。

5.4 安全性考虑(基础层面)对于大多数物联网设备,以下基础安全措施是必要的:

  • AP密码:为配网AP设置一个密码(apPassword),即使是一个简单的固定密码,也能防止路人随意连接。
  • 防暴力提交:对/save接口做简单的频率限制,防止被恶意脚本连续提交。
  • 输入净化:对接收到的SSID和密码进行长度检查,防止超长字符串导致缓冲区溢出。SSID最长32字节,WPA2密码最少8字节,最长63字节。
  • HTTPS:对于高安全要求场景,可以考虑使用HTTPS。但这需要向模块烧录证书,并消耗更多资源,实现复杂。多数家庭物联网设备仍使用HTTP,因为配网网络本身是临时的、隔离的。

6. 常见问题与深度排查指南

在实际部署中,你一定会遇到各种各样的问题。下面是我总结的“踩坑”实录和解决方法。

6.1 手机/电脑搜不到配网AP

  • 现象:模块启动了SoftAP,但手机WiFi列表里看不到“ESP8266_Config”。
  • 排查步骤
    1. 检查串口日志:首先确认串口打印了Configuration AP started. SSID: ESP8266_ConfigAP IP address: 192.168.4.1。如果没有,说明AP启动失败。
    2. 检查WiFi模式:确保代码中设置了WiFi.mode(WIFI_AP)WIFI_AP_STA。如果误设为WIFI_STA,则不会开启AP。
    3. 检查频道兼容性:有些国家的路由器或设备对WiFi频道有限制。ESP8266的SoftAP默认可能在频道1。尝试在代码中指定一个常见频道,如6:WiFi.softAP(apSSID, apPassword, 6)
    4. 距离与干扰:将手机靠近模块测试。2.4GHz信号穿墙能力弱。
    5. 手机兼容性:极少数旧手机可能不支持某些WiFi模式。用另一台设备测试。

6.2 能连接AP,但打不开192.168.4.1

  • 现象:手机显示已连接“ESP8266_Config”,但浏览器访问http://192.168.4.1时一直转圈或显示无法连接。
  • 排查步骤
    1. 获取IP地址:在手机WiFi设置里,查看当前连接的详细信息,确认获取到的IP地址确实是192.168.4.x网段,网关是192.168.4.1。有时手机会因为之前连接过类似网段而使用静态IP,导致冲突。
    2. 关闭移动数据:在测试时,暂时关闭手机的移动数据功能,防止手机因WiFi无互联网而自动切换到流量,导致对192.168.4.1的请求被忽略。
    3. 检查服务器启动:确认串口打印了HTTP server started
    4. 检查端口占用:Web服务器默认80端口。确保没有其他服务占用。
    5. 尝试使用IP直接访问:有些浏览器或系统对.1的地址有特殊处理。可以尝试在手机浏览器输入完整的http://192.168.4.1:80
    6. 清除浏览器缓存:有时旧的缓存会导致问题。

6.3 提交配置后,设备重启但无法连接目标WiFi

  • 现象:在网页提交配置后,设备重启,但串口显示一直在尝试连接,最终失败并又回到了配网模式。
  • 排查步骤
    1. 检查SSID和密码:这是最常见的原因。确保密码正确,区分大小写。特别注意是否有空格。可以在网页提交后,立即从EEPROM读取并打印出来验证。
    2. 检查路由器设置:目标路由器是否开启了MAC地址过滤?是否设置了最大连接数已满?尝试用其他设备先连接一下该路由器确认其工作正常。
    3. 信号强度:模块距离路由器太远或隔墙太多,信号太弱(RSSI < -80 dBm)可能导致连接不稳定或失败。在handleScan接口中返回信号强度,在网页上优先选择信号强的。
    4. 加密方式不匹配:虽然现代路由器基本都是WPA2-PSK,但有些企业网络或旧路由器可能使用WEP或WPA。ESP8266的WiFi.begin()默认支持WPA/WPA2。如果遇到特殊加密,可能需要调用更底层的连接函数。
    5. 电源问题:WiFi连接瞬间电流较大,如果使用劣质USB线或电源,可能导致电压跌落,使模块重启或连接失败。使用可靠的5V/1A以上电源适配器。

6.4 设备频繁断开重连

  • 现象:设备能连上WiFi,但运行一段时间后自动断开,然后又重连。
  • 排查步骤
    1. 电源稳定性:同上,这是首要怀疑对象。用示波器观察模块供电电压在WiFi发射时的波动。
    2. 路由器DHCP租期:检查路由器DHCP服务器设置的地址租期。租期过短可能导致IP续租失败。可以在代码中设置为静态IP,或者处理WiFi断开事件(WiFi.onStationModeDisconnected)并尝试重连。
    3. WiFi睡眠模式:ESP8266为了省电,默认可能开启WiFi睡眠。在setup()中加入WiFi.setSleepMode(WIFI_NONE_SLEEP);可以禁用睡眠,保持连接稳定。
    4. 网络干扰:2.4GHz频段拥挤,微波炉、蓝牙设备都可能造成干扰。尝试修改路由器和ESP的WiFi频道。

6.5 Web界面扫描不到任何网络

  • 现象:点击网页上的“Scan Networks”按钮,列表始终为空或提示失败。
  • 排查步骤
    1. 检查WiFi模式:必须使用WIFI_AP_STAWIFI_STA模式才能扫描。纯AP模式(WIFI_AP)下无法扫描。
    2. 扫描时机WiFi.scanNetworks()是阻塞的。如果在扫描完成前多次快速点击按钮,可能导致资源冲突。可以在前端按钮点击后将其禁用,直到收到响应。
    3. 内存不足:扫描结果会占用内存。如果设备剩余内存很少,扫描可能失败。在扫描前后打印ESP.getFreeHeap()检查内存情况。
    4. 检查JSON格式:使用浏览器的开发者工具(F12 -> Network),查看/scan请求的响应内容。确保返回的是有效的JSON格式。一个常见的错误是JSON字符串拼接时,最后一个网络后面多了一个逗号。

6.6 如何强制设备重新进入配网模式?这是一个常见的运维需求。有几种方法:

  1. 硬件按键:在loop()中检测一个GPIO引脚的电平(如连接一个按键到GND)。当检测到按键长按(如5秒)时,清除保存的WiFi配置并重启。
    if(digitalRead(BUTTON_PIN) == LOW) { delay(50); // 消抖 unsigned long pressStart = millis(); while(digitalRead(BUTTON_PIN) == LOW) { if(millis() - pressStart > 5000) { // 长按5秒 clearConfig(); ESP.restart(); break; } } }
  2. 软件指令:在设备连接网络后,提供一个隐藏的管理页面或UDP广播指令,接收特定命令后执行重置。
  3. 超时重置:如前所述,长时间连接失败后自动重置。

实现一个稳定可靠的Web配网功能,是物联网设备“开箱即用”体验的关键。它隐藏了技术的复杂性,将友好的交互界面呈现给最终用户。从简单的AT指令到构建一个微型Web服务器,这个演进过程体现了嵌入式开发中“用户体验至上”的思路。我个人的经验是,在项目初期就规划并调试好配网逻辑,能为后续的开发和测试节省大量时间。毕竟,谁也不想每次修改代码后,都拿着串口线去重新配置网络。把上述代码和思路吃透,根据你的具体硬件和需求稍作调整,就能打造出一个“绝对通用好使”的WiFi模块配置方案。

返回列表