ESP-IDF学习笔记-WIFI连接

使用ESP32的WIFI需要使用3个库的API,涉及NVS_FLASH,ESP_NETIF,ESP_WIFI,ESP_EVENT。nvs保存配置,netif提供tcp/ip操作接口,wifi库提供wiif的配置接口。

首先提供一下官方文档:

非易失存储库

WIFI库

ESP-NETIF

ESP_EVENT

还有描述整个WiFi编程结构的指南(非常关键的指南,放在了API指南里,要不是我搜一个函数我还找不到):

WIFI驱动指南

一,配置步骤概览

初始化NVS

NVS(非易失存储库)是ESP32分区中用来储存少量需要掉电保存的数据的分区(如果数据量很大,需要储存在文件系统的分区中)。在WIFI的配置中,该区存储了WIFI的设置,包扩上次连接的WIFI信息,上次开启的热点信息等配置信息(在设置wifi时自动储存)。因此,为了实现WiFi记忆功能,在使用WIFI前,需要先初始化NVS。如果不初始化,需要在每次运行时重新配置WiFi。

创建ESP-NETIF工作

ESP-NETIF库**提供了tcp/ip操作的相关接口(Lwip)**,与wifi驱动接口绑定后,可以处理tcp/ip的各种事务,包括DHCP等各种操作。为了使我们的WIFI能够正常联网,我们需要创建一个ESP-NETIF工作。

设置WIFI

WIFI库提供了操作WIFI的驱动,包括设置站点模式,配置WiFi信息,连接WIFI等操作。

事件循环

事件循环是ESPIDF提供的用于不同组件交流操作的库。一个组件可以创建事件,另外一个组件可以通过注册事件函数响应相应的事件。通过事件循环,各个库可以协同运行。其典型例子就是使用与WIFI库有关的组件时,比如使用DHCP获取IP地址,需要在WIFI库成功连接到WIFI之后,而DHCP的实现是高层库做的,因此就需要通过事件来通知其他组件WIFI已经连接,DHCP库通过将自己的函数注册成相应事件的处理函数,就可以实现协同运行。

二,具体配置流程

初始化NVS

首先在设置菜单中将WIFI选项中的WiFi NVS flash选中,使能WiFi_NVS_FLASH。让wifi设置能保存在NVS中。

这里直接给出代码,感兴趣可以看官方文档。

1
2
3
4
5
6
7
8
9
#include "nvs_flash.h"


esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);

初始化ESP-NETIF和创建事件循环

首先调用函数初始化netif

1
esp_err_t esp_netif_init(void)

这个函数在应用中需要被调用一次。(This function should be called exactly once from application code, when the application starts up.)

然后创建默认事件循环

1
esp_err_t esp_event_loop_create_default(void);

该函数创建了一个默认参数的事件循环,开始处理各个组件的事件。默认事件循环是一个特殊的,包含了各种系统事件处理(如wifi事件)。默认循环使用的事件注册API与用户的事件循环有一点不同以下使用的API都是默认循环的API。如果想知道用户API是什么,可以参考官方手册。

创建完成后,需要将netif的处理函数放入对应的事件中,比如在STA开启后自动开启DHCP客户端获取IP。

这里NETIF库已经写好了默认配置,直接调用函数即可。

1
esp_netif_t *esp_netif_create_default_wifi_sta(void);//sta模式

同样,如果开启了AP模式,则调用下面的函数。

1
esp_netif_t *esp_netif_create_default_wifi_ap(void);

sta/ap模式需要同时调用两个函数。

使用例子:

1
2
3
4
5
6
7
#include "esp_event.h"
#include "esp_wifi.h"
...
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());//创建默认事件处理循环
esp_netif_create_default_wifi_sta();

注册处理函数

在事件循环中加入了netif中默认的处理函数后,我们还需要注册自己的处理函数,用来实现一些功能,比如在启动wifi后开始连接ap,或者在断开连接后进行重连等。

一个事件处理函数的模板如下:

1
2
3
4
5
6
7
8
9
10
11
/*
arg是传入的参数,在注册函数时设置
event_base是事件基,表示一个大类的事件
event_id是具体的事件
event_data是传递的数据,比如在IP_EVENT 下的 IP_EVENT_STA_GOT_IP 事件会把获取到的IP地址传递过来
*/
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
...
}

创建好处理函数后,使用以下函数进行注册:

1
esp_err_t esp_event_handler_instance_register(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg, esp_event_handler_instance_t *instance);

参数:

  • event_base[in] 基事件
  • event_id[in] 具体事件id
  • event_handler[in] 要注册的处理函数
  • event_handler_arg[in] 要传递参数的指针,该函数不保留备份,因此需要确保其在被使用时有效
  • instance[out] 输出的句柄,可以用来删除(unregister)这个事件函数,如果不需要可以填NULL。

同时,如果不需要句柄,调用以下函数即可:

1
esp_err_t esp_event_handler_register(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);

上面两个函数在底层都调用了同一个函数进行注册,因此功能是一样的。

注意,对于event_base存在一个ESP_EVENT_ANY_BASE匹配全部的基事件;对于event_id存在ESP_EVENT_ANY_ID对应基事件的全部事件。即,ESP_EVENT_ANY_BASE+ESP_EVENT_ANY_ID==全部事件都响应。

对于同一事件多个处理函数,遵守先注册先调用,后注册后调用原则。(参考)

由于该规则,自己的事件处理函数注册必须要在esp_netif_create_default_wifi_sta()之后

esp_netif_create_default_wifi_sta()中注册了该组件的事件函数,为保证用户正常上网,该事件函数需在用户函数之前执行。

一部分WiFi相关事件在后文附上,也可以直接参考官方手册

示例,这里来自官方例程的一部分,调用的FreeRTOS的eventGroupAPI通知IP事件的完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "esp_wifi.h"
#include "esp_event.h"
#include "freertos/event_groups.h"

#define EXAMPLE_ESP_MAXIMUM_RETRY 10
static int s_retry_num = 0;

static EventGroupHandle_t s_wifi_event_group;


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();//STa开启后开始连接wifi
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
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);
}
}

void app_main(void)
{
...
esp_event_handler_instance_t instance_any_id;//创建句柄,用来管理该函数生命周期
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));

}

设置WiFi

处理好上层要响应的事件后,可以开始配置底层的WiFi驱动。

首先初始化WiFi驱动的资源和参数

1
2
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();//获取WIFI默认配置。这样可以给每个值都赋上初值
ESP_ERROR_CHECK(esp_wifi_init(&cfg));//初始化WIFI

使用WIFI_INIT_CONFIG_DEFAULT()获得每个变量的初值,这样可以防止某个参数忘记或设错导致的初始化失败。

在WiFi库的API被调用之前,esp_wifi_init()必须被调用。

之后设置WiFi的模式相应模式的配置

1
esp_err_t esp_wifi_set_mode(wifi_mode_t mode);//设置模式

mode有以下几种:

  • WIFI_MODE_STA:sta模式
  • WIFI_MODE_AP:ap模式
  • WIFI_MODE_APSTA:sta/ap模式

默认模式是STA。

设置相应模式的参数

1
esp_err_t esp_wifi_set_config(wifi_interface_t interface, wifi_config_t *conf);

其中wifi_interface_t如下:

1
2
3
4
typedef enum {
WIFI_IF_STA = ESP_IF_WIFI_STA,
WIFI_IF_AP = ESP_IF_WIFI_AP,
} wifi_interface_t;

wifi_config_t结构体定义如下:

1
2
3
4
typedef union {
wifi_ap_config_t ap; /**< configuration of AP */
wifi_sta_config_t sta; /**< configuration of STA */
} wifi_config_t;

这是一个联合,设置ap和sta时分别设置不同的部分。

对于wifi_sta_config_t需要关注的成员如下:

成员名 类型 作用
ssid[32] uint8_t WiFi的SSID
password[64] uint8_t WiFi的密码
scan_method wifi_scan_method_t 选择扫描模式
bssid_set bool 1:检查AP的MAC 0:不检查
bssid uint8_t 目的AP的MAC
channel uint8_t 0:通道未知 ;1-13
sort_method wifi_sort_method_t 扫描排序顺序设置
threshold wifi_scan_threshold_t 安全性强于或信号强度强于该设置的AP可被使用

wifi_scan_method_t:

  • WIFI_FAST_SCAN快速扫描模式,找到匹配的SSID后停止
  • WIFI_ALL_CHANNEL_SCAN扫描完全部通道为止

wifi_sort_method_t:

  • WIFI_CONNECT_AP_BY_SIGNAL按信号强弱整理
  • WIFI_CONNECT_AP_BY_SECURITY按加密等级整理

wifi_scan_threshold_t:

  • int8_t rssi信号的强度
  • wifi_auth_mode_t authmode最低信号的认证方式,如果想连接开放WiFi及以上,需设置为WIFI_AUTH_OPEN

wifi_auth_mode_t:

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef enum {
WIFI_AUTH_OPEN = 0, /**< authenticate mode : open */
WIFI_AUTH_WEP, /**< authenticate mode : WEP */
WIFI_AUTH_WPA_PSK, /**< authenticate mode : WPA_PSK */
WIFI_AUTH_WPA2_PSK, /**< authenticate mode : WPA2_PSK */
WIFI_AUTH_WPA_WPA2_PSK, /**< authenticate mode : WPA_WPA2_PSK */
WIFI_AUTH_WPA2_ENTERPRISE, /**< authenticate mode : WPA2_ENTERPRISE */
WIFI_AUTH_WPA3_PSK, /**< authenticate mode : WPA3_PSK */
WIFI_AUTH_WPA2_WPA3_PSK, /**< authenticate mode : WPA2_WPA3_PSK */
WIFI_AUTH_WAPI_PSK, /**< authenticate mode : WAPI_PSK */
WIFI_AUTH_OWE, /**< authenticate mode : OWE */
WIFI_AUTH_MAX
} wifi_auth_mode_t;

一个最小设置的例子:

1
2
3
4
5
6
7
8
wifi_config_t wifi_config = {
.sta = {
.ssid = "SSID",
.password = "PASSWORD",
.threshold.authmode = WIFI_AUTH_WPA_WPA2_PSK,
.sae_pwe_h2e = WPA3_SAE_PWE_BOTH,//添加这个是为了兼容最新的认证标准WPA3
},
};

对于wifi_ap_config_t需要关注的成员如下:

成员 类型 作用
ssid[32] uint8_t SSID
password[64] uint8_t 密码
ssid_len uint8_t strlen(WIFI_SSID)
channel uint8_t 选择通道
authmode wifi_auth_mode_t 选择认证方式
ssid_hidden uint8_t 是否广播SSID(SSID是否可见)
max_connection uint8_t 最大连接数

一个最小的config如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
wifi_config_t wifi_config = {
.ap = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
.channel = EXAMPLE_ESP_WIFI_CHANNEL,
.password = EXAMPLE_ESP_WIFI_PASS,
.max_connection = EXAMPLE_MAX_STA_CONN,
.authmode = WIFI_AUTH_WPA_WPA2_PSK,
.pmf_cfg = {
.required = false,//这里关闭了对pmf的要求,让其能连接更多设备
},
},
};

如果开启了nvs,esp_wifi_set_config()设置会被储存在nvs中,下次不用配置,可以使用esp_wifi_get_config()获取设置。

当一切都配置好后,可以开启WiFi:

1
esp_err_t esp_wifi_start(void);

开启后,可以在事件循环中使用esp_err_t esp_wifi_connect(void)去连接WiFi,参考上面事件函数中的代码。

对于已经连接的AP,使用以下API获得信息:

1
esp_err_t esp_wifi_sta_get_ap_info(wifi_ap_record_t *ap_info);

注意:在获得IP前,禁止一切socket操作。

三,扫描WiFi

在通常的应用中,我们不可能将WiFi信息固定。因此,在连接WiFi前先扫描WiFi是十分必要的,扫描仅在sta或sta/ap模式使用。

扫描使用以下API:

1
esp_err_t esp_wifi_scan_start(const wifi_scan_config_t *config, bool block);

其中,block用来确定是否阻塞式扫描。如果为1,将会阻塞直到扫描完成。否则,会立刻返回,结果一般在事件循环中处理。

config用来配置扫描设置,具有以下字段:

  • uint8_t *ssid:AP的SSID,如果不为NULL,则仅扫描相同NULL

  • uint8_t *bssid:AP的MAC,如果不为NULL,则仅扫描相同MAC

  • uint8_t channel:如果该字段值为 0,将进行全信道扫描;反之,将针对特定信道进行扫描。

  • bool show_hidden:如果该字段值为 0,本次扫描将忽略具有隐藏 SSID 的 AP;反之,这些 AP 也会在扫描时被视为正常 AP。

  • wifi_scan_type_t scan_type:WIFI_SCAN_TYPE_ACTIVE主动扫描; WIFI_SCAN_TYPE_PASSIVE 被动扫描

  • wifi_scan_time_t scan_time:用来控制扫描时间,不用关心。

将该参数设为NULL则使用默认扫描。

该函数扫描出的结果会储存在内存中,直到调用esp_wifi_scan_get_ap_records()或者esp_err_t esp_wifi_clear_ap_list(void)释放。

主动扫描:通过发送 probe request 进行扫描。该模式为默认的扫描模式。

被动扫描:不发送 probe request。跳至某一特定信道并等待 beacon。应用程序可通过 wifi_scan_config_t 中的 scan_type 字段使能被动扫描。

调用 API sp_wifi_set_config() 可全局配置一些扫描属性,请参阅 station的配置。

如果国家信息有误,调用函数 esp_wifi_set_country() 进行配置。

当全部扫描完成后,会产生WIFI_EVENT_SCAN_DONE事件,在该事件函数中,使用以下API获得扫描得到的AP数量:

1
esp_err_t esp_wifi_scan_get_ap_num(uint16_t *number);

获得AP数量后,使用以下API获得具体信息:

1
esp_err_t esp_wifi_scan_get_ap_records(uint16_t *number, wifi_ap_record_t *ap_records);

参数:

  • number:该参数是一个输入输出参数。但作为输入参数,它指示了ap_records最多储存的数量。作为输出参数,它则与esp_err_t esp_wifi_scan_get_ap_num(uint16_t *number);输出一样。
  • ap_records:储存扫描到AP信息的参数

ap_records:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
typedef struct {
uint8_t bssid[6]; /**< MAC address of AP */
uint8_t ssid[33]; /**< SSID of AP */
uint8_t primary; /**< channel of AP */
wifi_second_chan_t second; /**< secondary channel of AP */
int8_t rssi; /**< signal strength of AP */
wifi_auth_mode_t authmode; /**< authmode of AP */
wifi_cipher_type_t pairwise_cipher; /**< pairwise cipher of AP */
wifi_cipher_type_t group_cipher; /**< group cipher of AP */
wifi_ant_t ant; /**< antenna used to receive beacon from AP */
uint32_t phy_11b:1; /**< bit: 0 flag to identify if 11b mode is enabled or not */
uint32_t phy_11g:1; /**< bit: 1 flag to identify if 11g mode is enabled or not */
uint32_t phy_11n:1; /**< bit: 2 flag to identify if 11n mode is enabled or not */
uint32_t phy_lr:1; /**< bit: 3 flag to identify if low rate is enabled or not */
uint32_t wps:1; /**< bit: 4 flag to identify if WPS is supported or not */
uint32_t ftm_responder:1; /**< bit: 5 flag to identify if FTM is supported in responder mode */
uint32_t ftm_initiator:1; /**< bit: 6 flag to identify if FTM is supported in initiator mode */
uint32_t reserved:25; /**< bit: 7..31 reserved */
wifi_country_t country; /**< country information of AP */
} wifi_ap_record_t;

一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "esp_wifi.h"


#define DEFAULT_SCAN_LIST_SIZE 10

...
esp_wifi_scan_start(NULL, true);
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info));
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
ESP_LOGI(TAG, "Total APs scanned = %u", ap_count);
for (int i = 0; (i < DEFAULT_SCAN_LIST_SIZE) && (i < ap_count); i++) {
ESP_LOGI(TAG, "SSID \t\t%s", ap_info[i].ssid);
ESP_LOGI(TAG, "RSSI \t\t%d", ap_info[i].rssi);
print_auth_mode(ap_info[i].authmode);
if (ap_info[i].authmode != WIFI_AUTH_WEP) {
print_cipher_type(ap_info[i].pairwise_cipher, ap_info[i].group_cipher);
}
ESP_LOGI(TAG, "Channel \t\t%d\n", ap_info[i].primary);
}
...

附录-WiFi事件描述

该部分来自官方文档,在这里做参考。以下为具体事件名,头两个单词同时也是基事件名。

  • WIFI_EVENT_WIFI_READY
    Wi-Fi 驱动程序永远不会生成此事件,因此,应用程序的事件回调函数可忽略此事件。在未来的版本中,此事件可能会被移除。

  • WIFI_EVENT_SCAN_DONE
    扫描完成事件,由 esp_wifi_scan_start() 函数触发,将在以下情况下产生:

    扫描已完成,例如:Wi-Fi 已成功找到目标 AP 或已扫描所有信道。

    当前扫描因函数 esp_wifi_scan_stop() 而终止。

    在当前扫描完成之前调用了函数 esp_wifi_scan_start()。此时,新的扫描将覆盖当前扫描过程,并生成一个扫描完成事件。

    以下情况下将不会产生扫描完成事件:

    ​ 当前扫描被阻止。

    ​ 当前扫描是由函数 esp_wifi_connect() 触发的。

    接收到此事件后,事件任务暂不做任何响应。首先,应用程序的事件回调函数需调用 esp_wifi_scan_get_ap_num() 和 esp_wifi_scan_get_ap_records() 获取已扫描的 AP 列表,然后触发 Wi-Fi 驱动程序释放在扫描过程中占用的内存空间(切记该步骤)。 更多详细信息,请参阅 ESP32-S3 Wi-Fi 扫描。

  • WIFI_EVENT_STA_START
    如果调用函数 esp_wifi_start() 后接收到返回值 ESP_OK,且当前 Wi-Fi 处于 station 或 station/AP 共存模式,则将产生此事件。接收到此事件后,事件任务将初始化 LwIP 网络接口 (netif)。通常,应用程序的事件回调函数需调用 esp_wifi_connect() 来连接已配置的 AP。

  • WIFI_EVENT_STA_STOP
    如果调用函数 esp_wifi_stop() 后接收到返回值 ESP_OK,且当前 Wi-Fi 处于 station 或 station/AP 共存模式,则将产生此事件。接收到此事件后,事件任务将进行释放 station IP 地址、终止 DHCP 客户端服务、移除 TCP/UDP 相关连接并清除 LwIP station netif 等动作。此时,应用程序的事件回调函数通常不需做任何响应。

  • WIFI_EVENT_STA_CONNECTED
    如果调用函数 esp_wifi_connect() 后接收到返回值 ESP_OK,且 station 已成功连接目标 AP,则将产生此连接事件。接收到此事件后,事件任务将启动 DHCP 客户端服务并开始获取 IP 地址。此时,Wi-Fi 驱动程序已准备就绪,可发送和接收数据。如果您的应用程序不依赖于 LwIP(即 IP 地址),则此刻便可以开始应用程序开发工作。但是,如果您的应用程序需基于 LwIP 进行,则还需等待 got ip 事件发生后才可开始。

  • WIFI_EVENT_STA_DISCONNECTED
    此事件将在以下情况下产生:

    ​ 1. 调用了函数 esp_wifi_disconnect() 或 esp_wifi_stop(),且 Wi-Fi station 已成功连接至 AP。

    ​ 2. 调用了函数 esp_wifi_connect(),但 Wi-Fi 驱动程序因为某些原因未能成功连接至 AP,例如:未扫描到目标 AP、验证超时等。 或存在多个 SSID 相同的 AP,station 无法连接所有已找到的 AP,也将产生该事件。

    ​ 3. Wi-Fi 连接因为某些原因而中断,例如:station 连续多次丢失 N beacon、AP 踢掉 station、AP 认证模式改变等。

    接收到此事件后,事件任务的默认动作为:

    ​ 1. 关闭 station 的 LwIP netif。

    ​ 2. 通知 LwIP 任务清除导致所有套接字状态错误的 UDP/TCP 连接。针对基于套接字编写的应用程序,其回调函数可以在接收到此 事件时(如有必要)关闭并重新创建所有套接字。

    应用程序处理此事件最常用的方法为:调用函数 esp_wifi_connect() 重新连接 Wi-Fi。但是,如果此事件是由函数 esp_wifi_disconnect() 引发的,则应用程序不应调用 esp_wifi_connect() 来重新连接。应用程序须明确区分此事件的引发原因,因为某些情况下应使用其它更好的方式进行重新连接。请参阅 Wi-Fi 重新连接 和 连接 Wi-Fi 时扫描。

    需要注意的另一点是:接收到此事件后,LwIP 的默认动作是终止所有 TCP 套接字连接。大多数情况下,该动作不会造成影响。但对某些特殊应用程序可能除外。例如:

    应用程序创建一个了 TCP 连接,以维护每 60 秒发送一次的应用程序级、保持活动状态的数据。

    由于某些原因,Wi-Fi 连接被切断并引发了 WIFI_EVENT_STA_DISCONNECTED 事件。根据当前实现,此时所有 TCP 连接都将被移除,且保持活动的套接字将处于错误的状态中。但是,由于应用程序设计者认为网络层 不应 考虑这个 Wi-Fi 层的错误,因此应用程序不会关闭套接字。

    5 秒后,因为在应用程序的事件回调函数中调用了 esp_wifi_connect(),Wi-Fi 连接恢复。同时,station 连接至同一个 AP 并获得与之前相同的 IPV4 地址。

    60 秒后,当应用程序发送具有保持活动状态的套接字的数据时,套接字将返回错误,应用程序将关闭套接字并在必要时重新创建。

    在上述场景中,理想状态下应用程序套接字和网络层将不会受到影响,因为在此过程中 Wi-Fi 连接只是短暂地断开然后快速恢复。应用程序可通过 LwIP menuconfig 启动“IP 改变时保持 TCP 连接”的功能。

  • IP_EVENT_STA_GOT_IP
    当 DHCP 客户端成功从 DHCP 服务器获取 IPV4 地址或 IPV4 地址发生改变时,将引发此事件。此事件意味着应用程序一切就绪,可以开始任务(如:创建套接字)。

    IPV4 地址可能由于以下原因而发生改变:

    ​ 1. DHCP 客户端无法重新获取/绑定 IPV4 地址,且 station 的 IPV4 重置为 0。

    ​ 2. DHCP 客户端重新绑定了其它地址。

    ​ 3. 静态配置的 IPV4 地址已发生改变。

    函数 ip_event_got_ip_t 中的字段 ip_change 说明了 IPV4 地址是否发生改变。

    套接字的状态是基于 IPV4 地址的,这意味着,如果 IPV4 地址发生改变,则所有与此 IPV4 相关的套接字都将变为异常。接收到此事件后,应用程序需关闭所有套接字,并在 IPV4 变为有效地址时重新创建应用程序。

  • IP_EVENT_GOT_IP6
    当 IPV6 SLAAC 支持自动为 ESP32-S3 配置一个地址,或 ESP32-S3 地址发生改变时,将引发此事件。此事件意味着应用程序一切就绪,可以开始任务(如:创建套接字)。

  • IP_EVENT_STA_LOST_IP
    当 IPV4 地址失效时,将引发此事件。

    此事件不会在 Wi-Fi 断连后立刻出现。Wi-Fi 连接断开后,首先将启动一个 IPV4 地址丢失计时器,如果 station 在该计时器超时之前成功获取了 IPV4 地址,则不会发生此事件。否则,此事件将在计时器超时时发生。

    一般来说,应用程序可忽略此事件。这只是一个调试事件,主要使应用程序获知 IPV4 地址已丢失。

  • WIFI_EVENT_AP_START
    与 WIFI_EVENT_STA_START 事件相似。

  • WIFI_EVENT_AP_STOP
    与 WIFI_EVENT_STA_STOP 事件相似。

  • WIFI_EVENT_AP_STACONNECTED
    每当有一个 station 成功连接 ESP32-S3 AP 时,将引发此事件。接收到此事件后,事件任务将不做任何响应,应用程序的回调函数也可忽略这一事件。但是,您可以在此时进行一些操作,例如:获取已连接 station 的信息等。

  • WIFI_EVENT_AP_STADISCONNECTED
    此事件将在以下情况下发生:

    ​ 1. 应用程序通过调用函数 esp_wifi_disconnect() 或 esp_wifi_deauth_sta() 手动断开 station 连接。

    ​ 2. Wi-Fi 驱动程序出于某些原因断开 station 连接,例如:AP 在过去 5 分钟(可通过函数 esp_wifi_set_inactive_time() 修改该时间)内未接收到任何数据包等。

    ​ 3. station 断开与 AP 之间的连接。

    发生此事件时,事件任务将不做任何响应,但应用程序的事件回调函数需执行一些操作,例如:关闭与此 station 相关的套接字等。

  • WIFI_EVENT_AP_PROBEREQRECVED
    默认情况下,此事件处于禁用状态,应用程序可以通过调用 API esp_wifi_set_event_mask() 启用。 启用后,每当 AP 接收到 probe request 时都将引发此事件。

  • WIFI_EVENT_STA_BEACON_TIMEOUT
    如果 station 在 inactive 时间内未收到所连接 AP 的 beacon,将发生 beacon 超时,将引发此事件。inactive 时间通过调用函数 esp_wifi_set_inactive_time() 设置。

  • WIFI_EVENT_CONNECTIONLESS_MODULE_WAKE_INTERVAL_START
    非连接模块在 Interval 开始时触发此事件。 请参考 非连接模块功耗管理 。