4、断网检测与状态管理:如何实时监测WiFi连接状态、网络质量评估、状态机设计(在线/离线/恢复)

在ESP32离线可用设计中,断网检测与状态管理是决定系统鲁棒性的核心环节。许多开发者误以为“WiFi连接成功”就等于“网络可用”,但实际上,连接建立、IP获取、DNS解析、外网连通性、信号强度波动、路由器重启、运营商故障等,都是影响网络真实可用性的关键因素。本章将围绕“如何准确判断网络状态”“如何评估网络质量”“如何设计状态机以平滑处理在线、离线与恢复三种状态”展开,帮助你在ESP32上构建一套可靠、可观测、可自愈的网络状态管理机制。

ESP32 网络状态监测与状态机设计 ① WiFi连接层 · SSID/密码校验 · 关联认证 (WPA2/WPA3) · DHCP获取IP地址 · 信号强度 RSSI 监测 · 重连/漫游处理 状态: WL_CONNECTED / WL_DISCONNECTED ② 网络质量评估层 · 外网连通性探测 (Ping) · DNS 解析测试 · TCP 连接测试 (端口) · 响应时间 RTT 统计 · 丢包率计算 指标: 质量评分 0~100 ③ 状态机管理层 · 在线 (Online) · 离线 (Offline) · 恢复 (Recovering) · 状态迁移条件 · 事件回调/日志记录 策略: 降级/重试/补传 状态迁移核心逻辑 在线 正常收发数据 离线 本地存储/降级 恢复中 探测/重连/补传 连续失败≥N次 探测成功 质量达标 → 恢复在线 设计要点:状态迁移需防抖、防抖动,避免频繁切换导致系统不稳定

上图展示了ESP32网络状态管理的三层架构:底层是WiFi连接层,负责物理链路与IP获取;中间是网络质量评估层,通过主动探测判断网络是否真正可用;顶层是状态机管理层,根据评估结果驱动状态迁移,并触发相应的业务策略(如本地缓存、断网降级、恢复补传)。下面我们逐一深入。

4.1 核心原理:为什么“WiFi已连接”不等于“网络可用”

ESP32的WiFi库通常提供 WiFi.status() 来判断连接状态,返回 WL_CONNECTED 表示已成功关联AP并获取IP。但这一状态只说明“链路层”和“网络层”就绪,并不代表“应用层”可通信。典型场景包括:

  • 路由器WAN口断开:ESP32仍能连上路由器,但路由器本身没有外网,此时Ping公网IP必然失败。
  • DNS故障:路由器DHCP下发的DNS服务器失效,域名解析失败,但IP直连可能正常。
  • 信号弱但未断开:RSSI低于-80dBm时,虽然连接未断开,但丢包率极高,实际无法稳定传输数据。
  • AP隔离或防火墙:某些公共WiFi开启AP隔离,设备间无法互通,或对非标准端口限流。

因此,必须建立“连接状态”与“可用状态”的区分。连接状态由WiFi库维护,可用状态则需要应用层主动探测。状态机的设计正是基于这两层信息的融合。

4.2 判断方法:实时监测WiFi连接状态与网络质量评估

我们将监测分为三个层次,每层都有对应的实现方法和关键参数。

4.2.1 第一层:WiFi连接状态监测

ESP32的WiFi事件系统(WiFi.onEvent())可以捕获连接、断开、获取IP等事件。推荐使用事件回调而非轮询,以减少CPU占用并提高实时性。

// 事件回调示例
void WiFiEvent(WiFiEvent_t event) {
    switch (event) {
        case ARDUINO_EVENT_WIFI_STA_CONNECTED:
            Serial.println("已关联AP");
            break;
        case ARDUINO_EVENT_WIFI_STA_GOT_IP:
            Serial.printf("获取IP: %s\n", WiFi.localIP().toString().c_str());
            break;
        case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
            Serial.println("连接断开,准备重连");
            WiFi.reconnect();
            break;
    }
}
void setup() {
    WiFi.onEvent(WiFiEvent);
    WiFi.begin("SSID", "PASSWORD");
}

同时,定期读取 WiFi.RSSI() 获取信号强度。RSSI的典型阈值参考:

RSSI范围信号质量建议策略
-30 ~ -50 dBm极佳正常通信,可进行大数据量传输
-50 ~ -65 dBm良好正常通信,注意偶尔重传
-65 ~ -75 dBm一般降低发送频率,增加重试机制
-75 ~ -85 dBm较差仅传输关键数据,考虑本地缓存
低于 -85 dBm极差视为离线,切换本地模式

4.2.2 第二层:网络质量主动探测

仅靠RSSI不够,必须主动探测外网连通性。常用方法有三种,各有优劣:

  1. Ping测试:使用 ping.h 库向公网IP(如223.5.5.5)发送ICMP包。优点:直接反映网络层连通性;缺点:某些网络禁ICMP,且Ping通不代表TCP端口可用。
  2. TCP连接测试:尝试与已知服务器(如阿里云DNS 223.5.5.5:53)建立TCP连接。优点:更接近真实业务;缺点:需要服务器开放端口。
  3. HTTP/HTTPS请求:向自己的后端服务器发送一个轻量级请求(如GET /health)。优点:最真实;缺点:消耗流量,且服务器故障会误判。

推荐组合策略:先Ping公网IP判断网络层,再TCP连接判断传输层,最后HTTP请求判断应用层。但考虑到ESP32资源有限,实际项目中通常选择其中一种作为主要判据。以下是一个基于Ping的探测实现:

#include <ping.h>
bool checkNetwork() {
    bool ret = ping_start("223.5.5.5", 3, 0, 0, 5); // 3次,超时5秒
    return ret;
}

更精细的质量评估可以计算RTT平均值和丢包率,并映射为0~100的评分。例如:

int qualityScore = 100;
if (avgRTT > 200) qualityScore -= 20;
if (avgRTT > 500) qualityScore -= 30;
if (lossRate > 20) qualityScore -= 40;
if (lossRate > 50) qualityScore = 0;

4.2.3 第三层:状态机设计(在线/离线/恢复)

状态机是管理网络状态的核心。我们设计三个稳定状态和若干迁移条件:

  • 在线(ONLINE):网络质量评分≥60,且最近一次探测成功。此状态下正常发送数据,并定期(如每30秒)进行轻量探测。
  • 离线(OFFLINE):连续N次(如3次)探测失败,或WiFi断开且重连失败。此状态下所有数据写入本地日志(SPIFFS/LittleFS),并停止网络重试(或降低重试频率)。
  • 恢复(RECOVERING):从离线状态检测到WiFi重新连接,或单次探测成功。此状态下进行密集探测(如每5秒一次),连续2次成功则转入在线,否则回到离线。

状态迁移必须加入防抖(Debounce)机制,避免因单次抖动导致状态频繁切换。例如,从在线到离线需要连续3次失败,从离线到恢复需要1次成功,从恢复到在线需要连续2次成功。

设计要点:状态机应作为独立模块运行,通过事件或回调通知业务层。业务层只关心当前状态,不关心底层探测细节。例如,当状态变为OFFLINE时,业务层自动切换到本地存储模式;当变为ONLINE时,触发补传任务。

4.3 案例拆解:智能家居网关的断网自愈与数据补传

下面通过一个实际项目案例,完整展示状态管理的落地过程。

背景与约束:某智能家居项目使用ESP32作为家庭网关,采集温湿度、门窗状态等传感器数据,每10秒上报一次到云端。要求:家庭路由器偶尔重启,运营商网络不稳定,且设备安装在弱电箱内,WiFi信号较弱(RSSI约-70dBm)。

初始现象:设备经常出现“假连接”——WiFi显示已连接,但云端长时间收不到数据。排查发现,路由器WAN口断开时,ESP32仍保持连接,但所有HTTP请求超时。

关键参数与日志:

// 修改前的日志片段
[10:00:01] WiFi Connected, IP: 192.168.1.100
[10:00:11] HTTP POST timeout
[10:00:21] HTTP POST timeout
[10:00:31] WiFi still connected, but no internet
// 修改后增加状态日志
[10:00:01] State: ONLINE, RSSI: -68dBm, RTT: 45ms
[10:00:11] Probe failed (1/3)
[10:00:21] Probe failed (2/3)
[10:00:31] Probe failed (3/3) → State: OFFLINE
[10:00:41] Local storage enabled, buffered 3 records

分析判断过程:通过增加Ping探测,发现路由器WAN口断开时,Ping公网IP失败,但WiFi连接正常。因此,将“WiFi连接”与“网络可用”解耦,引入状态机后,系统能准确识别离线状态。

实际操作步骤:

  1. 在原有WiFi事件回调基础上,增加Ping探测任务(每10秒一次)。
  2. 实现状态机模块,定义三个状态和迁移条件(连续3次失败→离线,1次成功→恢复,连续2次成功→在线)。
  3. 离线时,传感器数据写入LittleFS的日志文件(每条记录约50字节,可存储数万条)。
  4. 恢复在线后,启动补传任务:按时间戳顺序逐条上传,每成功一条删除一条,并记录补传进度。
  5. 增加看门狗定时器,若状态机卡死(如长时间处于RECOVERING),自动重启WiFi模块。

结果对比:改造前,断网期间数据全部丢失,恢复后无法追溯;改造后,断网2小时内的数据完整补传,云端数据连续性达到99.9%。同时,由于离线时不再频繁尝试HTTP请求,设备功耗降低了约30%。

风险与代价:补传期间会占用带宽,可能影响实时数据上报。解决方案是限制补传速率(如每秒最多5条),并优先补传最近的数据(时间倒序)。另外,本地日志存储需考虑Flash磨损,建议使用环形缓冲区或定期清理。

常见误区:不要将“WiFi重连成功”直接等同于“网络恢复”。重连后仍需进行质量探测,否则可能进入“恢复→在线→离线”的快速循环,导致系统不稳定。务必在恢复状态中设置足够的探测次数和成功阈值。

4.4 常见误区与总结复盘

在实际项目中,以下误区最容易导致断网检测失效:

  • 误区一:只依赖WiFi.status()。如前所述,连接状态不等于可用状态。必须增加应用层探测。
  • 误区二:探测频率过高。每1秒Ping一次会消耗大量资源,且可能被路由器限流。建议在线时30秒一次,离线时10秒一次,恢复时5秒一次。
  • 误区三:忽略RSSI变化趋势。RSSI是瞬时值,波动大。应使用滑动平均(如最近5次取平均)来判断信号趋势,避免因瞬时抖动误判。
  • 误区四:状态切换无防抖。单次失败立即切换离线,会导致系统在信号波动时频繁切换,影响业务稳定性。
  • 误区五:补传逻辑与状态机耦合。补传应作为独立任务,由状态迁移事件触发,而不是在状态机内部实现,否则状态机逻辑会变得臃肿且难以调试。