ESP32 WiFi自动重连必须基于STA模式并配合事件驱动机制,而非仅依赖WiFi.begin();需监听WIFI_EVENT_STA_DISCONNECTED触发重连,结合指数退避策略(如1s→2s→4s)避免高频重试,并在IP_EVENT_STA_GOT_IP确认网络可用后启动业务。

WiFi自动重连必须用STA模式+状态轮询,不能只靠WiFi.begin()
ESP32的WiFi.begin()只是发起一次连接请求,失败后不会重试。实际项目中路由器可能重启、信号短暂中断,设备必须自己检测并恢复连接。核心逻辑是:保持WIFI_STA模式,用WiFi.status()持续判断状态,并在断开时主动调用WiFi.reconnect()而非重复begin()。
常见错误是写成死循环等待连接成功:while (WiFi.status() != WL_CONNECTED) delay(500);——这会卡住整个程序,无法响应BLE事件或看门狗复位。正确做法是把连接逻辑放进loop()里,配合超时计数和退避策略:
- 每次检测到
WL_DISCONNECTED或WL_NO_SSID_AVAIL,记录时间戳 - 首次重连延迟1秒,失败后指数退避(2s → 4s → 8s),避免高频扫描耗电
- 连续失败5次后进入深度睡眠或触发BLE配网入口
- 连接成功后调用
WiFi.setAutoReconnect(true)开启底层自动重连(但仅对瞬时掉线有效,不替代上层逻辑)
NimBLE库比官方BLE库更适合低功耗广播场景
官方Arduino ESP32 BLE库基于旧版Bluedroid,RAM占用高、广播间隔最小只能设到100ms,且不支持广播数据动态更新。NimBLE-Arduino(h2zero维护)编译后固件小30%,广播间隔可低至20ms,还支持NimBLEAdvertising::setScanResponse()分离广播包与扫描响应包——这对配网场景很关键:主广播包放设备名和Service UUID,扫描响应包才放WiFi状态等动态信息,减少广播负载。
容易踩的坑是直接用NimBLEDevice::startAdvertising()后就不管了。实际中需监听NIMBLE_GAP_EVENT_CONNECT和NIMBLE_GAP_EVENT_DISCONNECT事件,在断连后立即重启广播(否则手机App扫不到设备):
立即学习“C++免费学习笔记(深入)”;
- 调用
pAdvertising->stop()前先检查pAdvertising->isAdvertising(),避免空指针崩溃 - 重启广播前加
delay(10),否则NimBLE底层可能报GAP_ERR_INVALID_STATE - 广播数据长度超过31字节时,必须启用扩展广播(
setExtAdvData()),否则被截断
Wi-Fi与BLE共存时,必须手动协调射频资源
ESP32的Wi-Fi和BLE共享同一颗2.4GHz射频前端,同时高强度工作会互相干扰。典型现象是:Wi-Fi连接成功后BLE广播信号强度骤降10dBm,或手机App频繁断连。这不是代码bug,而是硬件资源争抢。
解决方案不是关一个开一个,而是用ESP-IDF底层API做时间分片:
- 调用
wifi_set_ps(WIFI_PS_MAX_MODEM)启用Wi-Fi最大省电模式,让Wi-Fi在空闲时让出射频给BLE - 在BLE广播回调中,用
esp_wifi_set_max_tx_power(78)临时降低Wi-Fi发射功率(单位0.25dBm),避免压制BLE信号 - 若使用ESP32-S3/C3/C6,可启用
esp_coex_preference_set(ESP_COEX_PREFER_BLE)强制BLE优先级更高 - 切忌在
loop()里高频调用WiFi.scanNetworks()——扫描时Wi-Fi会独占射频,BLE广播完全中断
保存WiFi凭据必须用NVS,别碰SPIFFS或EEPROM模拟
很多教程用Preferences库存WiFi密码,但没说明它底层就是NVS(Non-Volatile Storage)。NVS是ESP32专用的键值存储区,擦写寿命10万次,支持加密分区,而SPIFFS是文件系统,用来存网页或日志更合适;EEPROM模拟则依赖Flash扇区,频繁写入易损坏。
配置要点:
- 初始化时调用
prefs.begin("wifi", false),第二个参数false表示只读打开,写入前必须prefs.end()再begin("wifi", true) - 密码字段必须用
prefs.putString("pass", password),不能用putBytes()传明文——NVS本身不加密,敏感字段需自行AES加密 - 首次启动未配网时,
prefs.getString("ssid", "")返回空字符串,此时应跳转到BLE配网服务,而非尝试连接空SSID - 删除凭据用
prefs.clear(),不要逐个remove(),否则残留key可能撑满NVS分区
最常被忽略的是NVS分区表大小。默认Arduino IDE生成的分区表只给NVS留0x6000字节(24KB),存几百个设备凭据够用,但若同时存OTA校验码、设备证书、蓝牙绑定信息,很快会NVS_NOT_ENOUGH_SPACE。烧录前务必检查partitions.csv里nvs行的size字段,建议设为0x10000(64KB)。


















