主要函数解析
函数esp_netif_init()
esp_err_t esp_netif_init(void)
ESP32的WIFI连接是基于TCP服务的。这个函数的作用就是初始化底层的TCP/IP堆栈。可以说ESP32的WIFI模块依赖于这个函数。在使用WIFI之前需要先利用这个函数初始化TCP/IP。
函数esp_event_loop_create_default()
esp_err_t esp_event_loop_create_default(void)
在例程中ESP32的使用是基于esp自带的一个esp-event事件组的。这个函数的作用就是创建一个默认的esp事件组并且不断的循环它。
函数esp_netif_create_default_wifi_ap()
esp_netif_t *esp_netif_create_default_wifi_ap(void)
这个函数的作用就是创建默认的WIIF-AP,就是说创建一个默认的AP模式的WIFI。
函数esp_event_handler_instance_register()
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)
这个函数就是注册一个事件到默认的esp-event循环中。这个event循环很有讲究,可以算的上是魔改版本的FreeRTOS中的事件了。在ESP32就很少调用原FreeRTOS中的事件标志组了。
- 参数
- event_base: 这个参数的意思就相当于是要向哪个事件标志组创建一个什么标志,这个参数就相当于是目标事件标志组
- event_id: 这个参数指代的就是要创建的事件的ID
- event_handler: 此ID对应的回调函数
- event_handler_arg: 要向回调函数中传入的参数在回调函数中会以
void* arg的形式来接收 - instance: 与已注册事件处理程序和数据相关的事件处理程序实例对象,可以为NULL。 (暂时还不太理解,没怎么学过C++)
宏定义WIFI_INIT_CONFIG_DEFAULT()
这个宏定义就是用来初始化默认WIFI参数的,配合着WIFI参数结构体来使用比如这样
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
结构体struct wifi_init_config_t
这里面有许多关于WIFI设置的参数详细参考
函数esp_wifi_init()
esp_err_t esp_wifi_init(const wifi_init_config_t *config)
这个就不用多说了,用来配合着之前配置好的WIFI结构体来初始化WIFI底层。
联合体union wifi_config_t
这里面有两个成员

在AP模式下就配置结构体 wifi_ap_config_t ,在station模式下就配置 wifi_sta_config_t 就好
结构体wifi_ap_config_t 与 wifi_sta_config_t
函数esp_wifi_set_mode()
esp_err_t esp_wifi_set_mode(wifi_mode_t mode)
这个函数就是设置WIFI的工作模式的。可以设置为AP模式,STA模式,也可以同时设置为STA+AP模式。
函数esp_wifi_set_config()
esp_err_t esp_wifi_set_config(wifi_interface_t interface, wifi_config_t *conf)
这个函数就是将之前写好的WIFI参数配置好
需要注意的是,这个函数只能在接口已经开启的状态下调用,否则将会失败(配合着之前的esp_wifi_set_mode())函数吗?
函数esp_wifi_start()
esp_err_t esp_wifi_start(void)
将之前配置好的WIFI启动(AP或者STA或者AT+STA)
WIFI AP模式下的使用流程
程序参考自乐鑫官方的例程
WIFI AP模式的初始化流程
void wifi_init_softap(void)
{
ESP_ERROR_CHECK(esp_netif_init());//开启TCP/IP堆栈
ESP_ERROR_CHECK(esp_event_loop_create_default());//开启ESP-EVENT默认事件循环
esp_netif_create_default_wifi_ap();//创建一个默认的AP WIIF
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();//默认的WIFI配置
ESP_ERROR_CHECK(esp_wifi_init(&cfg));//初始化WIIF底层
//向循环事件标志组中创建一个WIIF事件
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL,
NULL));
//配置WIIF连接(账号,密码等)
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
},
};
if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) {
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));//设置WIFI为AP模式
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));//配置AP模式的WIIF
ESP_ERROR_CHECK(esp_wifi_start());//开启WIIF
ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
}
WIFI事件的回调函数
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
//判断事件的ID并作出相应的操作
if (event_id == WIFI_EVENT_AP_STACONNECTED) {
wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
ESP_LOGI(TAG, "station "MACSTR" join, AID=%d",
MAC2STR(event->mac), event->aid);
} else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d",
MAC2STR(event->mac), event->aid);
}
}
Wi-Fi驱动程序会将事件发送到默认事件循环。应用程序可以在使用进行注册的回调中处理这些事件esp_event_handler_register()。esp_netif组件还处理Wi-Fi事件,以提供一组默认行为。
例如,当Wi-Fi站连接到AP时,esp_netif将自动启动DHCP客户端(默认情况下)。
也就是说WIFI的使用基本离不开esp的事件循环组
WIFI Station模式下的使用流程
WIIF Station模式的初始化流程
void wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate();//创建一个事件标志组
ESP_ERROR_CHECK(esp_netif_init());//初始化TCP堆栈
ESP_ERROR_CHECK(esp_event_loop_create_default());//创建一个默认的事件循环
esp_netif_create_default_wifi_sta();//创建一个默认的WIFI STA模式
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();//将WIIF底层参数设置为默认值
ESP_ERROR_CHECK(esp_wifi_init(&cfg));//初始化WIFI底层驱动
esp_event_handler_instance_t instance_any_id;//创建一个实例
esp_event_handler_instance_t instance_got_ip;//创建一个获取ip的实例
//创建一个WIFI_EVENT事件标志组,并且为这个组中任何事件ID都设置一个名为event_handler的回调函数,并且绑定一个任何ID的实例
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
//创建一个IP_EVENT的事件标志组,并未IP_EVENT_STA_GOT_IP这个事件ID注册一个名为event_handler的回调函数(在esp-event中可以将多个事件位绑定为一个回调函数)并且绑定一个获取IP的实例
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
//用来配置WIFI STA模式下的参数(需要连接的WIFI账号以及密码)
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS,
/* Setting a password implies station will connect to all security modes including WEP/WPA.
* However these modes are deprecated and not advisable to be used. Incase your Access point
* doesn't support WPA2, these mode can be enabled by commenting below line */
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
.pmf_cfg = {
.capable = true,
.required = false
},
},
};
//设置WIFI模式为STA模式
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
//将之前设置好的WIFI参数配置
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
//开启WIIF(如果是STA模式的话开启WIFI会产生一个名为WIFI_EVENT_STA_START的事件标志,在事件任务中,它将启动DHCP客户端,该客户端最终将触发DHCP进程。)
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init_sta finished.");
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
* happened. */
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
/* The event will not be processed after unregister */
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
vEventGroupDelete(s_wifi_event_group);
}
WIFI事件的回调函数
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
//当事件标志组是WIFI_EVENT并且事件ID是WIFI_EVENT_STA_START时,就执行esp_wifi_connect()这个函数
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) {
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);
}
}
一旦esp_wifi_connect()被调用,Wi-Fi驱动程序将开始内部扫描/连接过程。
如果内部扫描/连接过程成功,将生成WIFI_EVENT_STA_CONNECTED。在事件任务中,它将启动DHCP客户端,该客户端最终将触发DHCP进程。
由于例如密码错误,找不到AP等原因,Wi-Fi连接可能会失败。在这种情况下,会出现WIFI_EVENT_STA_DISCONNECTED并提供这种失败的原因。
初始化DHCP客户端后,将开始IP阶段。如果从DHCP服务器成功接收到IP地址,则将出现IP_EVENT_STA_GOT_IP,并且事件任务将执行常规处理。
在应用程序事件回调中,IP_EVENT_STA_GOT_IP被中继到应用程序任务。对于基于LwIP的应用程序,此事件非常特殊,这意味着该应用程序已准备就绪,可以开始其任务,例如创建TCP / UDP套接字等。一个非常常见的错误是在收到IP_EVENT_STA_GOT_IP之前初始化套接字。接收IP之前,请勿开始与套接字相关的工作。
后记
ESP32的WIFI驱动库是很全的并且配合着事件循环组以及ESP32的双核特性,可以在另一个核上跑WIFI 蓝牙 等等进程。这是很方便的。
然而我对WIFI的相关协议了解的并不多…再接再厉吧
后续再学习下WIFI相关协议以及esp-event事件组的相关操作,这个魔改板的FreeRTOS给人感觉很惊喜,有许多方便的功能,但是用起来还是有些不顺手。