此篇博客用来分析 ESP Wi-Fi 连接异常断开的现象。
1 Wi-Fi 连接的过程
以下是一段 Wi-Fi 顺利连接过程的 log。其中可以清晰的看出 state 的转移(init-> auth -> assoc -> run)。进入 run 状态后,立刻就是 4 way handshake 过程。这其中每个过程出问题,都会让 state 直接转移到 init 状态。并且给出对应的原因。
[2020-06-16 20:52:41] I (18074) wifi:new:<13,2>, old:<13,2>, ap:<13,2>, sta:<13,0>, prof:13
[2020-06-16 20:52:41] I (18075) wifi:state: init -> auth (b0)
[2020-06-16 20:52:41] I (18084) wifi:state: auth -> assoc (0)
[2020-06-16 20:52:41] I (18091) wifi:state: assoc -> run (10)
[2020-06-16 20:52:41] I (18096) wpa: <EAPOL>state:6
[2020-06-16 20:52:41] I (18097) wpa: <EAPOL>receiving the 1/4 EAPOL-Key, state:6
[2020-06-16 20:52:41] I (18105) wpa: <EAPOL>state:7
[2020-06-16 20:52:41] I (18106) wpa: <EAPOL>receiving the 3/4 EAPOL-Key, state:7
[2020-06-16 20:52:41] I (18106) wifi:connected with HUAWEI-B311-62AD, aid = 2, channel 13, BW20, bssid = 44:55:c4:3a:62:ad
2 Wi-Fi 连接 & 运行中出错分析
Wi-Fi 连接 & 运行中出错都会让状态转移到 init,并且 log 里会有 16 进制数表示,后两位表示收到的管理帧的类型代码,前两位表示 Reason。举例如下:
wifi:state: run -> init (c800)
此时错误原因是 c8,对应收到的管理帧是 00。错误原因也可以在代码中实现打印。ESP-IDF 里的 station 示例 改动如下:
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
wifi_event_sta_disconnected_t *disconnected = (wifi_event_sta_disconnected_t*) event_data;
ESP_LOGE(TAG, "Disconnect reason : %d", disconnected->reason);
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
ESP_LOGI(TAG,"connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
其中以下两行为添加(改动)的部分:
wifi_event_sta_disconnected_t *disconnected = (wifi_event_sta_disconnected_t*) event_data;
ESP_LOGE(TAG, "Disconnect reason : %d", disconnected->reason);
Wi-Fi 连接异常打印的原因 log 如下:
I (11062) wifi station: retry to connect to the AP
I (11062) wifi station: connect to the AP fail
E (13112) wifi station: Disconnect reason : 201
常见的帧类型代码有 00 (什么都没收到,表示超时)、A0(disassoc)、B0(auth)和 C0(deauth)。前两位的原因可以从 Wi-Fi Reason Code 里查看。后两位可以直接从管理帧代码里查看。
常见管理帧如下:
名称 | 代码 | 功能 |
---|---|---|
ASSOC_REQ | 0X00 | 关联请求帧 |
ASSOC_RESP | 0X10 | 关联请求回复帧 |
REASSOC_REQ | 0X20 | 再关联请求帧 |
REASSOC_RESP | 0X30 | 再关联请求回复帧 |
PROBE_REQ | 0X40 | 探测请求帧 |
PROBE_RESP | 0X50 | 探测请求回复帧 |
BEACON | 0X80 | 信标帧 |
ATIM | 0X90 | |
DISASSOC | 0XA0 | 解关联帧 |
AUTH | 0XB0 | 认证帧 |
DEAUTH | 0XC0 | 解认证帧 |
ACTION | 0XD0 | 不需要 ack 的 Action 帧 |
- 详细功能可以参考 WLAN 无线网络 09 - 管理帧
- ASSOC_REQ 的代码是 00 ,但是 ASSOC_REQ 不会从 STA 上发出,所以 00 被用来当作超时的代码
3 实例分析
3.1 Auth 过程出错
这里有一段 Auth 过程出错的 log。
I (15211) wifi:new:<13,2>, old:<13,0>, ap:<13,2>, sta:<13,0>, prof:13
I (15212) wifi:state: init -> auth (b0)
W (15275) [main, 288]: device login is not ready.
W (15775) [main, 288]: device login is not ready.
I (16213) wifi:state: auth -> init (200)
I (16213) wifi:new:<13,0>, old:<13,2>, ap:<13,2>, sta:<13,0>, prof:13
I (16214) [mwifi, 176]: Parent is disconnected, reason: 2
可以看出,Auth 过程失败了,状态转移到了 init,并且括号里给出数字 200。根据上述过程分析,后两位 00 表示超时,前两位是 02(0 被省略了),表示 AUTH_EXPIRE。合起来就是 Auth 超时。
3.2 Assoc 过程出错
这里是一段 Assoc 过程出错的 log。
I (11562) wifi:new:<13,2>, old:<13,2>, ap:<13,2>, sta:<13,0>, prof:13
I (11562) wifi:state: init -> auth (b0)
I (11568) wifi:state: auth -> assoc (0)
W (11773) [main, 288]: device login is not ready.
W (12273) [main, 288]: device login is not ready.
I (12569) wifi:state: assoc -> init (400)
I (12570) wifi:new:<13,0>, old:<13,2>, ap:<13,2>, sta:<13,0>, prof:13
I (12570) [mwifi, 176]: Parent is disconnected, reason: 4
可以看出,状态转移到 init 后括号里给出的数字是(400),同理,4 表示 ASSOC_EXPIRE,意味着 Assoc 超时。
3.3 其他常见数字示例(log 略)
- (6c0): c0 是 deauth,6 表示 not_authed。当时 ESP32 处于 STA 模式,意味着收到了来自 AP 的 deauth 帧。AP 告诉 STA 还没有 auth。
- (c800): 00 是超时,c8 十进制数是 200,表示 beacon timeout。合起来就是 STA 侧收不到来自 AP 的 beacon 帧了。
3 总结
通过以上的过程分析,就可以通过 Wi-Fi 的 log 看出断开的原因,即使应用层没有在事件处理函数里打印 disconnected 里的 reason。但是这也不是万能的,想要找出更深层次的原因,例如数字(200、400)出现的原因,就只能抓取空中的 802.11 无线帧来看了,对应的抓包教程参考如下:
注:抓包是最好不要设置 Wi-Fi 的密码,这样方便后续分析。