ESP32的IDF开发学习-WiFi的开启、配置与连接
前言
本章节将实现如何使用ESP32的WiFi功能,尽可能的详细地介绍
简介
ESP32中的wifi支持双工作模式
- Station(STA)模式:连接到路由器或其他AP设备,可通过
esp_wifi_set_mode(WIFI_MODE_STA)
设置。 - SoftAP模式:创建本地热点,允许其他设备直连,最大支持 10台设备 同时接入(默认配置)。
- 混合模式:
STA+AP
模式并行运行(WIFI_MODE_APSTA
),例如设备既能连接云端服务器,又能作为配置热点供手机调试。
同时,ESP32也做了低功耗优化和射频性能提升 - 低功耗优化
ESP32-S3引入了 Light-sleep/Modem-sleep 精细功耗管理:- Modem-sleep:WiFi空闲时保持TCP/IP连接,仅射频模块休眠,电流低至 1 mA 左右,适合持续在线但数据发送不频繁的场景(如传感器定时上报)。
- 射频性能提升
- 外置天线支持:通过IPEX接口连接高增益天线,解决复杂环境(如金属外壳设备)信号衰减问题。
- 抗干扰增强:优化了2.4GHz频段的共存算法,降低蓝牙/WiFi同时运行时的通信冲突风险。
其搭载的搭载 512 KB SRAM 和 320 KB ROM,双核Xtensa LX7处理器(主频240 MHz),在处理高并发TCP连接(如同时维护多个WebSocket)时减少丢包率。
初始化步骤
初始化流程本质上是为TCP/IP协议栈、事件管理、驱动层、应用层之间的协作搭建桥梁。以下是官方推荐的核心步骤及其作用解析:
- s1.1:启动LwIP核心任务
esp_netif_init(); // 初始化LwIP协议栈,建立网络数据传输的「高速公路」
- 关键作用:初始化TCP/IP协议栈(LwIP),为后续WiFi通信提供IP层支持。
- 注意点:LwIP任务的优先级需高于应用任务,避免网络数据堆积。
- s1.2:创建系统事件中枢
esp_event_loop_create(); // 初始化事件循环——WiFi连接的「神经系统」
- 事件分工示例:
- WiFi层事件(如连接成功):
WIFI_EVENT
- IP层事件(如获取IP地址):
IP_EVENT
- WiFi层事件(如连接成功):
- 回调传递范式:建议在事件回调中将核心状态(如
IP_EVENT_STA_GOT_IP
)通过队列发送给应用任务,实现分层解耦。
- 事件分工示例:
- s1.3:绑定网络接口实例
- STA模式绑定:
esp_netif_create_default_wifi_sta(); // 为Station模式挂载「虚拟网卡」
- AP模式绑定:
esp_netif_create_default_wifi_ap(); // 为AP模式分配IP地址池(默认192.168.4.1)
- 背后的网络架构:此步骤会在LwIP中创建
netif
结构体,管理IP路由、DNS等核心参数。
- STA模式绑定:
- s1.4:初始化WiFi驱动引擎
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); esp_wifi_init(&cfg); // 启动WiFi驱动的「引擎舱」
- 隐藏的并发控制:该函数内部通过RTOS任务管理射频信号调度,开发者无需直接操作802.11帧。
- s1.5:启动应用任务
xTaskCreate(app_main_task, "App_Task", 4096, NULL, 5, NULL);
- 优先级设定技巧:应用任务优先级应低于事件任务(如默认优先级为5时,事件任务可设为6),避免事件处理被阻塞。
- 优先级设定技巧:应用任务优先级应低于事件任务(如默认优先级为5时,事件任务可设为6),避免事件处理被阻塞。
代码编写
头文件
#define WIFI_MODE 0 // 0:STA,链接热点, 1:AP,允许别人链接
#define SSID "LEE"
#define PASSWORD "wcyz1927"
#define AP_SSID "XBOT"
#define AP_PASSWORD "XBOT20241240"
#define MAX_AP_SCAN_NUM 10 //扫描WiFi数
#define WIFI_AUTO_RECONNECT_MAX_RETRIES 5
#define WIFI_AUTO_RECONNECT_BASE_DELAY_MS 10000
void configure_wifi(void);
void wifi_connect(void);
void wifi_scan(void);
配置wifi
我们需要按照步骤来配置WiFi的相关设置
/**
* 配置WiFi功能
*
* 本函数负责初始化WiFi模块,根据编译选项配置为AP或STA模式,并注册相应的事件处理器
* 它还负责启动WiFi模块,使设备能够连接到WiFi网络
*/
void configure_wifi(void)
{
// 初始化底层驱动和TCP/IP协议栈
ESP_ERROR_CHECK(esp_netif_init());
// 初始化事件循环
esp_event_loop_create_default();
// 根据编译选项选择WiFi模式
#if WIFI_MODE
esp_netif_create_default_wifi_ap();
#else
esp_netif_create_default_wifi_sta();
#endif
// 初始化WiFi配置
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
ESP_LOGI(TAG, "wifi_init_success");
// 新增事件循环注册
esp_event_handler_instance_t wifi_event_handle, ip_event_handle;
// 注册WiFi事件处理器
esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID,&wifi_event_handler_STA, NULL, &wifi_event_handle);
esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID,&wifi_event_handler_AP, NULL, NULL);
// 注册IP事件处理器(STA获取IP时触发)
esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP,&wifi_event_handler_STA, NULL, &ip_event_handle);
ESP_LOGI(TAG, "wifi_init_success");
// 根据编译选项配置WiFi为AP或STA模式
#if WIFI_MODE
wifi_config_t ap_config = {
.ap = {
.ssid = AP_SSID,
.password = AP_PASSWORD,
.max_connection = 5,
.authmode = WIFI_AUTH_WPA2_PSK
},
};
esp_wifi_set_mode(WIFI_MODE_AP);
esp_wifi_set_config(WIFI_IF_AP, &ap_config);
ESP_LOGI(TAG, "wifi_AP_config");
#else
wifi_config_t wifi_config = {
.sta = {
.ssid = SSID,
.password = PASSWORD
},
};
esp_wifi_set_mode(WIFI_MODE_STA);
esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
ESP_LOGI(TAG, "wifi_STA_config");
#endif
// 启动WiFi模块
esp_wifi_start();
}
这里有两个注册函数,主要用于记录当前wifi的状态
/**
* WIFI事件处理函数(AP模式)
* 处理AP模式下的WIFI事件
* @param arg 事件处理函数的上下文
* @param event_base 事件基类
* @param event_id 事件ID
* @param event_data 事件数据
*/
static void wifi_event_handler_AP(void *arg, esp_event_base_t event_base,int32_t event_id, void *event_data) {
// 处理AP模式下的客户端连接事件
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STACONNECTED) {
// 获取客户端连接事件数据
wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t*) event_data;
// 记录设备连接日志
ESP_LOGI(TAG, "设备已连接! MAC: %02X:%02X:%02X:%02X:%02X:%02X",
event->mac[0], event->mac[1], event->mac[2],
event->mac[3], event->mac[4], event->mac[5]);
}
// 处理AP模式下的客户端断开事件
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STADISCONNECTED) {
// 获取客户端断开事件数据
wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t*) event_data;
// 记录设备断开日志
ESP_LOGI(TAG, "设备已断开. MAC: %02X:%02X:%02X:%02X:%02X:%02X",
event->mac[0], event->mac[1], event->mac[2],
event->mac[3], event->mac[4], event->mac[5]);
}
}
/**
* WIFI事件处理函数(STA模式)
* 处理STA模式下的WIFI事件
* @param arg 事件处理函数的上下文
* @param event_base 事件基类
* @param event_id 事件ID
* @param event_data 事件数据
*/
static void wifi_event_handler_STA(void *arg, esp_event_base_t event_base,int32_t event_id, void *event_data) {
// 处理WIFI事件
if (event_base == WIFI_EVENT) {
switch (event_id) {
case WIFI_EVENT_STA_START:
// 记录WIFI驱动就绪日志
ESP_LOGI(TAG, "WiFi驱动就绪");
break;
case WIFI_EVENT_STA_CONNECTED:
// 记录成功连接到热点日志
ESP_LOGI(TAG, "已连接到热点");
break;
case WIFI_EVENT_STA_DISCONNECTED:
// 记录热点断开日志并启用自动重连
ESP_LOGW(TAG, "热点断开, 尝试重连...");
WIFI_AUTO_RECONNECT_ENABLED = 1;
break;
}
// 处理IP事件
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
// 获取IP事件数据
ip_event_got_ip_t *event = (ip_event_got_ip_t*) event_data;
// 记录成功获取IP日志
ESP_LOGI(TAG, "成功获取IP:" IPSTR, IP2STR(&event->ip_info.ip));
}
}
wifi扫描
注意,扫描和连接不能在一起使用
/**
* 打印认证模式
* @param authmode WIFI认证模式
*/
static void print_auth_mode(wifi_auth_mode_t authmode) {
// 根据认证模式打印相应信息
switch (authmode) {
case WIFI_AUTH_OPEN: ESP_LOGI("SCAN", "Auth Mode \tOpen"); break;
case WIFI_AUTH_WEP: ESP_LOGI("SCAN", "Auth Mode \tWEP"); break;
case WIFI_AUTH_WPA_PSK: ESP_LOGI("SCAN", "Auth Mode \tWPA-PSK"); break;
case WIFI_AUTH_WPA2_PSK: ESP_LOGI("SCAN", "Auth Mode \tWPA2-PSK"); break;
case WIFI_AUTH_WPA3_PSK: ESP_LOGI("SCAN", "Auth Mode \tWPA3-PSK"); break;
default: ESP_LOGI("SCAN", "Auth Mode \tUnknown"); break;
}
}
/**
* 打印加密类型
* @param pairwise Pairwise加密类型
* @param group Group加密类型
*/
static void print_cipher_type(wifi_cipher_type_t pairwise, wifi_cipher_type_t group) {
// 打印Pairwise加密类型
ESP_LOGI("SCAN", "Pairwise Cipher \t%s",
(pairwise == WIFI_CIPHER_TYPE_NONE) ? "None" :
(pairwise == WIFI_CIPHER_TYPE_WEP40) ? "WEP40" :
(pairwise == WIFI_CIPHER_TYPE_WEP104) ? "WEP104" :
(pairwise == WIFI_CIPHER_TYPE_TKIP) ? "TKIP" :
(pairwise == WIFI_CIPHER_TYPE_CCMP) ? "CCMP" : "Unknown");
// 打印Group加密类型
ESP_LOGI("SCAN", "Group Cipher \t\t%s",
(group == WIFI_CIPHER_TYPE_NONE) ? "None" :
(group == WIFI_CIPHER_TYPE_WEP40) ? "WEP40" :
(group == WIFI_CIPHER_TYPE_WEP104) ? "WEP104" :
(group == WIFI_CIPHER_TYPE_TKIP) ? "TKIP" :
(group == WIFI_CIPHER_TYPE_CCMP) ? "CCMP" : "Unknown");
}
/**
* 扫描可用的WiFi接入点(AP)
* 此函数启动WiFi扫描,获取并打印周围可用的WiFi热点信息
* 它首先检查WIFI_MODE宏是否未定义,如果未定义则开始扫描
* 扫描完成后,根据发现的AP数量动态分配内存,并打印每个AP的详细信息
*/
void wifi_scan(void)
{
#if !WIFI_MODE
ESP_ERROR_CHECK(esp_wifi_scan_start(NULL, true));
// 获取AP数量
uint16_t ap_count = 0;
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
// 实际扫描到的AP数量可能超过预分配内存
uint16_t max_ap = (ap_count > MAX_AP_SCAN_NUM) ? MAX_AP_SCAN_NUM : ap_count;
wifi_ap_record_t *ap_records = malloc(sizeof(wifi_ap_record_t) * max_ap);
if (!ap_records) {
ESP_LOGE("SCAN", "内存分配失败");
return;
}
// 获取AP列表
uint16_t ap_num = max_ap;
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_num, ap_records));
// 打印结果
ESP_LOGI("SCAN", "发现 %d 个WiFi热点:", ap_num);
for (int i = 0; i < ap_num; i++) {
ESP_LOGI("SCAN", "--- AP %d ---", i + 1);
ESP_LOGI("SCAN", "SSID: \t\t%s", ap_records[i].ssid);
ESP_LOGI("SCAN", "RSSI: \t\t%d dBm", ap_records[i].rssi);
print_auth_mode(ap_records[i].authmode);
if (ap_records[i].authmode != WIFI_AUTH_WEP) {
print_cipher_type(ap_records[i].pairwise_cipher, ap_records[i].group_cipher);
}
ESP_LOGI("SCAN", "Channel: \t%d", ap_records[i].primary);
}
free(ap_records); // 释放内存
#else
ESP_LOGI("SCAN", "WiFi AP scan not supported");
return;
#endif
}
wifi链接与重连机制
/**
* 连接WiFi网络
* 此函数根据WIFI_MODE宏的定义来决定是否执行连接操作
* 在实际连接之前,会检查WIFI_MODE宏是否未定义
* 如果未定义,则打印连接信息并尝试连接WiFi
*/
void wifi_connect(void)
{
#if !WIFI_MODE
ESP_LOGI(TAG, "wifi_connect");
esp_wifi_connect();
wifi_auto_reconnect();
#endif
}
/**
* 启用WIFI自动重连功能
*/
void wifi_auto_reconnect(void) {
// 在非WIFI模式下注册WIFI事件处理器
#if !WIFI_MODE
esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL);
esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL);
// 记录自动重连启用日志
ESP_LOGI("WIFI", "自动重连已启用, 最大重试次数:%d", WIFI_AUTO_RECONNECT_MAX_RETRIES);
#endif
}
// 定义当前重连尝试次数
static uint8_t WIFI_AUTO_RECONNECT_RETRIES_NOW=0;
// 定义是否启用自动重连
static uint8_t WIFI_AUTO_RECONNECT_ENABLED = 0;
/**
* WIFI事件处理函数
* 处理WIFI断连和重连逻辑
* @param arg 事件处理函数的上下文
* @param event_base 事件基类
* @param event_id 事件ID
* @param event_data 事件数据
*/
static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
// 处理STA模式下的断连事件
if (event_id == WIFI_EVENT_STA_DISCONNECTED) {
// 检查是否启用自动重连且重连次数未超过最大值
if (WIFI_AUTO_RECONNECT_ENABLED && WIFI_AUTO_RECONNECT_RETRIES_NOW < WIFI_AUTO_RECONNECT_MAX_RETRIES) {
// 增加重连尝试次数
WIFI_AUTO_RECONNECT_RETRIES_NOW++;
// 记录重连尝试日志
ESP_LOGI("WIFI", "尝试重连第%d次", WIFI_AUTO_RECONNECT_RETRIES_NOW);
// 立即触发重连
esp_wifi_connect();
} else {
// 记录重连失败日志
ESP_LOGI("WIFI", "重连失败,已达最大次数:%d", WIFI_AUTO_RECONNECT_MAX_RETRIES);
}
// 处理STA模式下成功获取IP事件
} else if (event_id == IP_EVENT_STA_GOT_IP) {
// 清零重连尝试次数
WIFI_AUTO_RECONNECT_RETRIES_NOW = 0;
// 禁用自动重连
WIFI_AUTO_RECONNECT_ENABLED = 0;
}
}
代码地址
代码开源地址