本章学习把ESP32-S3配置为 STA模式,即连接附近的热点。 STA模式相关知识请读者查看上一章节的内容。
本章分为如下几个小节:
49.1 硬件设计
49.2 软件设计
49.3 下载验证
49.1 硬件设计
- 例程功能
本章实验功能简介:扫描附近的 WIFI 信号,并连接到一个真实存在的 WIFI 热点。
- 硬件资源
1) LED 灯
LED-IO1
2) XL9555
IIC_INT-IO0(需在 P5 连接 IO0)
IIC_SDA-IO41
IIC_SCL-IO42
3) SPILCD
CS-IO21
SCK-IO12
SDA-IO11
DC-IO40(在 P5 端口,使用跳线帽将 IO_SET 和 LCD_DC 相连)
PWR- IO1_3(XL9555)
RST- IO1_2(XL9555)
4) ESP32-S3 内部 WiFi
- 原理图
本章实验使用的 WiFi 为 ESP32-S3 的片上资源,因此并没有相应的连接原理图。
49.2 软件设计
49.2.1 程序流程图
本实验的程序流程图:

图 49.2.1.1 程序流程图
49.2.2 程序解析
49.2.2,1 本次实验用到库函数说明
只分析下esp_event_handler_register()函数,其他函数可以参考上一章分析:
esp_event_handler_register是 ESP-IDF 事件循环库 (esp_event) 中的一个函数。它的核心作用是:将一个用户自定义的回调函数(事件处理器)注册到特定的事件上。当该事件被事件循环派发时,注册的回调函数就会被自动调用。
这是一种典型的事件驱动编程模型,允许程序异步地响应系统发生的各种事件(如 WiFi 连接成功、获取到 IP 地址、Socket 有数据可读等),而不需要主动去轮询状态。
函数原型:
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_base_t |
事件基 。标识事件所属的类别或模块。这是一个字符串常量,例如: • WIFI_EVENT (WiFi 相关事件) • IP_EVENT (IP 网络层相关事件) • ESP_EVENT_BASE_ANY (通配符,代表任何事件基) |
event_id |
int32_t |
事件 ID 。在特定事件基下标识一个具体的事件。例如: • 在 WIFI_EVENT 基下: - WIFI_EVENT_STA_START (Station 模式启动) - WIFI_EVENT_STA_CONNECTED (连接到 AP) - WIFI_EVENT_STA_DISCONNECTED (从 AP 断开) • 在 IP_EVENT 基下: - IP_EVENT_STA_GOT_IP (Station 获取到 IP 地址) • ESP_EVENT_ANY_ID (通配符,代表指定事件基下的任何事件) |
event_handler |
esp_event_handler_t |
事件处理函数指针 。这是你定义的函数,当事件发生时将被调用。其函数签名必须为: void (*event_handler)(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) |
event_handler_arg |
void* |
传递给事件处理函数的参数 。这是一个用户自定义的指针,在调用 event_handler 时,会作为第一个参数原样传递给它。通常用于传递上下文信息(如结构体、对象指针等)。如果不需要,可以设为 NULL 。 |
表 49.2.2.1 esp_event_handler_register()函数参数说明
返回值:
返回值 | 说明 |
---|---|
ESP_OK |
事件处理器注册成功。 |
ESP_ERR_NO_MEM |
内存不足,无法注册处理器。 |
ESP_ERR_INVALID_ARG |
参数无效(例如,event_handler 为 NULL )。 |
其他错误码 | 其他内部错误。 |
表 49.2.2.2 esp_event_handler_register()函数返回值说明
49.2.2.2 main函数解析
在本章节实验中,我们只关心 main.c 文件内容即可,该文件内容如下:
/* 链接wifi名称 */
#define DEFAULT_SSID "RedmiK50"
/* wifi密码 */
#define DEFAULT_PWD "abc1234567"
/* 事件标志 */
static EventGroupHandle_t wifi_event;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static const char *TAG = "static_ip";
char lcd_buff[100] = {0};
/* WIFI默认配置 */
#define WIFICONFIG() { \
.sta = { \
.ssid = DEFAULT_SSID, \
.password = DEFAULT_PWD, \
.threshold.authmode = WIFI_AUTH_WPA2_PSK, \
}, \
}
/**
* @brief 链接显示
* @param flag:2->链接;1->链接失败;0->再链接中
* @retval 无
*/
void connet_display(uint8_t flag)
{
if(flag == 2)
{
spilcd_fill(0,90,320,240,WHITE);
sprintf(lcd_buff, "ssid:%s",DEFAULT_SSID);
spilcd_show_string(0, 90, 240, 16, 16, lcd_buff, BLUE);
sprintf(lcd_buff, "psw:%s",DEFAULT_PWD);
spilcd_show_string(0, 110, 240, 16, 16, lcd_buff, BLUE);
}
else if (flag == 1)
{
spilcd_show_string(0, 90, 240, 16, 16, "wifi connecting fail", BLUE);
}
else
{
spilcd_show_string(0, 90, 240, 16, 16, "wifi connecting......", BLUE);
}
}
/**
* @brief WIFI链接糊掉函数
* @param arg:传入网卡控制块
* @param event_base:WIFI事件
* @param event_id:事件ID
* @param event_data:事件数据
* @retval 无
*/
static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
static int s_retry_num = 0;
/* 扫描到要连接的WIFI事件 */
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
{
connet_display(0);
esp_wifi_connect();
}
/* 连接WIFI事件 */
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED)
{
connet_display(2);
}
/* 连接WIFI失败事件 */
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
{
/* 尝试连接 */
if (s_retry_num < 20)
{
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
}
else
{
xEventGroupSetBits(wifi_event, WIFI_FAIL_BIT);
}
ESP_LOGI(TAG,"connect to the AP fail");
}
/* 工作站从连接的AP获得IP */
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, "static ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(wifi_event, WIFI_CONNECTED_BIT);
}
}
/**
* @brief WIFI初始化
* @param 无
* @retval 无
*/
void wifi_sta_init(void)
{
static esp_netif_t *sta_netif = NULL;
wifi_event= xEventGroupCreate(); /* 创建一个事件标志组 */
/* 网卡初始化 */
ESP_ERROR_CHECK(esp_netif_init());
/* 创建新的事件循环 */
ESP_ERROR_CHECK(esp_event_loop_create_default());
sta_netif= esp_netif_create_default_wifi_sta();
assert(sta_netif);
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL) );
ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL) );
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
wifi_config_t wifi_config = WIFICONFIG();
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start());
/* 等待链接成功后、ip生成 */
EventBits_t bits = xEventGroupWaitBits(wifi_event,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
/* 判断连接事件 */
if (bits & WIFI_CONNECTED_BIT)
{
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
DEFAULT_SSID, DEFAULT_PWD);
}
else if (bits & WIFI_FAIL_BIT)
{
connet_display(1);
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
DEFAULT_SSID, DEFAULT_PWD);
}
else
{
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
}
/**
* @brief 程序入口
* @param 无
* @retval 无
*/
void app_main(void)
{
esp_err_t ret;
uint8_t x = 0;
ret = nvs_flash_init(); /* 初始化NVS */
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ESP_ERROR_CHECK(nvs_flash_init());
}
led_init(); /* LED初始化 */
my_spi_init(); /* SPI初始化 */
myiic_init(); /* IIC初始化 */
xl9555_init(); /* 初始化按键 */
spilcd_init(); /* LCD屏初始化 */
spilcd_show_string(0, 0, 240, 32, 32, "ESP32-S3", RED);
spilcd_show_string(0, 40, 240, 24, 24, "WiFi STA Test", RED);
spilcd_show_string(0, 70, 240, 16, 16, "ATOM@ALIENTEK", RED);
wifi_sta_init();
while (1)
{
LED0_TOGGLE();
vTaskDelay(pdMS_TO_TICKS(500));
}
}
从上述源码中,首先创建了事件组、 WiFi 事件回调函数,并配置 WiFi 为 STA 模式。当系统搜索到可连接的热点时,它会尝试与该热点建立连接。如果连接成功,则会在 LCD 上显示连接信息,并发送一个连接事件标志。如果连接失败,系统会尝试发送 20 次连接请求,直到没有收到任何连接回复为止。此时,会发送一个连接失败事件标志。通过查看这些连接事件标志,可以确定热点是否成功连接。
分析下代码中注册了两个事件WIFI_EVENT和IP_EVENT原因:
首先了解连接 WiFi 的完整流程:
ESP32 作为 Station (STA) 模式连接到路由器并准备好进行网络通信,需要完成两个主要的阶段:
链路层连接 (Layer 2 Connection): 也就是 WIFI_EVENT_STA_CONNECTED事件所代表的。这表示 ESP32 和路由器的 WiFi 天线之间已经建立了无线电连接,完成了身份认证(密码验证)。但这仅仅意味着物理层和数据链路层通了,还不能进行 IP 网络通信。
网络层连接 (Layer 3 Connection): 也就是 IP_EVENT_STA_GOT_IP事件所代表的。这表示 ESP32 已经通过 DHCP 协议(或静态配置)从路由器成功获取到了一个 IP 地址、子网掩码、网关和 DNS 服务器。只有到了这一步,你的 ESP32 才真正加入到本地网络中,能够通过 IP 地址与其他设备(如手机、电脑、服务器)进行 TCP/UDP 通信。
为什么必须监听 IP_EVENT_STA_GOT_IP?
我们可以通过一个比喻来理解:
WIFI_EVENT_STA_CONNECTED:就像你的手机显示"已连接到 RedmiK50"。你的手机和路由器之间已经握手成功,但还没有给你分配一个"门牌号"(IP 地址)。此时你还不能上网,不能刷视频。
IP_EVENT_STA_GOT_IP:就像路由器对你的手机说:"好的,你的门牌号是192.168.1.105"。现在你的手机有了网络身份,才能真正开始网络访问。
**程序逻辑上认为的"连接成功"是以获得 IP 地址为标志,**实现代码:
EventBits_t bits = xEventGroupWaitBits(wifi_event,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, // 等待【连接成功】或【连接失败】的位
pdFALSE,
pdFALSE,
portMAX_DELAY);
if (bits & WIFI_CONNECTED_BIT) { // 判断是否【连接成功】
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", DEFAULT_SSID, DEFAULT_PWD);
}
WIFI_CONNECTED_BIT
是在 IP_EVENT_STA_GOT_IP
的事件处理中:
else if(event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
{
...
xEventGroupSetBits(wifi_event, WIFI_CONNECTED_BIT); // 【在这里】设置连接成功标志
}
49.3 下载验证
程序下载成功后,需要利用手机或其他设备创建一个 WiFi 热点。在创建热点时,需要注意提供正确的账号(代码中DEFAULT_SSID宏,修改为你创建的Wifi热点账号)名和密码(代码中DEFAULT_PWD宏,修改为你创建的Wifi热点密码),以确保程序能够成功连接。同时,确保程序中要连接的热点账号与密码与所创建的热点一致。当 LCD 显示热点的账号名和密码时,此时 ESP32-S3 设备已经与热点连接成功了,否则, LCD 提示连接失败,如下图所示:

图 49.3.1 Wifi热点连接成功显示