本章学习把 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);

返回值:

返回值

类型

说明

成功

esp_netif_t*

指向新创建的网络接口对象的指针

失败

NULL

如果创建失败则返回 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);

参数说明:

参数名

类型

方向

说明

if_key

const char*

输入

网络接口的唯一标识键字符串

表 50.2.2.3 函数esp_netif_get_handle_from_ifkey()参数说明

返回值:

返回值

类型

说明

成功

esp_netif_t*

指向找到的网络接口对象的指针

失败

NULL

未找到与提供的 if_key匹配的网络接口

表 50.2.2.4 函数esp_netif_get_handle_from_ifkey()返回值说明

    主要作用:

  • ​​接口查找​​:在已创建的网络接口中查找具有指定键名的接口;
  • ​​句柄获取​​:返回找到的接口的操作句柄;
  • ​​接口访问​​:提供了一种在不同代码模块中访问同一网络接口的方式。

    常用接口键名,系统为默认创建的网络接口预定义了以下键名(本次实验传入WIFI_AP_DEF"):

接口键名 (if_key)

对应的接口类型

创建函数

"WIFI_STA_DEF"

默认 WiFi Station 接口

esp_netif_create_default_wifi_sta()

"WIFI_AP_DEF"

默认 WiFi AP 接口

esp_netif_create_default_wifi_ap()

"ETH_DEF"

默认以太网接口

esp_netif_create_default_eth()

表 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

esp_netif_t*

输入

要查询的网络接口句柄。

本次实验为esp_netif_get_handle_from_ifkey()函数的返回值

ip_info

esp_netif_ip_info_t*

输出

存储获取的 IP 信息的结构体指针

表 50.2.2.6 函数esp_netif_get_ip_info()参数说明   

    返回值:

返回值

类型

说明

ESP_OK

esp_err_t

获取成功

其他错误码

esp_err_t

获取失败

表 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);

     参数说明:

参数名

类型

方向

说明

addr

struct in_addr

输入

包含要转换的二进制IPv4地址的结构体

buf

char*

输出

用于存储结果字符串的缓冲区

buflen

int

输入

缓冲区的大小(字节数)

表 50.2.2.7 函数inet_ntoa_r()参数说明      

    返回值:

返回值

类型

说明

成功

char*

指向结果字符串的指针(与 buf参数相同)

失败

NULL

如果缓冲区太小无法容纳结果字符串

表 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 外部设备断开热点设备

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐