概览

[English]

ESP32-C61 Wi-Fi 功能列表

ESP32-C61 支持以下 Wi-Fi 功能:

  • Wi-Fi Aware (NAN)

如何编写 Wi-Fi 应用程序

准备工作

一般来说,要编写自己的 Wi-Fi 应用程序,最高效的方式是先选择一个相似的应用程序示例,然后将其中可用的部分移植到自己的项目中。如果你希望编写一个强健的 Wi-Fi 应用程序,强烈建议在开始之前先阅读本文。非强制要求,请依个人情况而定。

本文将补充说明 Wi-Fi API 和 Wi-Fi 示例的相关信息,重点描述使用 Wi-Fi API 的原则、当前 Wi-Fi API 实现的限制以及使用 Wi-Fi 时的常见错误。同时,本文还介绍了 Wi-Fi 驱动程序的一些设计细节。建议选择一个示例 example 进行参考。

  • wifi/getting_started/station 演示如何使用 station 功能连接到 AP。

  • wifi/getting_started/softAP 演示如何使用 SoftAP 功能将 ESP32-C61 配置为 AP。

  • wifi/scan 演示如何扫描可用的 AP,配置扫描设置,并显示扫描结果。

  • wifi/fast_scan 演示如何执行快速和全通道扫描,查找附近的 AP,设置信号强度的阈值和认证模式,并根据信号强度和认证模式连接到最合适的 AP。

  • wifi/wps 演示如何使用 WPS 入网功能,简化连接 Wi-Fi 路由器的过程,支持 PIN 或 PBC 模式。

  • wifi/wps_softap_registrar 演示如何在 SoftAP 模式下使用 WPS 注册器功能,从而简化从 station 连接到 Wi-Fi SoftAP 的过程。

  • wifi/smart_config 演示如何使用 smartconfig 功能通过 ESPTOUCH app 连接到目标 AP。

  • wifi/power_save 演示如何使用 station 模式的省电模式。

  • wifi/softap_sta 演示如何配置 ESP32-C61 同时用作 AP 和 station,从而可将其用作 Wi-Fi NAT 路由器。

  • wifi/iperf 演示如何实现 iPerf 性能测量工具所使用的协议,允许在两个芯片之间或在单个芯片和运行 iPerf 工具的计算机之间进行性能测量,并提供测试 station/SoftAP TCP/UDP RX/TX 吞吐量的具体说明。

  • wifi/roaming/roaming_app 演示如何使用 Wi-Fi Roaming App 功能,在兼容的 AP 之间高效漫游。

  • wifi/roaming/roaming_11kvr 演示如何使用 11k 和 11v API 实现漫游功能。

  • wifi/itwt 演示如何使用 iTWT 功能,该功能仅在 station 模式下工作,并在不同的省电模式下提供设置、拆卸和挂起的命令,还展示了启用和禁用 iTWT 时的电流消耗差异。

设置 Wi-Fi 编译时选项

请参阅 Wi-Fi Menuconfig

Wi-Fi 初始化

请参阅 ESP32-C61 Wi-Fi station 通用场景ESP32-C61 Wi-Fi AP 通用场景

启动/连接 Wi-Fi

请参阅 ESP32-C61 Wi-Fi station 通用场景ESP32-C61 Wi-Fi AP 通用场景

事件处理

通常,在理想环境下编写代码难度并不大,如 WIFI_EVENT_STA_STARTWIFI_EVENT_STA_CONNECTED 中所述。难度在于如何在现实的困难环境下编写代码,如 WIFI_EVENT_STA_DISCONNECTED 中所述。能否在后者情况下完美地解决各类事件冲突,是编写一个强健的 Wi-Fi 应用程序的根本。请参阅 ESP32-C61 Wi-Fi 事件描述, ESP32-C61 Wi-Fi station 通用场景, ESP32-C61 Wi-Fi AP 通用场景。另可参阅 ESP-IDF 中的 事件处理概述

编写错误恢复程序

除了能在比较差的环境下工作,错误恢复能力也对一个强健的 Wi-Fi 应用程序至关重要。请参阅 ESP32-C61 Wi-Fi API 错误代码

ESP32-C61 Wi-Fi API 错误代码

所有 ESP32-C61 Wi-Fi API 都有定义好的返回值,即错误代码。这些错误代码可分类为:

  • 无错误,例如:返回值 ESP_OK 代表 API 成功返回

  • 可恢复错误,例如:ESP_ERR_NO_MEM

  • 不可恢复的非关键性错误

  • 不可恢复的关键性错误

一个错误是否为关键性取决于其 API 和应用场景,并且由 API 用户定义。

要使用 Wi-Fi API 编写一个强健的应用程序,根本原则便是要时刻检查错误代码并编写相应的错误处理代码。 一般来说,错误处理代码可用于解决:

  • 可恢复错误,你可以编写相应的错误恢复代码。例如,当 esp_wifi_start() 返回 ESP_ERR_NO_MEM 时,调用可恢复错误处理代码 vTaskDelay 以获取微秒级的延迟后重新尝试。

  • 不可恢复非关键性错误,打印错误代码可以帮助你更好地处理该类错误。

  • 不可恢复关键性错误,可使用 "assert" 语句处理该类错误。例如,如果 esp_wifi_set_mode() 返回 ESP_ERR_WIFI_NOT_INIT,这意味着 Wi-Fi 驱动程序未通过 esp_wifi_init() 成功初始化。这类错误可以在应用开发阶段被快速检测到。

esp_common/include/esp_err.h 中, ESP_ERROR_CHECK 负责检查返回值。这是一个较为常见的错误处理代码,可在应用程序开发阶段作为默认的错误处理代码。但是,我们强烈建议 API 的使用者编写自己的错误处理代码。

初始化 ESP32-C61 Wi-Fi API 参数

在为 API 初始化结构体参数时,应遵循以下两种方式之一:

  • 显式设置参数的所有字段

  • 先使用 get API 获取当前配置,然后设置特定于应用程序的字段

初始化或获取整个结构体这一步至关重要,因为大多数情况下,值为 0 意味着程序使用了默认值。未来可能会向该结构体添加更多字段,而将这些字段初始化为 0 可以确保在 ESP-IDF 更新到新版本后,应用程序仍能正常运行。

ESP32-C61 Wi-Fi 编程模型

ESP32-C61 Wi-Fi 编程模型如下图所示:

Wi-Fi 编程模型

Wi-Fi 驱动程序可以看作是一个无法感知上层代码(如 TCP/IP 堆栈、应用程序任务、事件任务等)的黑匣子。通常,应用程序任务(代码)负责调用 Wi-Fi 驱动程序 API 来初始化 Wi-Fi,并在必要时处理 Wi-Fi 事件。然后,Wi-Fi 驱动程序接收 API 调用,处理后将事件发送给应用程序。

Wi-Fi 事件处理是在 esp_event 库 的基础上进行的。Wi-Fi 驱动程序将事件发送至 默认事件循环,应用程序便可以使用 esp_event_handler_register() 中的回调函数处理这些事件。除此之外,esp_netif 组件 也负责处理 Wi-Fi 事件,并产生一系列默认行为。例如,当 Wi-Fi station 连接至一个 AP 时,esp_netif 将自动开启 DHCP 客户端服务(系统默认)。

ESP32-C61 Wi-Fi 事件描述

WIFI_EVENT_WIFI_READY

Wi-Fi 驱动程序永远不会生成此事件,因此,应用程序的事件回调函数可忽略此事件。在未来的版本中,此事件可能会被移除。

WIFI_EVENT_SCAN_DONE

扫描完成事件,由 esp_wifi_scan_start() 函数触发,将在以下情况下产生:

  • 扫描已完成,例如:Wi-Fi 已成功找到目标 AP 或已扫描所有信道。

  • 当前扫描因函数 esp_wifi_scan_stop() 而终止。

  • 在当前扫描完成之前调用了函数 esp_wifi_scan_start()。此时,新的扫描将覆盖当前扫描过程,并生成一个扫描完成事件。

以下情况下将不会产生扫描完成事件:

接收到此事件后,事件任务暂不做任何响应。首先,应用程序的事件回调函数需调用 esp_wifi_scan_get_ap_num()esp_wifi_scan_get_ap_records() 获取已扫描的 AP 列表,然后触发 Wi-Fi 驱动程序释放在扫描过程中占用的内存空间(切记该步骤)。 更多详细信息,请参阅 ESP32-C61 Wi-Fi 扫描

WIFI_EVENT_STA_START

如果调用函数 esp_wifi_start() 后接收到返回值 ESP_OK,且当前 Wi-Fi 处于 station 或 station/AP 共存模式,则将产生此事件。接收到此事件后,事件任务将初始化 LwIP 网络接口 (netif)。通常,应用程序的事件回调函数需调用 esp_wifi_connect() 来连接已配置的 AP。

WIFI_EVENT_STA_STOP

如果调用函数 esp_wifi_stop() 后接收到返回值 ESP_OK,且当前 Wi-Fi 处于 station 或 station/AP 共存模式,则将产生此事件。接收到此事件后,事件任务将进行释放 station IP 地址、终止 DHCP 客户端服务、移除 TCP/UDP 相关连接并清除 LwIP station netif 等动作。此时,应用程序的事件回调函数通常不需做任何响应。

WIFI_EVENT_STA_CONNECTED

如果调用函数 esp_wifi_connect() 后接收到返回值 ESP_OK,且 station 已成功连接目标 AP,则将产生此连接事件。接收到此事件后,事件任务将启动 DHCP 客户端服务并开始获取 IP 地址。此时,Wi-Fi 驱动程序已准备就绪,可发送和接收数据。如果你的应用程序不依赖于 LwIP(即 IP 地址),则此刻便可以开始应用程序开发工作。但是,如果你的应用程序需基于 LwIP 进行,则还需等待 got ip 事件发生后才可开始。

WIFI_EVENT_STA_DISCONNECTED

此事件将在以下情况下产生:

  • 调用了函数 esp_wifi_disconnect()esp_wifi_stop(),且 Wi-Fi station 已成功连接至 AP。

  • 调用了函数 esp_wifi_connect(),但 Wi-Fi 驱动程序因为某些原因未能成功连接至 AP,例如:未扫描到目标 AP、验证超时等。或存在多个 SSID 相同的 AP,station 无法连接所有已找到的 AP,也将产生该事件。

  • Wi-Fi 连接因为某些原因而中断,例如:station 连续多次丢失 N beacon、AP 踢掉 station、AP 认证模式改变等。

接收到此事件后,事件任务的默认动作为:

  • 关闭 station 的 LwIP netif。

  • 通知 LwIP 任务清除导致所有套接字状态错误的 UDP/TCP 连接。针对基于套接字编写的应用程序,其回调函数可以在接收到此事件时(如有必要)关闭并重新创建所有套接字。

应用程序处理此事件最常用的方法为:调用函数 esp_wifi_connect() 重新连接 Wi-Fi。但是,如果此事件是由函数 esp_wifi_disconnect() 引发的,则应用程序不应调用 esp_wifi_connect() 来重新连接。应用程序须明确区分此事件的引发原因,因为某些情况下应使用其它更好的方式进行重新连接。请参阅 Wi-Fi 重新连接连接 Wi-Fi 时扫描

需要注意的另一点是:接收到此事件后,LwIP 的默认动作是终止所有 TCP 套接字连接。大多数情况下,该动作不会造成影响。但对某些特殊应用程序可能除外。例如:

  • 应用程序创建一个了 TCP 连接,以维护每 60 秒发送一次的应用程序级、保持活动状态的数据。

  • 由于某些原因,Wi-Fi 连接被切断并引发了 WIFI_EVENT_STA_DISCONNECTED 事件。根据当前实现,此时所有 TCP 连接都将被移除,且保持活动的套接字将处于错误的状态中。但是,由于应用程序设计者认为网络层 不应 考虑这个 Wi-Fi 层的错误,因此应用程序不会关闭套接字。

  • 5 秒后,因为在应用程序的事件回调函数中调用了 esp_wifi_connect(),Wi-Fi 连接恢复。同时,station 连接至同一个 AP 并获得与之前相同的 IPV4 地址。

  • 60 秒后,当应用程序发送具有保持活动状态的套接字的数据时,套接字将返回错误,应用程序将关闭套接字并在必要时重新创建。

在上述场景中,理想状态下应用程序套接字和网络层将不会受到影响,因为在此过程中 Wi-Fi 连接只是短暂地断开然后快速恢复。

IP_EVENT_STA_GOT_IP

当 DHCP 客户端成功从 DHCP 服务器获取 IPV4 地址或 IPV4 地址发生改变时,将引发此事件。此事件意味着应用程序一切就绪,可以开始任务(如:创建套接字)。

IPV4 地址可能由于以下原因而发生改变:

  • DHCP 客户端无法重新获取/绑定 IPV4 地址,且 station 的 IPV4 重置为 0。

  • DHCP 客户端重新绑定了其它地址。

  • 静态配置的 IPV4 地址已发生改变。

函数 ip_event_got_ip_t 中的字段 ip_change 说明了 IPV4 地址是否发生改变。

套接字的状态是基于 IPV4 地址的,这意味着,如果 IPV4 地址发生改变,则所有与此 IPV4 相关的套接字都将变为异常。接收到此事件后,应用程序需关闭所有套接字,并在 IPV4 变为有效地址时重新创建应用程序。

IP_EVENT_GOT_IP6

当 IPV6 SLAAC 支持自动为 ESP32-C61 配置一个地址,或 ESP32-C61 地址发生改变时,将引发此事件。此事件意味着应用程序一切就绪,可以开始任务(如:创建套接字)。

IP_EVENT_STA_LOST_IP

当 IPV4 地址失效时,将引发此事件。

此事件不会在 Wi-Fi 断连后立刻出现。Wi-Fi 连接断开后,首先将启动一个 IPV4 地址丢失计时器(可通过 CONFIG_ESP_NETIF_LOST_IP_TIMER_ENABLECONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL 配置)。如果 station 在该计时器超时之前成功获取了 IPV4 地址,则不会发生此事件。否则,此事件将在计时器超时时发生。

一般来说,应用程序可忽略此事件。这只是一个调试事件,主要使应用程序获知 IPV4 地址已丢失。

WIFI_EVENT_AP_START

WIFI_EVENT_STA_START 事件相似。

WIFI_EVENT_AP_STOP

WIFI_EVENT_STA_STOP 事件相似。

WIFI_EVENT_AP_STACONNECTED

每当有一个 station 成功连接 ESP32-C61 AP 时,将引发此事件。接收到此事件后,事件任务将不做任何响应,应用程序的回调函数也可忽略这一事件。但是,你可以在此时进行一些操作,例如:获取已连接 station 的信息等。

WIFI_EVENT_AP_STADISCONNECTED

此事件将在以下情况下发生:

发生此事件时,事件任务将不做任何响应,但应用程序的事件回调函数需执行一些操作,例如:关闭与此 station 相关的套接字等。

WIFI_EVENT_AP_PROBEREQRECVED

默认情况下,此事件处于禁用状态,应用程序可以通过调用 API esp_wifi_set_event_mask() 启用。 启用后,每当 AP 接收到 probe request 时都将引发此事件。

WIFI_EVENT_STA_BEACON_TIMEOUT

如果 station 在 inactive 时间内未收到所连接 AP 的 beacon,将发生 beacon 超时,将引发此事件。inactive 时间通过调用函数 esp_wifi_set_inactive_time() 设置。

WIFI_EVENT_CONNECTIONLESS_MODULE_WAKE_INTERVAL_START

非连接模块在 Interval 开始时触发此事件。 请参考 非连接模块功耗管理

ESP32-C61 Wi-Fi 配置

使能 Wi-Fi NVS 时,所有配置都将存储到 flash 中;反之,请参阅 Wi-Fi NVS Flash

Wi-Fi 模式

调用函数 esp_wifi_set_mode() 设置 Wi-Fi 模式。

模式

描述

WIFI_MODE_NULL

NULL 模式:此模式下,内部数据结构不分配给 station 和 AP,同时,station 和 AP 接口不会为发送/接收 Wi-Fi 数据进行初始化。通常,此模式用于 Sniffer,或者你不想通过调用函数 esp_wifi_deinit() 卸载整个 Wi-Fi 驱动程序来同时停止 station 和 AP。

WIFI_MODE_STA

station 模式:此模式下,esp_wifi_start() 将初始化内部 station 数据,同时 station 接口准备发送/接收 Wi-Fi 数据。调用函数 esp_wifi_connect() 后,station 将连接到目标 AP。

WIFI_MODE_AP

AP 模式:在此模式下,esp_wifi_start() 将初始化内部 AP 数据,同时 AP 接口准备发送/接收 Wi-Fi 数据。随后,Wi-Fi 驱动程序开始广播 beacon,AP 即可与其它 station 连接。

WIFI_MODE_APSTA

station/AP 共存模式:在此模式下,函数 esp_wifi_start() 将同时初始化 station 和 AP。该步骤在 station 模式和 AP 模式下完成。请注意 ESP station 所连外部 AP 的信道优先于 ESP AP 信道。

Station 基本配置

API esp_wifi_set_config() 可用于配置 station。配置的参数信息会保存到 NVS 中。下表详细介绍了各个字段。

字段

描述

ssid

station 想要连接的目标 AP 的 SSID。

password

目标 AP 的密码。

scan_method

WIFI_FAST_SCAN 模式下,扫描到一个匹配的 AP 时即结束。 WIFI_ALL_CHANNEL_SCAN 模式下,在所有信道扫描所有匹配的 AP。默认扫描模式是 WIFI_FAST_SCAN

bssid_set

如果 bssid_set 为 0,station 连接 SSID 与 “ssid” 字段相同的 AP,同时忽略字段 “bssid”。其他情况下,station 连接 SSID 与 “ssid” 字段相同、BSSID 与 “bssid” 字段也相同的 AP。

bssid

只有当 bssid_set 为 1 时有效。见字段 “bssid_set”。

channel

该字段为 0 时,station 扫描信道 1 ~ N 寻找目标 AP;否则,station 首先扫描值与 “channel” 字段相同的信道,再扫描其他信道。比如,当该字段设置为 3 时,扫描顺序为 3,1,2,...,N 。如果你不知道目标 AP 在哪个信道,请将该字段设置为 0。

sort_method

该字段仅用于 WIFI_ALL_CHANNEL_SCAN 模式。

如果设置为 WIFI_CONNECT_AP_BY_SIGNAL,所有匹配的 AP 将会按照信号强度排序,信号最好的 AP 会被首先连接。比如,如果 station 想要连接 ssid 为 “apxx” 的 AP,且扫描到两个这样的 AP。第一个 AP 的信号为 -90 dBm,第二个 AP 的信号为 -30 dBm,station 首先连接第二个 AP。除非失败,才会连接第一个。

如果设置为 WIFI_CONNECT_AP_BY_SECURITY,所有匹配的 AP 将会按照安全性排序。比如,如果 station 想要连接 ssid 为 “apxx” 的 AP,并且扫描到两个这样的 AP。第一个 AP 为开放式,第二个 AP 为 WPA2 加密,station 首先连接第二个 AP。除非失败,才会连接第一个。

threshold

该字段用来筛选找到的 AP,如果 AP 的 RSSI 或安全模式小于配置的阈值,则不会被连接。

如果 RSSI 设置为 0,则表示默认阈值、默认 RSSI 阈值为 -127 dBm。如果 authmode 阈值设置为 0,则表示默认阈值,默认 authmode 阈值为开放模式。

注意

WEP/WPA 安全模式在 IEEE802.11-2016 协议中已弃用,建议不要使用。可使用 authmode 阈值代替,通过将 threshold.authmode 设置为 WIFI_AUTH_WPA2_PSK 使用 WPA2 模式。

AP 基本配置

API esp_wifi_set_config() 可用于配置 AP。配置的参数信息会保存到 NVS 中。下表详细介绍了各个字段。

Wi-Fi 协议模式

目前,IDF 支持以下协议模式:

Wi-Fi 带宽模式

Wi-Fi 国家/地区代码

主信道

AP 模式下,AP 的信道定义为主信道。station 模式下,station 所连 AP 的信道定义为主信道。station/AP 共存模式下,AP 和 station 的主信道必须相同。如果不同,station 的主信道始终优先。比如,初始时,AP 位于信道 6,但 station 连接信道 9 的 AP。因为 station 的主信道具有优先性,该 AP 需要将信道从 6 切换至 9,确保与 station 主信道相同。切换信道时,AP 模式下的 ESP32-C61 将使用信道切换公告 (CSA) 通知连接的 station。支持信道切换的 station 将直接通过,无需与 AP 断连再重新连接。

Wi-Fi Menuconfig

Wi-Fi 缓冲区配置

如果要修改默认的缓冲区数量或类型,最好也了解缓冲区在数据路径中如何分配或释放。下图显示了发送数据方向的过程。

TX Buffer Allocation

描述:

  • 应用程序分配需要发送的数据。

  • 应用程序调用 TCPIP 或套接字相关的 API 发送用户数据。这些 API 会分配一个在 LwIP 中使用的 PBUF,并复制用户数据。

  • 当 LwIP 调用 Wi-Fi API 发送 PBUF 时,Wi-Fi API 会分配一个“动态发送数据缓冲区”或“静态发送数据缓冲区”,并复制 LwIP PBUF,最后发送数据。

下图展示了如何在接收数据方向分配或释放缓冲区:

接收数据缓冲区分配

描述:

  • Wi-Fi 硬件在空中接收到数据包后,将数据包内容放到“静态接收数据缓冲区”,也就是“接收数据 DMA 缓冲区”。

  • Wi-Fi 驱动程序分配一个“动态接收数据缓冲区”、复制“静态接收数据缓冲区”,并将“静态接收数据缓冲区”返回给硬件。

  • Wi-Fi 驱动程序将数据包传送到上层 (LwIP),并分配一个 PBUF 用于存放“动态接收数据缓冲区”。

  • 应用程序从 LwIP 接收数据。

下表是 Wi-Fi 内部缓冲区的配置情况。

缓冲区类型

分配类型

默认

是否可配置

描述

静态接收数据缓冲区(硬件接收数据缓冲区)

静态

10 * 1600 Bytes

这是一种 DMA 内存,在函数 esp_wifi_init() 中初始化,在函数 esp_wifi_deinit() 中释放。 该缓冲区形成硬件接收列表。当通过空中接收到一个帧时,硬件将该帧写入缓冲区,并向 CPU 发起一个中断。然后,Wi-Fi 驱动程序从缓冲区中读取内容,并将缓冲区返回到列表中。

如果应用程序希望减少 Wi-Fi 静态分配的内存,可以将该值从 10 减少到 6, 从而节省 6400 Bytes 的内存。除非禁用 AMPDU 功能,否则不建议将该值降低到 6 以下。

动态接收数据缓冲区

动态

32

缓冲区的长度可变,取决于所接收帧的长度。当 Wi-Fi 驱动程序 从“硬件接收数据缓冲区”接收到一帧时,需要从堆中分配“动态接收数据缓冲区”。在 Menuconfig 中配置的“动态接收数据缓冲区” 数量用来限制未释放的“动态接收数据缓冲区”总数量。

动态发送数据缓冲区

动态

32

这是一种 DMA 内存,位于堆内存中。当上层 (LwIP) 向 Wi-Fi 驱动程序发送数据包时,该缓冲区首先分配一个“动态发送数据缓 冲区”,并复制上层缓冲区。

动态发送数据缓冲区和静态发送数据缓冲区相互排斥。

静态发送数据缓冲区

静态

16 * 1600 Bytes

这是一种 DMA 内存,在函数 esp_wifi_init() 中初始化,在函数 esp_wifi_deinit() 中释放。 当上层 (LwIP) 向 Wi-Fi 驱动程序发送数据包时,该缓冲区首先 分配一个“静态发送数据缓冲区”,并复制上层缓冲区。

动态发送数据缓冲区和静态发送数据缓冲区相互排斥。

由于发送数据缓冲区必须是 DMA 缓冲区,所以当使能 PSRAM 时,发送数据缓冲区必须是静态的。

管理短缓冲区

动态

8

Wi-Fi 驱动程序的内部缓冲区。

管理长缓冲区

动态

32

Wi-Fi 驱动程序的内部缓冲区。

管理超长缓冲区

动态

32

Wi-Fi 驱动程序的内部缓冲区。

Wi-Fi NVS Flash

如果使能 Wi-Fi NVS flash,所有通过 Wi-Fi API 设置的 Wi-Fi 配置都会被存储到 flash 中,Wi-Fi 驱动程序在下次开机或重启时将自动加载这些配置。但是,应用程序可视情况禁用 Wi-Fi NVS flash,例如:其配置信息不需要存储在非易失性内存中、其配置信息已安全备份,或仅出于某些调试原因等。

故障排除

请见 乐鑫 Wireshark 使用指南


此文档对您有帮助吗?