第四十九章 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热点连接成功显示

相关推荐
时光の尘2 小时前
【PCB电路设计】常见元器件简介(电阻、电容、电感、二极管、三极管以及场效应管)
单片机·嵌入式硬件·pcb·二极管·电感·三极管·场效应管
Lu Zelin2 小时前
单片机为什么不能跑Linux
linux·单片机·嵌入式硬件
宁静致远20213 小时前
stm32 freertos下基于hal库的模拟I2C驱动实现
stm32·嵌入式硬件·freertos
七芒星20234 小时前
多目标识别YOLO :YOLOV3 原理
图像处理·人工智能·yolo·计算机视觉·目标跟踪·分类·聚类
Learn Beyond Limits4 小时前
Mean Normalization|均值归一化
人工智能·神经网络·算法·机器学习·均值算法·ai·吴恩达
摩羯座-185690305945 小时前
爬坑 10 年!京东店铺全量商品接口实战开发:从分页优化、SKU 关联到数据完整性闭环
linux·网络·数据库·windows·爬虫·python
ACERT3335 小时前
5.吴恩达机器学习—神经网络的基本使用
人工智能·python·神经网络·机器学习
YoungLime5 小时前
DVWA靶场之十三:CSP 绕过(Content Security Policy (CSP) Bypass)
网络·安全·web安全
C嘎嘎嵌入式开发5 小时前
(一) 机器学习之深度神经网络
人工智能·神经网络·dnn