第四十九章 ESP32S3 WiFi 路由实验

本章学习把ESP32-S3配置为 STA模式,即连接附近的热点。 STA模式相关知识请读者查看上一章节的内容。

本章分为如下几个小节:

49.1 硬件设计

49.2 软件设计

49.3 下载验证

49.1 硬件设计
  1. 例程功能

本章实验功能简介:扫描附近的 WIFI 信号,并连接到一个真实存在的 WIFI 热点。

  1. 硬件资源

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

  1. 原理图

本章实验使用的 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_handlerNULL)。
其他错误码 其他内部错误。

表 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热点连接成功显示

相关推荐
NAGNIP7 小时前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab9 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab9 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP12 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年12 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼13 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS13 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区14 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈14 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang14 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx