搞嵌入式这几年,最烦的一件事就是设备换网络环境后要重新烧程序。尤其是ESP8266这种小板子,板载WiFi功能本来是它最大的卖点,结果每次改个WiFi密码就得把开发板从插座上拆下来、连USB线、重新编译上传,做个智能插座都能折腾出一身汗。所以我很早就想把手动烧录改成“一键配网”,再加上掉电保存WiFi账号密码的功能,让设备自己学会连网。这篇文章就基于ESP8266和Arduino开发环境,把从配网原理、存储方案到完整代码、实际踩坑的整个过程写透,适合正在做智能家居、遥控小车、传感器采集这类项目的同学参考。
开头先把需求捋清楚。所谓“一键配网”,指的是设备首次上电时进入待配网状态,手机端通过专用App或小程序把当前WiFi的SSID和密码广播出去,ESP8266收到后自动连接,整个过程不需要USB线、不需要重新编译代码。“掉电保存WiFi账号密码”则是把配好的SSID和密码写入非易失存储区,断电重启后自动加载,不用每次上电都重新配。
这两个功能单独看不难,合在一起就是一套完整的“零工具配置”流程。对产品原型验证和批量部署来说,这几乎是刚需。下面我把整个项目从方案选型到代码落地拆开讲。
1. 这个项目到底解决了什么痛点
1.1 传统烧录方式在量产和小批量部署时的尴尬
很多刚开始用ESP8266的人,会习惯把WiFi的SSID和密码直接写死在代码里:
const char* ssid = "MyHomeWiFi"; const char* password = "12345678";然后编译上传一次,设备就能联网。问题是这个方案只适合自己家里玩,一旦设备卖给别人、或者换了个WiFi环境,就得重新接USB线、重新改代码、重新烧录。如果设备是嵌入在墙里、挂在屋顶、或者在机箱里,那拆机的成本比硬件本身还贵。
小批量部署场景更明显。比如某项目要做20个环境监测节点,放在不同用户的家里,每个用户的WiFi密码都不一样,如果每个节点都单独编译一次,不仅操作繁琐,还容易在接线、烧录过程中把板子搞坏。而支持一键配网和掉电保存之后,20块板子烧录同一份固件,用户拿到手自己配网,现场效率完全不同。
1.2 一键配网与掉电保存的组合逻辑
这两个功能是天然搭配的。配网解决的是“把WiFi账号密码交给设备”的传输问题,掉电保存解决的是“传输完之后怎么稳定持有”的存储问题。如果只配网不保存,设备重启后还要重新配,那“一键”的意义就没了一半;如果只保存不配网,数据还是要靠烧录写进去,前面说的痛点依然存在。
最终的用户体验应该是这样的:
- 设备上电,检测到没有保存过WiFi信息,自动进入配网模式;
- 用户用手机App发送WiFi账号密码;
- 设备收到后自动连接WiFi,连接成功就把信息写入非易失存储;
- 设备掉电、重启,自动从存储里读取账号密码直接连接,全程不需要任何手动操作。
这套流程跑通之后,设备就可以“傻瓜化”交付了。
1.3 适合做这个项目的开发场景
就我的实际经验来说,下面几类场景特别适合用这套方案:
- 智能插座、智能灯、门磁传感器这类需要快速入网的家电模块;
- 需要布置多个节点的环境采集系统(温湿度、空气质量、土壤湿度等);
- 用ESP8266做小机器人、小车的底盘控制,需要现场连手机热点调试的情况;
- 任何你不希望频繁重新烧录固件的“摸不到”的设备。
如果你正在做的项目符合其中一条,那这篇文章里讲到的思路和代码基本可以直接落到你的项目里。
2. 配网方案选型:SmartConfig、AirKiss还是手动网页配网
2.1 SmartConfig的工作原理
ESP8266在Arduino框架下最常见的配网方式是WiFi.beginSmartConfig()。这套机制本质上是利用WiFi报文来传输数据:设备处于混杂模式,持续监听空中的数据包;手机App把SSID和密码编码后,通过UDP广播的形式发送出去;设备收到完整数据包后就能解析出WiFi账号密码。
听起来有点绕,但你可以理解成两个人隔着一堵墙喊话。喊内容听不清楚,但约定好一套“敲门暗号”,墙上的人听到暗号就知道外面的人想说什么。
Arduino框架把它封装得非常简单,核心API就三个:
WiFi.beginSmartConfig(); // 启动配网监听 WiFi.smartConfigDone(); // 查询是否配网成功 WiFi.smartConfigGet(); // 获取配网结果手机端的操作也不复杂,连上同一个WiFi后,打开相应的配网App或小程序,输入密码发送即可。
2.2 三种配网方式对比
除了SmartConfig,还有AirKiss和基于HTTP的网页配网。AirKiss是类似的无线广播方案,很多国产App都支持;网页配网则是ESP8266自己开一个热点,用户手机连上这个热点后打开浏览器访问配置页面,输入家里的WiFi信息,设备再切换过去连接。
我整理了一张对比表,方便你直接看差异:
| 配网方式 | 手机端操作 | 设备端实现 | 优点 | 缺点 |
|---|---|---|---|---|
| SmartConfig | App发送账号密码 | 监听空中数据包 | 操作快,无需切换热点 | 需要专用App,个别路由器兼容性差 |
| AirKiss | 微信小程序或App发送 | 类似SmartConfig监听 | 使用习惯好,扫码即可 | 协议相对封闭,调试困难 |
| 网页配网 | 连接设备热点,浏览器配置 | 设备内置Web服务器 | 无需额外App,兼容性最好 | 操作步骤多,需要切换WiFi |
2.3 为什么我最终选择SmartConfig作为默认方案
我的项目选择SmartConfig作为默认方案,原因有两条。
第一,Arduino框架对它封装最完善,代码量最少,稳定性经过大量用户验证。第二,配网时手机不需要断开当前WiFi去连设备的热点,整个过程在同一个网络内完成,逻辑上更顺滑。
网页配网虽然兼容性最好,但手机上要手动切换热点、再打开浏览器输入IP,对普通用户来说学习成本偏高。尤其是当我希望设备完成配网后能立刻继续跑业务逻辑时,SmartConfig的流程更干净。
当然,如果你的设备要交付给大量非技术用户,且不确定他们能装App,那网页配网是更稳妥的选择。我目前的做法是把SmartConfig作为主方案,同时预留了网页配网的接口,后面有需要再补。
3. 掉电保存到底存到哪里:EEPROM模拟与LittleFS文件系统
3.1 ESP8266上“EEPROM”的真实机制
Arduino框架里有个EEPROM.h库,用起来像是原生的EEPROM,但ESP8266芯片本身并没有真正的EEPROM硬件。它是把外接Flash上的某一块区域模拟成EEPROM来用,底层运作方式是“先读整块到内存,修改后在写回Flash”。
这个区别一定要清楚,因为它直接影响你的代码写法。传统AVR的EEPROM是逐字节独立操作的,我在某个地址写一个字节,不会影响相邻地址的数据。但ESP8266模拟EEPROM不一样,它默认只模拟了4KB空间,操作时得先EEPROM.begin(size),表明你想用多大区域;修改完数据后还要调用EEPROM.commit(),数据才会真正写进Flash。如果你只改内存不改落盘,掉电后一切白干。
另外还要注意,EEPROM.begin()的size参数如果和上一次不同,可能会导致Flash里已有的数据错位读取。所以工程里一旦定好存储大小,就不要随便改。
3.2 存储容量、寿命与操作粒度
ESP8266的外接Flash一般有1MB、2MB、4MB甚至更大,但留给用户存储配置的区域并不等于整块Flash。Arduino EEPROM模拟库默认用4KB,这对保存WiFi账号密码来说绰绰有余。
Flash的擦写寿命是有限制的,常见规格是十万次左右。注意,这里的十万次是针对每个“擦除块”的,而ESP8266的EEPROM模拟在写入时会做磨损均衡,实际寿命会好一些。但即便如此,也不要在主循环里频繁调用EEPROM.commit(),那会让Flash寿命快速见底。我的经验是:只在配网成功、或者用户主动修改配置时才执行一次写入。
3.3 存储布局设计
虽然WiFi账号密码总共用不了几个字节,我还是建议在EEPROM里规划一个固定布局,把版本号、校验位也放进去,这样后期扩展字段、识别数据有效性会方便很多。
以我的项目为例,我用了这样一个布局:
| 偏移地址 | 长度 | 内容 | 说明 |
|---|---|---|---|
| 0 | 4 | 魔数 | 固定写0xE5 0x01 0x23 0x07,用来判断是否已配过网 |
| 4 | 1 | 配置版本 | 当前配置数据结构版本号 |
| 5 | 32 | SSID | 最长32字节,不足补0 |
| 37 | 64 | 密码 | 最长64字节,不足补0 |
| 101 | 1 | 校验字节 | 对前面所有字节做异或,用于简单校验 |
这段布局预留了足够的空间,既不会溢出EEPROM的4KB区域,也为以后添加设备名、服务器地址等字段留了余地。每款设备的固件都共用一个版本号,如果以后改了数据结构,只要递增版本号,读取逻辑就能区分旧数据和新数据,避免误读脏数据。
4. 完整代码拆解:从配网触发到复位自启
4.1 引脚配置与按键触发逻辑
为了让设备自己判断是“首次使用”还是“需要重新配置”,我会在GPIO上接一个按键。用户长按按键3秒,清除EEPROM里的配网信息并重启;如果设备检测到EEPROM里没有合法配置,自动进入配网模式。
在我的测试板上,按键接在GPIO0,这个引脚在开发板上通常有现成的下拉设计,还兼着烧录模式选择脚,用作按键输入比较方便。代码里我用Button库或者简单轮询都能实现,这里用轮询方式减少依赖:
#define CONFIG_BUTTON_PIN 0 #define LED_PIN 2 // 很多ESP8266开发板板载LED接GPIO2 bool buttonPressed = false; unsigned long pressStartTime = 0; void checkButton() { if (digitalRead(CONFIG_BUTTON_PIN) == LOW) { if (!buttonPressed) { buttonPressed = true; pressStartTime = millis(); } else if (millis() - pressStartTime > 3000) { clearWifiConfig(); ESP.restart(); } } else { buttonPressed = false; } }这段逻辑很简单:检测到GPIO0被拉低就开始计时,持续低电平超过3秒就执行清配置和重启。GPIO0在正常运行时是浮空状态,不过很多开发板已经外接了上拉电阻,所以按键一端接GND、另一端接GPIO0就行,按下后引脚变低,很可靠。
4.2 SmartConfig配网流程代码
配网的核心流程分四步:启动SmartConfig监听、等待完成、读取账号密码、连接WiFi。我封装了一个函数:
bool startSmartConfig() { Serial.println("Enter SmartConfig mode..."); WiFi.mode(WIFI_STA); WiFi.beginSmartConfig(); unsigned long startTime = millis(); while (!WiFi.smartConfigDone()) { delay(500); digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // LED闪烁提示配网中 if (millis() - startTime > 120000) { Serial.println("SmartConfig timeout"); return false; } } Serial.println("SmartConfig received."); while (WiFi.status() != WL_CONNECTED) { delay(500); digitalWrite(LED_PIN, !digitalRead(LED_PIN)); if (millis() - startTime > 200000) { Serial.println("WiFi connect timeout"); return false; } } Serial.print("Connected, SSID: "); Serial.println(WiFi.SSID()); return true; }这里有三个容易踩的细节。第一,WiFi.mode(WIFI_STA)要先明确设置工作模式,不设置的话有些固件版本会在Station和SoftAP同时存在的状态下监听不到手机广播,导致配网一直超时。第二,最外层超时时间必须比内层连接超时时间短,不然会出现“配网已经完成但死在连接阶段”的假象。第三,LED闪烁不能用delay()太久,否则会影响监听数据包的接收。我试过在监听循环里delay()超过3秒,手机端哪怕一直发数据,设备也收不到。
4.3 WiFi信息读取与自动连接
设备重启后第一步不是急着配网,而是先检查EEPROM里有没有可用的配置。读取逻辑这样写:
bool loadWifiConfig(String &ssid, String &password) { EEPROM.begin(128); uint8_t header[4]; for (int i = 0; i < 4; i++) { header[i] = EEPROM.read(i); } if (header[0] != 0xE5 || header[1] != 0x01 || header[2] != 0x23 || header[3] != 0x07) { return false; } char ssidBuf[33] = {0}; char pwdBuf[65] = {0}; for (int i = 0; i < 32; i++) { ssidBuf[i] = char(EEPROM.read(5 + i)); } for (int i = 0; i < 64; i++) { pwdBuf[i] = char(EEPROM.read(37 + i)); } uint8_t calcCheck = 0; for (int i = 0; i < 101; i++) { calcCheck ^= EEPROM.read(i); } if (calcCheck != EEPROM.read(101)) { return false; } ssid = String(ssidBuf); password = String(pwdBuf); return true; }读取时做两个判断:一是魔数是否对得上,二是校验字节是否一致。只要有一个不对,就认为配置失效,之后主函数会切入SmartConfig配网流程。这个校验能识别出绝大部分“读到一半断电导致写脏数据”的情况,比单纯判断字符串非空可靠得多。
读取成功之后,主函数直接发起连接:
WiFi.mode(WIFI_STA); WiFi.begin(ssid.c_str(), password.c_str()); unsigned long connectStart = millis(); while (WiFi.status() != WL_CONNECTED) { delay(500); if (millis() - connectStart > 15000) { // 连接失败,可能WiFi环境变了,重新进入配网 startSmartConfig(); break; } }你可能会问,为什么连接失败不直接清EEPROM、进入配网?因为有时候只是路由器暂时没启动好,或者设备离路由器太远信号不稳,马上清掉配网信息会让用户重新走一遍配网,体验很差。我的做法是规定时间内连不上,先重新启动SmartConfig尝试配网,如果配网也超时,就不停重试,同时保留旧配置作为下次启动的默认项。
4.4 掉电保存与状态指示
配网成功后,把WiFi账号密码写入EEPROM并提交。这一步不要省略EEPROM.commit(),只调用write()的话数据在内存里,掉电就丢。
void saveWifiConfig(String ssid, String password) { EEPROM.begin(128); // 写魔数 EEPROM.write(0, 0xE5); EEPROM.write(1, 0x01); EEPROM.write(2, 0x23); EEPROM.write(3, 0x07); // 写版本 EEPROM.write(4, 0x01); // 写SSID for (int i = 0; i < 32; i++) { char c = i < ssid.length() ? ssid[i] : 0; EEPROM.write(5 + i, c); } // 写密码 for (int i = 0; i < 64; i++) { char c = i < password.length() ? password[i] : 0; EEPROM.write(37 + i, c); } // 写校验字节 uint8_t check = 0; for (int i = 0; i < 101; i++) { check ^= EEPROM.read(i); } EEPROM.write(101, check); boolean ok = EEPROM.commit(); Serial.printf("Save config: %s\n", ok ? "OK" : "FAILED"); }这段代码里有几个细节值得注意。SSID和密码用char类型逐字节存储,写入时候不足部分补0,相当于天然的字符串终止符,读取时用String构造就能正确处理。EEPROM.begin(128)里的128是整个操作区域的大小,虽然我们只用了102个字节,但预留到128是为了防止某些库按分区粒度做擦除时产生问题。
状态指示我用GPIO2的板载LED。配网中LED快速闪烁,连接成功后LED常亮或熄灭;连不上时LED慢速闪烁。做个简单的状态机函数:
void updateStatusLed(bool connected, bool configuring) { static unsigned long lastBlink = 0; if (connected) { digitalWrite(LED_PIN, HIGH); // 熄灭 } else if (configuring) { if (millis() - lastBlink > 300) { digitalWrite(LED_PIN, !digitalRead(LED_PIN)); lastBlink = millis(); } } else { if (millis() - lastBlink > 1000) { digitalWrite(LED_PIN, !digitalRead(LED_PIN)); lastBlink = millis(); } } }实际调试的时候,这个LED简直是救命的设备。有一次我把板子装进了一个不透光的盒子里,全靠LED的闪烁规律来判断它到底在配网、连上还是崩了,省了一大堆开壳的功夫。
5. 实测过程中的坑与排查链路
5.1 第一次掉电失效:问题出在没调用commit
第一次做这个项目时,我在代码里写了EEPROM.write(),然后直接ESP.restart()。重启后设备读出来的配置全是空,日志里明显能看到魔数校验失败。查了几分钟才反应过来:EEPROM.write()只是把数据写进了内存缓冲,没有EEPROM.commit(),数据根本没有落盘。
这个问题对很多从AVR转过来的老玩家来说是惯性坑。AVR的EEPROM写一个字节就已经落盘了,ESP8266的模拟机制完全不同,必须显式提交。我后来在代码里养成一个习惯:凡是涉及保存数据的函数,写完内存之后立刻看一眼有没有commit(),没有就补上。这个习惯帮我躲过了不少后续项目的暗雷。
排查链路也分享一下吧。当时我先是打印EEPROM里第一行的四个字节,发现全是0xFF,这基本说明Flash区域从未被写入过;然后再看代码,发现saveWifiConfig里确实遗漏了EEPROM.commit()。补上之后,重启读取马上就正常了。如果你也遇到掉电丢配置的问题,先按这个顺序查:有没有调用commit,写入区域是否越过了begin指定的大小,读取时有没有误判校验字节。
5.2 配网经常超时的根源
我遇到的第二个坑是配网经常超时,不是一次都不成功,而是十次里能成功六七次,剩下的要么卡在监听阶段,要么卡在连接阶段。
排查下来有两个原因。第一个是网络环境的问题:公司楼里WiFi信号杂、设备太多,手机和开发板不在同一个频段上也会导致收不到。我后来把手机热点和路由器2.4G频段分开测试,发现2.4G环境下配网成功率明显更高。第二个原因就是我前面说的,监听循环里用delay(100)甚至delay(500)都会干扰ESP8266对数据包的监听,因为它的监听窗口是有时序的。
解决办法有两个方向。一个是缩短监听循环里的延时,把delay(500)换成delay(50)甚至delay(1),实测成功率能提升不少。另一个是同时开启AirKiss监听,两边一起收,哪个先到用哪个。不过AirKiss需要引入其他库,代码会稍微复杂一些,非必要我不建议一上来就做。
5.3 手机端常见误操作
这一条是给交付阶段做技术支持的同学看的。配网失败不一定是设备问题,手机端操作占了很大比重。
最常见的问题是手机连着5G频段WiFi,而ESP8266只支持2.4G。手机发给设备的账号密码是“5G那个WiFi的名字和密码”,设备要连的目标网络其实就是同一个路由器下发的2.4G信号,但很多路由器会把两个频段的SSID设置成不同的名字,导致设备收到一个自己根本扫不到的SSID。解决办法是让用户确认手机连接的是2.4G频段,或者把路由器双频合一模式关掉。
第二个常见问题是配网App和开发板不在同一局域网。SmartConfig是基于WiFi报文广播的,手机和开发板必须连同一个WiFi。有些人手机开了流量,没连WiFi就开始配,当然配不上。第三,手机和开发板隔得太远、中间有金属遮挡,也会造成丢包。
我在交付文档里直接加了一段“配网前四项检查”,让用户照着做:连2.4G WiFi、关闭流量、靠近设备、确认App里密码无误。能少掉一半的售后咨询。
5.4 增加看门狗与断线重连机制
ESP8266在配网过程中如果长时间卡死,程序就失去了响应。为了防止这种情况,我建议开启硬件看门狗。Arduino框架里使用ESP8266的看门狗非常简单:
ESP.wdtDisable(); // 初始化时先关闭,配置好以后重新开 ESP.wdtEnable(8000); // 8秒喂狗一次在主循环里定期调用ESP.wdtFeed()。如果程序卡在某个死循环里超过8秒没喂狗,芯片会自动重启。配合我前面写的启动检查逻辑,重启后如果EEPROM里有旧配置就先尝试连接,这样即使意外卡死,用户也只需要等它重启,不用重新配网。
断线重连机制也很重要。设备在运行过程中难免遇到路由器重启、信号波动导致掉线,这时候不能让设备傻等。我在主循环里检测WiFi.status() != WL_CONNECTED持续超过10秒,就调用WiFi.disconnect()后重新WiFi.begin(ssid, password),再不行就重启进入配网流程。这套组合拳下来,设备的自动恢复能力基本达到了“无人值守”的要求。
6. 这个项目后续还能怎么扩展
6.1 接入MQTT和OTA
配网和掉电保存只是地基,地基打好之后,上面想盖什么楼都可以。最常见的是直接接入MQTT服务器,把设备状态推送到云端,或者接收来自App的遥控指令。ESP8266的多线程能力很弱,但单线程里跑个轻量MQTT客户端完全没问题。
OTA升级是另一个必须提前考虑的点。一旦设备已经嵌入到现场或用户家里,想更新固件就必须走OTA。我建议在写完配网和存储之后立刻把OTA烧录功能集成进项目,因为OTA需要用到WiFi连接状态,而配网保存的数据刚好为OTA提供了网络基础。不用等到产品成熟再补,越早加,后面改代码越轻松。
我这里提一个很实在的细节:OTA升级过程中掉电会导致设备变砖,所以不少方案会把新固件先写入临时分区,校验完成后再切换启动分区,也就是双分区OTA。ESP8266的Arduino库已经支持了这个功能,但需要在分区表配置里预留足够空间。如果你的板子是1MB Flash,跑完固件再跑OTA会非常紧张,建议优先用4MB Flash的板子。
6.2 多套账号密码与故障切换
有些场景下设备不止会出现在一个WiFi环境里,比如我的小车项目,一会儿在家连路由器,一会儿在户外连手机热点。我后来扩展了存储结构,在EEPROM里保存两到三组WiFi账号密码,启动时依次尝试,哪组能连上就用哪组。
这种设计的代价是存储和读取逻辑都会复杂一点,但效果很值。各组数据配上单独的魔数和校验位,互不干扰。切换逻辑写完后,我实测过从家用路由器切换到手机热点,设备在15秒内就能自动完成重连,用户体验比重新配网流畅太多。
6.3 配置清理与恢复出厂
最后建议你把“恢复出厂设置”做成一个明确的交互:长按按键超过5秒清配置并重启,或者用一个串口命令触发。这个东西在产品阶段几乎是必备功能。不然用户换路由器之后,设备一直试图连接旧WiFi连不上,还没办法手动重置,就只能拆机刷固件了,那前面做的一键配网等于白费。
我现在的实现很简单:GPIO0按键长按3秒进入配网模式、长按5秒恢复出厂并按新配置逻辑重启。中间的防抖和计时处理在前面的checkButton()里稍微扩展一下就能做到。要注意的是,长短按的逻辑需要有明确的状态反馈,比如长按3秒时LED常亮10秒、长按5秒时LED快速闪烁,否则用户不知道自己的操作有没有生效。
7. 几个提高成功率的小技巧
7.1 配网成功后延时再保存
SmartConfig收到账号密码后,不要立刻保存,先尝试连接,确认WL_CONNECTED了再写EEPROM。这样可以避免“配网工具把密码发出去了,但路由器根本连不上”的情况。等到确认连接成功再保存,用户下次启动时就能直接连上。
这个顺序需要在配网流程和保存逻辑之间做好协调。我的做法是把“连接成功”作为保存触发条件,代码上就是把WiFi.status() == WL_CONNECTED的判断放在保存函数之前。如果始终连不上,就不保存,设备继续保持监听状态,等待用户重新发送。
7.2 利用RTC Memory做状态恢复
ESP8266内部有一块RTC Memory,可以在深度睡眠或者软件重启之后保留部分数据。即使你没有用深度睡眠,RTC Memory也可以用来快速判断“上电时是否已经配过网”。
不过要注意,RTC Memory在彻底断电后数据会丢失,所以它只能作为快速判断的缓存,真正的配置必须存在EEPROM或LittleFS里。我的项目会在启动时先读RTC Memory标记,如果标记显示已经配过网,就直接走连接流程,不再等待整份EEPROM读取,能省几十毫秒启动时间,虽然不多,但积少成多。
7.3 日志打印与串口调试的艺术
配网是个很讲究时序的功能,强烈建议你在代码里保留完整的串口日志,并且按模块打上明确前缀:
#define DEBUG_SAVE 1 #define DEBUG_LOAD 1 #define DEBUG_NET 1 void debugPrint(const char* tag, const char* msg) { if (strcmp(tag, "SAVE") == 0 && DEBUG_SAVE) Serial.printf("[SAVE] %s\n", msg); if (strcmp(tag, "LOAD") == 0 && DEBUG_LOAD) Serial.printf("[LOAD] %s\n", msg); if (strcmp(tag, "NET") == 0 && DEBUG_NET) Serial.printf("[NET] %s\n", msg); }正式发布固件的时候可以把调试开关关掉,开发阶段打开。实际排查问题时,日志能立刻告诉你设备是死在了监听阶段、连接阶段还是存储阶段,省去大量盲猜的功夫。
我在项目开发过程中最常用的一条调试手段,是把手机热点名字改成一个带数字的SSID,比如TEST_AP_5,然后在代码里打印收到的SSID,对比是不是和热点名一致。这一步能快速判定手机广播的数据有没有被设备完整接收。
另外,配网时不要用手机流量来测试。手机流量模式下WiFi会断开,SmartConfig根本没法工作。别问我是怎么知道的,我拿着设备到楼下跑了两趟才意识到是手机自动切了流量。
8. 最后说点实操体会
这套“一键配网+掉电保存WiFi账号密码”的方案,我已经在温湿度采集器、智能灯控制器和小车上分别跑过,稳定性整体是令人满意的。最难调试的永远是配网阶段,但一旦把监听延时、频段选择、EEPROM提交这几个关键点理顺,后面的连接、保存、重启就基本不会出幺蛾子。
如果你准备在自己的项目里照搬这套代码,有一个建议:先搭一块最简测试板,把按键、LED和串口打印全部接出来,跑通一遍配网、保存、断电重启、长按清除的整体流程,再往正式电路上集成。不要一上来就做完整产品,否则出了问题,分不清是硬件接线问题还是软件逻辑问题,排查起来非常痛苦。
最后分享一个小技巧:把“配网模式”做成可重入的。也就是说,无论设备当前处于什么状态,只要按键长时间被按下,就能无条件进入配网模式。这样可以覆盖几乎所有现场故障。我见过不少设备卡在了一个连不上WiFi的状态里,用户不知道怎么办,最后只能用牙签捅复位孔。有了强制的重配网入口,这个问题就完全不存在了。
这个项目本身不复杂,但它把ESP8266从“烧录一次用一次”变成了“通电即用”的即插即用设备。如果你正在做类似的东西,照着上面的思路走,基本不会翻车。遇到具体问题,欢迎在评论区带上日志和串口输出,我们一起看。