ESP32-S3的WiFi库支持配置及监控ESP32-S3的Wi-Fi连网功能。它支持配置基站模式(即STA 模式或 WiFi 客户端模式),此时 ESP32-S3 连接到接入点(AP)。还支持 AP 模式(即SoftAP 模式或接入点模式),此时基站连接到 ESP32-S3。同时,支持 AP-STA 共存模式,此时ESP32-S3 既是接入点,同时又作为 STA。本章节的实验是基于乐鑫官方提供的 WiFi 库来实现的,但遗憾的是乐鑫并没有公开 WiFi 库的源码,所以只能调用 API 函数实现。
        本章分为如下几个小节:
48.1 WiFi 模式概述
48.2 硬件设计
48.3 软件设计
48.4 下载验证

48.1 WiFi 模式概述

        WiFi 主要有两种模式: STA 和 AP 模式。 AP 模式即无线接入点,是常说的手机热点,被其他设备连接; STA 模式即 Station,是连接热点的设备。另外, ESP32S3 可支持 STA 和 AP两种模式共存,就像手机那样可以开热点,也可以连接其他热点。
        WiFi 库支持配置及监控 ESP32S3 Wi-Fi 连网功能。支持配置:
①: Station 模式(即 STA 模式或 WiFi 客户端模式),此时 ESP32 S3 连接到接入点 (AP)。
②: AP 模式(即 Soft-AP 模式或接入点模式),此时基站连接到 ESP32S3 设备。
③: Station/AP 共存模式(ESP32S3 既是接入点,同时又作为基站连接到另外一个接入点)。
④:上述模式的各种安全模式(WPA、 WPA2 及 WEP 等)。
⑤:扫描接入点(包括主动扫描及被动扫描)。
⑥:使用混杂模式监控 IEEE802.11 Wi-Fi 数据包。
        下面学习ESP32S3 开启 WiFi 两种模式启动流程,如下:
(1) WiFi-AP 启动流程
        WiFi-AP 启动流程如下。

图 48.1.1 AP 启动流程

        上图展示了 ESP32 系列芯片以 AP 模式开启 WiFi 的启动流程。首先,系统需要对 lwIP 协议栈进行初始化。接着,创建一个任务,该任务将用于触发相应的事件。然后,配置 WiFi 参数和AP 模式参数。最后,启动 WiFi,从而完成以 AP 模式开启 WiFi 的操作。

(2)WiFi-STA 启动流程

图 48.1.2 STA 模式启动流程

        上图展示了 ESP32 系列芯片以 STA 模式开启 WiFi 的启动流程。首先,系统需要创建定时器和事件组,并对 lwIP 协议栈进行初始化。接着,创建一个任务,该任务将用于触发相应的事件。然后,配置 WiFi 参数和 STA 模式参数。最后,启动 WiFi,从而完成以 STA 模式开启 WiFi的操作。

48.2 硬件设计

1. 例程功能
        本章实验功能简介:扫描附近的 WIFI 信号,并在 LCD 显示屏右侧显示 12 个 WIFI 名称。
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 的片上资源,因此并没有相应的连接原理图。

48.3 软件设计
48.3.1 程序流程图

        本实验的程序流程图。

图 48.3.1 程序流程图

48.3.2 程序解析

48.3.2.1 本次实验用到了库函数说明

(1)网络接口初始化函数

函数名

功能

参数

返回值

说明

esp_netif_init()

初始化TCP/IP网络栈

esp_err_t

使用任何网络功能前必须调用的基础初始化函数

esp_event_loop_create_default()

创建默认的事件循环

esp_err_t

用于处理WiFi、IP等系统事件的事件循环

esp_netif_create_default_wifi_sta()

创建默认的Station模式网络接口

esp_netif_t*

创建并配置默认的STA模式网络接口,返回指向网络接口对象的指针

表 48.3.2.1 网络接口初始化函数说明

(2)WiFi驱动控制函数

函数名

功能

参数

返回值

说明

esp_wifi_init(const wifi_init_config_t *config)

初始化WiFi驱动

config: WiFi初始化配置结构体指针

esp_err_t

使用WIFI_INIT_CONFIG_DEFAULT()宏获取默认配置,必须在其他WiFi函数之前调用

esp_wifi_set_mode(wifi_mode_t mode)

设置WiFi工作模式

mode: WiFi模式(如WIFI_MODE_STA

esp_err_t

必须在esp_wifi_start()之前调用

esp_wifi_start(void)

启动WiFi

esp_err_t

根据之前设置的模式启动WiFi功能

表 48.3.2.2 WiFi驱动控制函数

结构体名称

说明

重要字段

wifi_init_config_t

WiFi初始化配置结构体

使用WIFI_INIT_CONFIG_DEFAULT()宏获取默认值

常用WiFi模式wifi_mode_t枚举值:

枚举值

说明

WIFI_MODE_NULL

WiFi未初始化

WIFI_MODE_STA

Station模式(连接其他热点)

WIFI_MODE_AP

Access Point模式(创建热点)

WIFI_MODE_APSTA

同时启用STA和AP模式

表 48.3.2.3 WiFi模式wifi_mode_t枚举值

(3)WiFi扫描相关函数

函数名

功能

参数

返回值

说明

esp_wifi_scan_start(const wifi_scan_config_t *config, bool block)

开始WiFi扫描

config: 扫描配置(可为NULL使用默认配置)
block: 是否阻塞等待扫描完成

esp_err_t

默认配置会扫描所有信道,不指定特定SSID,显示隐藏网络

esp_wifi_scan_get_ap_num(uint16_t *number)

获取扫描到的AP数量

number: 指向存储AP数量的变量的指针

esp_err_t

必须在扫描完成后调用

esp_wifi_scan_get_ap_records(uint16_t *number, wifi_ap_record_t *ap_records)

获取扫描到的AP详细信息

number: 输入时是最大记录数,输出时是实际获取的记录数
ap_records: 指向存储AP信息的数组

esp_err_t

wifi_ap_record_t结构体包含SSID、RSSI、信道、认证模式等信息

表 48.3.2.4 WiFi扫描相关函数

结构体wifi_ap_record_t字段说明:

字段名

类型

说明

取值范围/示例

​​ssid​​

uint8_t[32]

接入点的 SSID(网络名称)

最大32字符,以null结尾的字符串

​​bssid​​

uint8_t[6]

接入点的 MAC 地址(BSSID)

6字节的MAC地址,如 {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}

​​primary​​

uint8_t

主信道

1-14(取决于地区法规)

​​second​​

uint8_t

次要信道(用于HT40模式)

0:无次要信道
1-14:次要信道号

​​rssi​​

int8_t

接收信号强度指示

-127 到 0(负值)
值越接近0,信号越强
-30:极强信号
-67:良好信号
-70:一般信号
-80:弱信号

​​authmode​​

wifi_auth_mode_t

认证模式

见下方认证模式表格

​​pairwise_cipher​​

wifi_cipher_type_t

单播加密算法

见下方加密算法表格

​​group_cipher​​

wifi_cipher_type_t

组播加密算法

见下方加密算法表格

​​ant​​

wifi_ant_t

天线编号

WIFI_ANT_0:天线0
WIFI_ANT_1:天线1
WIFI_ANT_MAX:自动选择

​​phy_11b​​

uint32_t

支持802.11b

0:不支持
1:支持

​​phy_11g​​

uint32_t

支持802.11g

0:不支持
1:支持

​​phy_11n​​

uint32_t

支持802.11n(HT)

0:不支持
1:支持

​​phy_lr​​

uint32_t

支持Long Range模式

0:不支持
1:支持

​​wps​​

uint32_t

支持WPS(WiFi Protected Setup)

0:不支持
1:支持

​​ftm_responder​​

uint32_t

支持FTM(Fine Timing Measurement)响应

0:不支持
1:支持

​​ftm_initiator​​

uint32_t

支持FTM发起

0:不支持
1:支持

​​reserved​​

uint32_t

保留字段,供未来使用

-

表 48.3.2.5 结构体wifi_ap_record_t字段说明

认证模式详细说明 (wifi_auth_mode_t):

枚举值

说明

安全级别

​​WIFI_AUTH_OPEN​​

开放网络,无需认证

无安全

​​WIFI_AUTH_WEP​​

WEP认证

低安全

​​WIFI_AUTH_WPA_PSK​​

WPA-PSK认证

中等安全

​​WIFI_AUTH_WPA2_PSK​​

WPA2-PSK认证

高安全

​​WIFI_AUTH_WPA_WPA2_PSK​​

WPA/WPA2混合认证

高安全

​​WIFI_AUTH_WPA2_ENTERPRISE​​

WPA2企业级认证

企业级安全

​​WIFI_AUTH_WPA3_PSK​​

WPA3-PSK认证

极高安全

​​WIFI_AUTH_WPA2_WPA3_PSK​​

WPA2/WPA3混合认证

极高安全

​​WIFI_AUTH_WAPI_PSK​​

WAPI-PSK认证

中国标准

​​WIFI_AUTH_OWE​​

OWE(Opportunistic Wireless Encryption)

增强开放安全

​​WIFI_AUTH_MAX​​

认证模式最大值

-

表 48.3.2.6 结构体认证模式详细说明

加密算法详细说明 (wifi_cipher_type_t):

枚举值

说明

常用场景

​​WIFI_CIPHER_TYPE_NONE​​

无加密

开放网络

​​WIFI_CIPHER_TYPE_WEP40​​

WEP加密,40位密钥

WEP网络

​​WIFI_CIPHER_TYPE_WEP104​​

WEP加密,104位密钥

WEP网络

​​WIFI_CIPHER_TYPE_TKIP​​

TKIP加密

WPA网络

​​WIFI_CIPHER_TYPE_CCMP​​

CCMP加密(AES)

WPA2/WPA3网络

​​WIFI_CIPHER_TYPE_TKIP_CCMP​​

TKIP/CCMP混合加密

兼容模式

​​WIFI_CIPHER_TYPE_AES_CMAC128​​

AES-CMAC-128加密

WAPI网络

​​WIFI_CIPHER_TYPE_SMS4​​

SMS4加密

WAPI网络

​​WIFI_CIPHER_TYPE_GCMP​​

GCMP加密

未来标准

​​WIFI_CIPHER_TYPE_GCMP256​​

GCMP-256加密

未来标准

​​WIFI_CIPHER_TYPE_UNKNOWN​​

未知加密类型

-

(4)未包含在代码中但重要的相关函数

函数名

功能

参数

返回值

说明

esp_wifi_stop(void)

停止WiFi

esp_err_t

停止WiFi功能,释放资源

esp_wifi_deinit(void)

反初始化WiFi驱动

esp_err_t

清理WiFi驱动占用的资源

esp_wifi_connect(void)

连接到AP

esp_err_t

连接到之前配置的AP

esp_wifi_disconnect(void)

断开当前连接

esp_err_t

断开与当前AP的连接

esp_wifi_set_config(wifi_interface_t interface, wifi_config_t *conf)

设置WiFi配置

interface: 接口类型
conf: 配置结构体

esp_err_t

设置STA或AP模式的配置参数

表 48.3.2.7 未包含在代码中但重要的相关函数说明

   注意事项:

  • 资源管理​​:WiFi 功能会消耗较多内存和电源,不使用时应及时停止和反初始化;
  • 事件处理​​:对于非阻塞扫描,需要通过事件循环处理 WIFI_EVENT_SCAN_DONE事件;
  • 配置选项​​:可以通过 wifi_scan_config_t结构体自定义扫描参数,如:指定扫描特定 SSID、设置主动/被动扫描、限制扫描信道、设置扫描时间。

48.3.2.2 main函数解析

        在本章节实验中,我们只关心 main.c 文件内容即可,该文件内容如下:
        完整工作流程初始化阶段​​ ->配置阶段->​扫描阶段->清理阶段​​(代码中有缺失,本次实验补充上)上述源码按这个流程实现,从 wifi_scan 函数开始,该函数首先创建了 event loop 事件回调,即任务事件处理机制。接着,配置 WiFi为 STA模式(设备连接热点模式)并设置相应的参数,然后启动 WiFi。最后,程序会扫描附近的 12 个热点,并在 LCD 上显示热点的名称。此外,读者还可以通过串口查看热点的安全模式等身份认证信息。

/* 存储12个WIFI名称 */
#define DEFAULT_SCAN_LIST_SIZE  12
static const char *TAG = "scan";

/**
 * @brief       身份认证模式
 * @param       authmode :身份验证模式
 * @retval      无
 */
static void print_auth_mode(int authmode)
{
    switch (authmode) {
    case WIFI_AUTH_OPEN:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_OPEN");
        break;
    case WIFI_AUTH_OWE:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_OWE");
        break;
    case WIFI_AUTH_WEP:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WEP");
        break;
    case WIFI_AUTH_WPA_PSK:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA_PSK");
        break;
    case WIFI_AUTH_WPA2_PSK:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA2_PSK");
        break;
    case WIFI_AUTH_WPA_WPA2_PSK:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA_WPA2_PSK");
        break;
    case WIFI_AUTH_ENTERPRISE:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_ENTERPRISE");
        break;
    case WIFI_AUTH_WPA3_PSK:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA3_PSK");
        break;
    case WIFI_AUTH_WPA2_WPA3_PSK:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA2_WPA3_PSK");
        break;
    case WIFI_AUTH_WPA3_ENTERPRISE:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA3_ENTERPRISE");
        break;
    case WIFI_AUTH_WPA2_WPA3_ENTERPRISE:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA2_WPA3_ENTERPRISE");
        break;
    case WIFI_AUTH_WPA3_ENT_192:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA3_ENT_192");
        break;
    default:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_UNKNOWN");
        break;
    }
}

/**
 * @brief       打印WIFI密码类型
 * @param       pairwise_cipher :密码类型
 * @param       group_cipher    :群密码类型
 * @retval      无
 */
static void print_cipher_type(int pairwise_cipher, int group_cipher)
{
    switch (pairwise_cipher) {
    case WIFI_CIPHER_TYPE_NONE:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_NONE");
        break;
    case WIFI_CIPHER_TYPE_WEP40:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_WEP40");
        break;
    case WIFI_CIPHER_TYPE_WEP104:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_WEP104");
        break;
    case WIFI_CIPHER_TYPE_TKIP:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_TKIP");
        break;
    case WIFI_CIPHER_TYPE_CCMP:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_CCMP");
        break;
    case WIFI_CIPHER_TYPE_TKIP_CCMP:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_TKIP_CCMP");
        break;
    case WIFI_CIPHER_TYPE_AES_CMAC128:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_AES_CMAC128");
        break;
    case WIFI_CIPHER_TYPE_SMS4:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_SMS4");
        break;
    case WIFI_CIPHER_TYPE_GCMP:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_GCMP");
        break;
    case WIFI_CIPHER_TYPE_GCMP256:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_GCMP256");
        break;
    default:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_UNKNOWN");
        break;
    }

    switch (group_cipher) {
    case WIFI_CIPHER_TYPE_NONE:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_NONE");
        break;
    case WIFI_CIPHER_TYPE_WEP40:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_WEP40");
        break;
    case WIFI_CIPHER_TYPE_WEP104:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_WEP104");
        break;
    case WIFI_CIPHER_TYPE_TKIP:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_TKIP");
        break;
    case WIFI_CIPHER_TYPE_CCMP:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_CCMP");
        break;
    case WIFI_CIPHER_TYPE_TKIP_CCMP:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_TKIP_CCMP");
        break;
    case WIFI_CIPHER_TYPE_SMS4:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_SMS4");
        break;
    case WIFI_CIPHER_TYPE_GCMP:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_GCMP");
        break;
    case WIFI_CIPHER_TYPE_GCMP256:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_GCMP256");
        break;
    default:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_UNKNOWN");
        break;
    }
}

/**
 * @brief       将Wi-Fi初始化为sta并设置扫描方法
 * @param       无
 * @retval      无
 */
void wifi_scan(void)
{
    char lcd_buff[100] = {0};
    /* 网卡初始化 */
    ESP_ERROR_CHECK(esp_netif_init());
    /* 创建新的事件循环 */
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    /* 用户初始化STA模式 */
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);
    /* wifi配置初始化 */
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    uint16_t number = DEFAULT_SCAN_LIST_SIZE;
    wifi_ap_record_t ap_info[DEFAULT_SCAN_LIST_SIZE];
    uint16_t ap_count = 0;
    memset(ap_info, 0, sizeof(ap_info));
    /* 设置WIFI为STA模式 */
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    /* 启动WIFI */
    ESP_ERROR_CHECK(esp_wifi_start());
    /* 开始扫描附件的WIFI */
    esp_wifi_scan_start(NULL, true);
    /* 获取上次扫描中找到的AP数量 */
    ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
    /* 获取上次扫描中找到的AP列表 */
    ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info));
	//停止 WiFi
	esp_wifi_stop();
	//释放 WiFi 子系统占用的所有资源
	esp_wifi_deinit();
	
    ESP_LOGI(TAG, "Total APs scanned = %u", ap_count);
    /* 下面是打印附件的WIFI信息 */
    for (int i = 0; (i < DEFAULT_SCAN_LIST_SIZE) && (i < ap_count); i++)
    {
        sprintf(lcd_buff, "%s",ap_info[i].ssid);
        spilcd_show_string(200, 20 * i, 240, 16, 16, lcd_buff, BLUE);
        ESP_LOGI(TAG, "SSID \t\t%s", ap_info[i].ssid);
        ESP_LOGI(TAG, "RSSI \t\t%d", ap_info[i].rssi);
        print_auth_mode(ap_info[i].authmode);
        
        if (ap_info[i].authmode != WIFI_AUTH_WEP)
        {
            print_cipher_type(ap_info[i].pairwise_cipher, ap_info[i].group_cipher);
        }

        ESP_LOGI(TAG, "Channel \t\t%d\n", ap_info[i].primary);
    }
}

/**
 * @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(10, 80, 240, 32, 32, "ESP32-S3", RED);
    spilcd_show_string(10, 120, 240, 24, 24, "WiFi SCAN Test", RED);
    spilcd_show_string(10, 150, 240, 16, 16, "ATOM@ALIENTEK", RED);
    spilcd_draw_line(190,0,190,239,DARKBLUE);
    wifi_scan();

    while (1)
    {
        LED0_TOGGLE();
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}
48.4 下载验证

        程序下载成功后,可以看到 LCD 显示附近热点名称,如下图所示:

图 48.4.1 附近热点名称信息

Logo

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

更多推荐