第五十章 ESP32S3 WiFi 热点实验
本章学习把 ESP32-S3 配置为 AP 模式,即创建连接热点,可使用手机连接该热点。
本章分为如下几个小节:
50.1 硬件设计
50.2 软件设计
50.3 下载验证
50.1 硬件设计
1. 例程功能
本章实验功能简介:当手机连接这个热点时, LCD 显示该连接设备的 MAC 地址,断开时,LCD 显示断开设备的 MAC 地址。
2. 硬件资源
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
3. 原理图
本章实验使用的 WiFi 为 ESP32-S3 的片上资源,因此并没有相应的连接原理图。
50.2 软件设计
50.2.1 程序流程图
本实验的程序流程图:
图 50.2.1.1 程序流程图
50.2.2 程序解析
50.2.2.1 本次实验用到库函数说明
(1)创建并配置一个适用于 WiFi AP 模式的网络接口
用于创建并配置一个默认的 WiFi 接入点(AP)模式网络接口。这个函数简化了 WiFi AP 模式的初始化过程,自动设置了许多默认参数。函数无参数,原型如下:
esp_netif_t *esp_netif_create_default_wifi_ap(void);
返回值:
返回值 | 类型 | 说明 |
---|---|---|
成功 |
| 指向新创建的网络接口对象的指针 |
失败 |
| 如果创建失败则返回 NULL |
表 50.2.2.1 函数esp_netif_create_default_wifi_ap()返回值说明
函数返回值esp_netif_t*说明:
esp_netif_t
是连接底层网络驱动(如 WiFi、以太网)和上层 TCP/IP 协议栈(LwIP)的桥梁。它的地位可以用下图表示:
它是一个不透明指针(Opaque Pointer),代表一个抽象的网络接口实例。
关键特性:
- 不透明性:不需要知道其内部结构的具体细节,只需通过 ESP-IDF 提供的 API 函数来操作它;
- 抽象代表:每个 esp_netif_t实例代表一个具体的网络接口(如 WiFi STA、WiFi AP、以太网等);
- 操作句柄:它是几乎所有 esp_netif_xxx()API 函数的第一个参数,用于指定要操作哪个网络接口。
总结:简单来说,可以将 esp_netif_t*
理解为网络接口的“身份证”。ESP-IDF 提供了各种函数(API),需要出示这张“身份证”(传入 esp_netif_t*
参数)来告诉系统想要操作哪个具体的接口。不需要关心里面包含了什么信息,只需知道如何使用它即可。
函数主要作用:
- 创建并配置一个适用于 WiFi AP 模式的网络接口;
- 设置默认的 TCP/IP 栈配置;
- 自动配置 DHCP 服务器;
- 设置默认的 IP 地址(通常为 192.168.4.1)。
创建的默认配置:
配置项 | 默认值 | 说明 |
---|---|---|
IP 地址 | 192.168.4.1 | AP 的默认 IP 地址 |
子网掩码 | 255.255.255.0 | C 类网络的默认掩码 |
网关 | 192.168.4.1 | 与 IP 地址相同 |
DHCP 服务器 | 启用 | 自动为连接的客户端分配 IP |
表 50.2.2.2 函数esp_netif_create_default_wifi_ap()创建的默认配置说明
使用流程:初始化 NVS -> 创建默认事件循环 -> 初始化网络接口 -> 调用 esp_netif_create_default_wifi_ap() -> 配置 WiFi AP 参数 -> 启动 WiFi AP模式
注意事项:
- 调用时机:应在 WiFi 初始化之前调用此函数;
- 内存管理:创建的接口由 ESP-IDF 内部管理,通常不需要手动释放;
- 默认配置:如果不需特殊配置,使用默认值即可满足大多数应用场景;
- 多接口:如果需要多个 AP 接口,应使用 esp_netif_create()创建自定义接口;
- 错误处理:始终检查返回值,确保接口创建成功。
(2)Wifi事件注册
esp_event_handler_register()函数在上一章做了详细说明,需要说一点本次函数使用如下:
ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,&wifi_event_handler, NULL) );
函数第二个参数传入的参数为ESP_EVENT_ANY_ID,ESP_EVENT_ANY_ID是一个通配符(Wildcard)。当它作为 event_id参数传递给 esp_event_handler_register()时,意思是:“请为指定事件基(event_base)下的 所有具体事件,注册这个事件处理函数”。参数说明:
- event_base = WIFI_EVENT:监听来自 WiFi 模块 的事件;
- event_id = ESP_EVENT_ANY_ID:不挑剔具体是哪种 WiFi 事件。无论是连接开始、连接成功、连接断开、扫描完成等等,只要是 WiFi 模块产生的事件,我都要监听;
- event_handler = &wifi_event_handler:当上述 任何WiFi 事件发生时,都去调用wifi_event_handler这个函数。
简单来说,ESP_EVENT_ANY_ID
就是“我全都要”的意思。它告诉事件系统:“只要是 WIFI_EVENT
这家公司(事件基)派来的所有快递(事件),都直接交给 wifi_event_handler
这个人(处理函数)签收处理”。
(3)获取对应网络接口句柄
函数用于通过接口的唯一标识键(if_key)获取对应的网络接口句柄(esp_netif_t)。 函数原型如下:
esp_netif_t *esp_netif_get_handle_from_ifkey(const char *if_key);
参数说明:
参数名 | 类型 | 方向 | 说明 |
---|---|---|---|
|
| 输入 | 网络接口的唯一标识键字符串 |
表 50.2.2.3 函数esp_netif_get_handle_from_ifkey()参数说明
返回值:
返回值 | 类型 | 说明 |
---|---|---|
成功 |
| 指向找到的网络接口对象的指针 |
失败 |
| 未找到与提供的 |
表 50.2.2.4 函数esp_netif_get_handle_from_ifkey()返回值说明
主要作用:
- 接口查找:在已创建的网络接口中查找具有指定键名的接口;
- 句柄获取:返回找到的接口的操作句柄;
- 接口访问:提供了一种在不同代码模块中访问同一网络接口的方式。
常用接口键名,系统为默认创建的网络接口预定义了以下键名(本次实验传入WIFI_AP_DEF"):
接口键名 (if_key) | 对应的接口类型 | 创建函数 |
---|---|---|
| 默认 WiFi Station 接口 |
|
| 默认 WiFi AP 接口 |
|
| 默认以太网接口 |
|
表 50.2.2.5 常用接口键名说明
(4)获取指定网络接口的 IP 地址、子网掩码和网关信息
用于获取指定网络接口的 IP 地址、子网掩码和网关信息。这个函数对于查询网络接口的当前 IP 配置状态非常有用。
函数原型:
esp_err_t esp_netif_get_ip_info(esp_netif_t *esp_netif, esp_netif_ip_info_t *ip_info);
参数说明:
参数名 | 类型 | 方向 | 说明 |
---|---|---|---|
|
| 输入 | 要查询的网络接口句柄。 本次实验为esp_netif_get_handle_from_ifkey()函数的返回值 |
|
| 输出 | 存储获取的 IP 信息的结构体指针 |
表 50.2.2.6 函数esp_netif_get_ip_info()参数说明
返回值:
返回值 | 类型 | 说明 |
---|---|---|
|
| 获取成功 |
其他错误码 |
| 获取失败 |
表 50.2.2.7 函数esp_netif_get_ip_info()返回值说明
函数作用:
- 提供 IP 配置查询:获取网络接口的 IP 地址、子网掩码和网关信息;
- 支持网络状态监测:通过检查 IP 地址判断网络连接状态;
- 简化信息显示:方便在用户界面或日志中显示网络配置;
- 与其他函数配合:与 IP 设置、接口创建等函数协同工作。
(5)字节序转换函数
inet_ntoa_r
是一个用于将 IPv4 地址从二进制格式(网络字节序)转换为点分十进制字符串格式的函数。它是 inet_ntoa
的可重入(线程安全)版本。函数原型如下:
char *inet_ntoa_r(struct in_addr addr, char *buf, int buflen);
参数说明:
参数名 | 类型 | 方向 | 说明 |
---|---|---|---|
|
| 输入 | 包含要转换的二进制IPv4地址的结构体 |
|
| 输出 | 用于存储结果字符串的缓冲区 |
|
| 输入 | 缓冲区的大小(字节数) |
表 50.2.2.7 函数inet_ntoa_r()参数说明
返回值:
返回值 | 类型 | 说明 |
---|---|---|
成功 |
| 指向结果字符串的指针(与 |
失败 |
| 如果缓冲区太小无法容纳结果字符串 |
表 50.2.2.7 函数inet_ntoa_r()返回值说明
主要作用:
- 二进制到字符串转换:将32位网络字节序的IPv4地址转换为点分十进制格式;
- 线程安全:与 inet_ntoa不同,inet_ntoa_r是可重入的,可以在多线程环境中安全使用;
- 内存安全:使用用户提供的缓冲区,避免静态缓冲区带来的安全问题。
(6)其他函数说明参见之前章节说明
50.2.2.2 main函数解析
static const char *TAG = "AP";
#define EXAMPLE_ESP_WIFI_SSID "MyWifi"
#define EXAMPLE_ESP_WIFI_PASS "abc1234567"
#define EXAMPLE_MAX_STA_CONN 5
#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
static char lcd_buff[100] = {0};/*** @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)
{/* 设备连接 */if (event_id == WIFI_EVENT_AP_STACONNECTED){spilcd_fill(0,90,320,240,WHITE);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);sprintf(lcd_buff, "MACSTR:"MACSTR,MAC2STR(event->mac));spilcd_show_string(0, 90, 320, 16, 16, lcd_buff, BLUE);spilcd_show_string(0, 110, 320, 16, 16, "With device connection", BLUE);} /* 设备断开 */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);spilcd_fill(0,90,320,320,WHITE);sprintf(lcd_buff, "Device disconnected:"MACSTR,MAC2STR(event->mac));spilcd_show_string(0, 90, 320, 16, 16, lcd_buff, BLUE);}
}/*** @brief WIFI初始化* @param 无* @retval 无*/
static void wifi_init_softap(void)
{/* 初始化网卡 */ESP_ERROR_CHECK(esp_netif_init());/* 创建新的事件循环 */ESP_ERROR_CHECK(esp_event_loop_create_default());/* 使用默认配置初始化包括netif的Wi-Fi */esp_netif_create_default_wifi_ap();wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();ESP_ERROR_CHECK(esp_wifi_init(&cfg));ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));/* 配置WIFI */wifi_config_t wifi_config = {.ap = {.ssid = EXAMPLE_ESP_WIFI_SSID,.ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),.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));ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));ESP_ERROR_CHECK(esp_wifi_start());esp_netif_ip_info_t ip_info;esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"), &ip_info);char ip_addr[16];inet_ntoa_r(ip_info.ip.addr, ip_addr, 16);ESP_LOGI(TAG, "Set up softAP with IP: %s", ip_addr);ESP_LOGI(TAG, "wifi_init_softap finished. SSID:'%s' password:'%s'",EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);spilcd_show_string(0, 90, 240, 16, 16, "wifi connecting......", BLUE);
}/*** @brief 程序入口* @param 无* @retval 无*/
void app_main(void)
{esp_err_t ret;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 AP Test", RED);spilcd_show_string(0, 70, 240, 16, 16, "ATOM@ALIENTEK", RED);wifi_init_softap();while (1){LED0_TOGGLE();vTaskDelay(pdMS_TO_TICKS(500));}
}
上述源码相对简单,主要将 ESP32-S3 设备配置为 AP 模式,即作为热点设备。然后,设置热点设备的账号、密码、安全模式等参数。在 WiFi 事件回调函数中,当有外部设备请求连接时,程序会在 LCD 上显示连接设备的 MAC 地址等信息。而当外部设备从连接状态断开时, LCD 会显示当前断开的外部设备 MAC 地址。
50.3 下载验证
程序下载成功后,利用手机连接 ESP32-S3 热点设备(本次实验命名为"MyWifi"),当手机连接热点设备成功时,LCD 显示手机的 MAC 地址等信息,当手机从已连接状态断开时, LCD 显示断开的外部设备的MAC 地址。下图为连接成功的 LCD 显示效果图。
图 50.3.1 外部设备连接热点设备
下图为外部设备从已连接状态断开效果图,如下所示。
图 50.3.2 外部设备断开热点设备