代码
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sys.h"
/*
用idf.py menuconfig进入菜单配置
或者修改下述宏定义进而配置wifi ssid、密码
*/
#define EXAMPLE_ESP_WIFI_SSID "胡警文粉丝团"
#define EXAMPLE_ESP_WIFI_PASS "hjlhjw@123!"
#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_RETRY //最大重试数
/*连接成功时,FreeRTOS事件组来传递信号*/
static EventGroupHandle_t s_wifi_event_group;
/* The event group allows multiple bits for each event, but we only care about two events:
* - we are connected to the AP with an IP
* - we failed to connect after the maximum amount of retries */
/*
事件组设有多个比特位来标志每个事件,但我们仅关心其中两个
1.连接到了AP且分配到了个ip
2.重连次数超过最大重试数,判定为连接失败
*/
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static const char *TAG = "wifi station";
static int s_retry_num = 0; //记录重连次数
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) { //如果是wifi事件相关&&事件id==wifi STA模式开始,开始连接WiFi
esp_wifi_connect();
}
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { //如果是wifi事件相关&&事件id==wifi STA模式连接失败
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); //置位wifi事件组的失败标志位,表示连接失败
}
ESP_LOGI(TAG,"connect to the AP fail");
}
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { //如果是ip事件相关&&事件id==已取得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)); //串口打印ip
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
void wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate(); //初始化wifi事件组
ESP_ERROR_CHECK(esp_netif_init()); //检查
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); //配置wifi
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id; //存id
esp_event_handler_instance_t instance_got_ip; //存ip
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, //注册wifi事件
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, //注册ip事件
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
wifi_config_t wifi_config = { //wifi配置初始化
.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
},
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init_sta finished.");
//等待,直到到连接建立,或者连接失败,事件标志bits被event_handler()置位
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
/* xEventGroupWaitBits()在回调发生之前就会返回Bits,所以我们可以用来测试事件是否真的发生了*/
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");
}
//事件注销后不会再被处理
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);
}
void app_main(void)
{
//Initialize NVS
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_LOGI(TAG, "ESP_WIFI_MODE_STA");
wifi_init_sta();
}
解析
1.事件组定义
/*连接成功时,FreeRTOS事件组来传递信号*/
static EventGroupHandle_t s_wifi_event_group;
/* The event group allows multiple bits for each event, but we only care about two events:
* - we are connected to the AP with an IP
* - we failed to connect after the maximum amount of retries */
/*
事件组设有多个比特位来标志每个事件,但我们仅关心其中两个
1.连接到了AP且分配到了个ip
2.重连次数超过最大重试数,判定为连接失败
*/
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
查看定义到这就可以明白,上述一个事件由多位bit表示是什么意思了,每一个事件标志实际上是一个32/16位的unsigned int型变量。
我们再看看BIT0,BIT1
确实是32位
2.事件处理函数
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) { //如果是wifi事件相关&&事件id==wifi STA模式开始,开始连接WiFi
esp_wifi_connect();
}
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { //如果是wifi事件相关&&事件id==wifi STA模式连接失败
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); //置位wifi事件组的失败标志位,表示连接失败
}
ESP_LOGI(TAG,"connect to the AP fail");
}
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { //如果是ip事件相关&&事件id==已取得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)); //串口打印ip
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
这个函数的功能简单,根据事件id、类型的不同,执行不同操作。详细请看注释
3.初始化函数
(1) esp_netif_create_default_wifi_sta();
这是官方提供给用户,用于初始化默认STA模式的API
(2)wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); //配置wifi
(3)
esp_event_handler_instance_t instance_any_id; esp_event_handler_instance_t instance_got_ip;
所以这两变量的实际类型是void*
参考:C 语言中 void* 详解及应用
(4)
void wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate(); //初始化wifi事件组
ESP_ERROR_CHECK(esp_netif_init()); //检查
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); //配置wifi
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id; //存id
esp_event_handler_instance_t instance_got_ip; //存ip
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, //注册wifi事件
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, //注册ip事件
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
wifi_config_t wifi_config = { //wifi配置初始化
.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
},
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init_sta finished.");
//等待,直到到连接建立,或者连接失败,事件标志bits被event_handler()置位
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
/* xEventGroupWaitBits()在回调发生之前就会返回Bits,所以我们可以用来测试事件是否真的发生了*/
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");
}
//事件注销后不会再被处理
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);
}
注册事件
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, //注册wifi事件
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, //注册ip事件
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
注册一个事件处理实例到默认死循环中
@param[in] :不能为NULL,被注册的 Event_loop 的句柄
- @param[in] :handle函数要处理的事件的基础id
- @param[in] :handle函数要处理的事件的id
- @param[in] :事件触发时,handle函数入口
- @param[in] :事件触发时,传递给handle函数的参数
- @param[out] instance :可以为NULL,后面自行看吧
//可以看我的基于VScode的ESP32开发学习(四),对这里做了详细分析
后言
看wifi之前先去看事件注册相关例程,会轻松很多。