学习目标
本教程将深入探讨如何通过 ESP-IDF 框架实现乐鑫芯片与 Wi-Fi 接入点 (AP) 的连接。
完成本教程后,您将能够:
- 了解 Wi-Fi 的相关背景知识;
- 明确启用 Wi-Fi 连接所需的关键组件;
- 学会利用乐鑫芯片配置一个基本的 Wi-Fi 连接;
- 在 ESP-IDF 平台上从零开始实现 Wi-Fi 连接的完整过程。
推荐学习方式
本教程将广泛使用 ESP-IDF 框架,因此提前安装本框架并对其具有一些基本的熟悉程度很重要。
要安装并开始使用 ESP-IDF,请参阅此处的教程。
简介
Wi-Fi 技术是 Wireless Fidelity 的缩写,作为无线局域网络 (WLAN) 的一种实现方式,通过无线电波为设备提供高速互联网接入和网络连接。它使得智能手机、笔记本电脑、平板电脑以及物联网设备等能够无线接入互联网,实现了设备的互联互通。
Wi-Fi 技术极大地促进了信息获取和通信方式的革新,使得用户无需依赖物理电缆即可享受无缝的网络连接体验。作为现代通信技术的核心组成部分,Wi-Fi 持续发展,以更快的速度、更高的可靠性以及增强的安全功能来满足日益增长的数字连接需求。
乐鑫提供了多种支持 Wi-Fi功能 SoC 系列,从支持 Wi-Fi 4 标准的 ESP32-S3 到支持 Wi-Fi 6 标准的 ESP32-C6 ,乐鑫的产品线为各种基于 Wi-Fi 技术的项目提供了丰富的选择。
对于您想深入了解 Wi-Fi 技术的操作机制和配置选项,我们推荐阅读由乐鑫工程师编写的 ESP32-C3 Wireless Adventure 第七章。此外,我们还提供了详尽的 ESP32 Wi-Fi 驱动程序文档,深入阐述了如何利用 Wi-Fi 应用程序接口 (APIs) 来实现高效稳定的无线连接。
所需硬件
- 运行 Windows、Linux 或 macOS 的计算机
- ESP32-C3-DevKitM-1 开发板
- 适配开发板的 USB 线(供电+数据通信)
其他具有 Wi-Fi 功能的硬件包括:
- ESP32-S 系列 SoC
- ESP32-C 系列 SoC
- ESP32 系列 SoC
以下 SoC 系列不支持 Wi-Fi 技术。
- ESP32-P 系列 SoC
- ESP32-H 系列 SoC
如果您需要乐鑫产品支持哪些无线技术的更多相关信息,可以查询 ESP 产品选型工具。
所需软件
本教程将使用以下软件:
- ESP-IDF 版本 v5.2.2
分步指南
在本教程中,我们将通过两种方法在乐鑫芯片上建立 Wi-Fi 连接:
- 使用简化的辅助函数 example_connect()(有关该函数的更多信息,点击此处查看)
- 使用 Wi-Fi API
对以上两种方法进行对比:
- example_connect() 函数旨在简化用户将 Wi-Fi 功能集成到项目中的流程。而无需深入理解 Wi-Fi 协议的底层细节,适合快速基于乐鑫 SoC 进行开发
- 使用 Wi-Fi API 则允许用户对乐鑫芯片上的 Wi-Fi 功能进行高级配置和管理。这包括但不限于调整 beacon 间隔、设置信道切换公告计数以及定义 FTM 响应模式等。这种方式适合开发规模更大、需求更复杂的应用程序
第一部分:学习使用 example_connect() 函数
第 1 步:设置项目
对于 Linux 和 macOS 用户,在使用 idf.py 命令时,请确保您已在相应的终端会话中配置了ESP-IDF环境。您可以通过执行以下命令来实现这一点:
. $HOME/esp/esp-idf/export.sh
有关 ESP-IDF 环境变量设置的更多信息,请参阅此处。建议创建别名来设置 ESP-IDF。
首先,我们需要新建一个 ESP-IDF 项目,将它命名为 simple_connect。为此,我们可以运行:
idf.py create-project simple_connect
此命令将创建一个最小的空白 ESP-IDF 项目,结构如下:
├── CMakeLists.txt # Build configuration declaring entire project
├── main # Contains project source code
│ ├── CMakeLists.txt # File that registers the main component
│ └── simple_connect.c # Contains the main entry point of the program
└── README.md # File to describe the project
以下是创建文件的简要概述:
- 顶层项目 simple_connect/CMakeLists.txt 文件:此文件设置项目范围的 全局 CMake 变量,并将项目与构建系统的其余部分集成。
- main 目录:此目录包含项目的源代码。
- 项目目录 main/CMakeLists.txt 文件:此文件设置变量定义来控制项目的构建过程。
- simple_connect.c:此文件包含程序的主入口点 app_main() 我们将在这里编写源代码。
- 要了解 ESP-IDF 项目的结构,请参阅此处。有关 idf.py 命令行工具中可用命令的列表,请参阅此处。
第 2 步:添加依赖项
首先,转到项目目录的根目录(在该情境下指 simple_connect 文件夹),然后运行idf.py create-manifest。
这将创建一个定义项目依赖关系的清单文件 (main/idf_component.yml)。有关 ESP-IDF 中依赖关系和组件管理的更多信息,请参阅此处。
在 idf_component.yml 添加以下内容的 protocol_examples_common 依赖项:
dependencies:
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
第 3 步:设置目标 SoC
我们需要配置 ESP-IDF,为所使用的特定目标 (SoC) 构建项目。
在项目目录的根目录中,运行 idf.py set-target esp32XX
注意:esp32XX 指的是使用的目标芯片。在该案例中,我们使用的是 ESP32-C3-DevKitM-1,因此我们运行命令idf.py set-target esp32c3。
您的 ESP-IDF 版本中支持的目标的完整列表可以通过运行 idfidf.py --list-targets 查看。
第 4 步:配置 Wi-Fi 认证信息
- 要配置 Wi-Fi 认证信息,请完成以下操作(另请参阅下面的 asciinema 视频):
- 通过运行打开 ESP-IDF 项目配置工具 idf.py menuconfig;
- 转到 Example Connection Configuration,填入想要连接的 ssid 和 password
小贴士:
- 按 Enter 键保存对 SSID 和密码的更改
- 按 S 键保存所有所做的更改
- 按 Q 退出项目配置工具
第 5 步:编辑源代码
现在,让我们编写一个简单的程序,该程序旨在实现与 Wi-Fi 接入点的连接,并在断开连接后进行相应的处理。完整的源代码位于 main/simple_connect.c 文件中,而接下来的部分将提供详细的解释说明。
头文件和宏
我们将包含以下头文件并定义一些宏:
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "protocol_examples_common.h"
#include "esp_wifi.h"
#define TAG "simple_connect_example"
系统初始化
在我们使用任何资源进行 Wi-Fi 连接之前,都需要一些初始化步骤。
// System initialization
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
解析:
- nvs_flash_init():在 flash 中初始化 nvs 分区,允许用户在重新启动时存储(少量)所需的信息。这里列举了一些关于 NVS 的常见问题
- esp_netif_int():初始化网络接口 (netif),这是底层的 TCP/IP 协议栈。
- esp_event_loop_create_default():创建一个默认事件循环,允许组件声明事件,以便其他组件可以注册处理程序。
连接和断开 Wi-Fi
与 Wi-Fi 连接和断开连接的功能很简单:
ESP_ERROR_CHECK(example_connect());
ESP_ERROR_CHECK(example_disconnect());
打印接入点信息
与 Wi-Fi 建立连接后,我们可以打印出一些具有以下功能的 AP 信息。
// Print out Access Point Information
wifi_ap_record_t ap_info;
ESP_ERROR_CHECK(esp_wifi_sta_get_ap_info(&ap_info));
ESP_LOGI(TAG, "--- Access Point Information ---");
ESP_LOG_BUFFER_HEX("MAC Address", ap_info.bssid, sizeof(ap_info.bssid));
ESP_LOG_BUFFER_CHAR("SSID", ap_info.ssid, sizeof(ap_info.ssid));
ESP_LOGI(TAG, "Primary Channel: %d", ap_info.primary);
ESP_LOGI(TAG, "RSSI: %d", ap_info.rssi);
关于 wifi_ap_record_t 和 esp_wifi_sta_get_ap_info() 的文档可以在这里找到。您也可以浏览 ESP 日志库获取更多相关信息。
第 6 步:运行应用程序
该案例的完整源代码可以在这里找到。
在 simplesimple_connect.c 中编写完源代码后,您就可以编译固件并将其烧录到乐鑫芯片上。
该步骤可以通过以下命令来完成:
- 编译固件:idf.py build
- 将生成的固件烧录到乐鑫芯片上:idf.py flash
- 从串口观察日志打印:idf.py monitor
提供的源代码将在终端上输出以下内容:
example_connect() 函数为在乐鑫芯片上建立 Wi-Fi 连接提供了一个良好的入门。
在接下来的章节中,我们将探讨如何编写更稳定、配置项更多且能够处理各种错误情况的完整 Wi-Fi 代码。
第二部分:使用 Wi-Fi API
example_connect() 函数为我们提供了一种简单的方式来建立 Wi-Fi 连接。然而,对于开发实际应用或更复杂的项目,编写更强大的 Wi-Fi 代码是值得投入时间和精力的。
在本节中,我们将详细介绍 Wi-Fi 驱动程序和 API 的各个组件,并从头开始构建 Wi-Fi 代码。
有关其他示例,请随时参考以下来源:
第 1 步:设置项目
下面创建另一个新的 ESP-IDF 项目,并称命名为 wifi_tutorial。您可以运行:
idf.py create-project wifi_tutorial
第 2 步:将文件添加到项目中
在这个例子中,我们将演示如何在 ESP-IDF 项目中添加更多标头和源代码文件。
在 main 文件夹中,创建一个名为 tutorialtutorial.h 的标头文件和另一个名为 tutorial.c 的文件。该项目的结构将如下:
├── CMakeLists.txt
├── main
│ ├── CMakeLists.txt
│ └── wifi_tutorial.c # Source file that contains the entry point
│ └── tutorial.c # Create this source file
│ └── tutorial.h # Create this header file
└── README.md
下面,我们需要编辑 main/CMakeLists.txt,以便添加的文件将包含在构建过程中。有关 ESP-IDF 构建系统的更多信息,您可以参考本文档。
包括以下内容 main/CMakeLists.txt
idf_component_register(SRCS "tutorial.c" "wifi_tutorial.c"
INCLUDE_DIRS ".")
idf_component_register(SRCS "tutorial.c" "wifi_tutorial.c" INCLUDE_DIRS ".")
在 main/tutorial.h 文件中,我们将首先声明以下函数:
#pragma once
#include "esp_err.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
esp_err_t tutorial_init(void);
esp_err_t tutorial_connect(char* wifi_ssid, char* wifi_password);
esp_err_t tutorial_disconnect(void);
esp_err_t tutorial_deinit(void);
在下面的章节中,我们将详细介绍在乐鑫芯片中正确设置、连接和终止 Wi-Fi 连接的步骤。
第 3 步:Wi-Fi 初始化
我们将首先在 tutorial.c 中定义初始化步骤所需的各种标头、宏和静态变量。
在 tutorial.c 中,我们首先定义以下内容:
// tutorial.c
#include "tutorial.h"
#include <inttypes.h>
#include <string.h>
#include "freertos/event_groups.h"
#define TAG "tutorial"
#define WIFI_AUTHMODE WIFI_AUTH_WPA2_PSK
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static const int WIFI_RETRY_ATTEMPT = 3;
static int wifi_retry_count = 0;
static esp_netif_t *tutorial_netif = NULL;
static esp_event_handler_instance_t ip_event_handler;
static esp_event_handler_instance_t wifi_event_handler;
static EventGroupHandle_t s_wifi_event_group = NULL;
在 tutorial_init() 我们将初始化设置 Wi-Fi 所需的硬件和接口,如下所示:
esp_err_t tutorial_init(void)
{
// Initialize Non-Volatile Storage (NVS)
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
s_wifi_event_group = xEventGroupCreate();
ret = esp_netif_init();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize TCP/IP network stack");
return ret;
}
ret = esp_event_loop_create_default();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to create default event loop");
return ret;
}
ret = esp_wifi_set_default_wifi_sta_handlers();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set default handlers");
return ret;
}
tutorial_netif = esp_netif_create_default_wifi_sta();
if (tutorial_netif == NULL) {
ESP_LOGE(TAG, "Failed to create default WiFi STA interface");
return ESP_FAIL;
}
// Wi-Fi stack configuration parameters
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_cb,
NULL,
&wifi_event_handler));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
ESP_EVENT_ANY_ID,
&ip_event_cb,
NULL,
&ip_event_handler));
return ret;
}
值得注意的是,初始化步骤与之前使用 example_connect() 的示例相似。此外,还有其他类型的初始化步骤,例如定义事件循环、注册事件处理程序和创建网络接口。有关这些附加步骤的文档可以在这里找到:
在本教程中,我们还使用事件位来指示发生了什么 Wi-Fi 事件。有关事件位和事件组的详细信息可以在这里找到。
第 4 步:Wi-Fi 配置和连接
要建立 Wi-Fi 连接,我们可以使用以下方法配置连接类型、安全级别和硬件模式:
esp_err_t tutorial_connect(char* wifi_ssid, char* wifi_password)
{
wifi_config_t wifi_config = {
.sta = {
// this sets the weakest authmode accepted in fast scan mode (default)
.threshold.authmode = WIFI_AUTHMODE,
},
};
strncpy((char*)wifi_config.sta.ssid, wifi_ssid, sizeof(wifi_config.sta.ssid));
strncpy((char*)wifi_config.sta.password, wifi_password, sizeof(wifi_config.sta.password));
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); // default is WIFI_PS_MIN_MODEM
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); // default is WIFI_STORAGE_FLASH
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_LOGI(TAG, "Connecting to Wi-Fi network: %s", wifi_config.sta.ssid);
ESP_ERROR_CHECK(esp_wifi_start());
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE, pdFALSE, portMAX_DELAY);
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "Connected to Wi-Fi network: %s", wifi_config.sta.ssid);
return ESP_OK;
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGE(TAG, "Failed to connect to Wi-Fi network: %s", wifi_config.sta.ssid);
return ESP_FAIL;
}
ESP_LOGE(TAG, "Unexpected Wi-Fi error");
return ESP_FAIL;
}
我们可以通过 wifi_config_t 配置 Wi-Fi 连接。要在乐鑫芯片上设置 Wi-Fi station,并配置 wifi_sta_config_t 中的字段。以下是一些常用配置的字段:
- wifi_config.ssid:目标 AP 的 SSID
- wifi_config.password:目标 AP 的密码
- wifi_config.scan_method:扫描AP方式
- wifi_config.threshold.authmode:接受 wifi 连接的最弱安全认证方式
有关 STA 配置的文档可以在这里找到。authmodes 的相对强度和 Wi-Fi 配置中每个字段的配置选项可以在 esp_wifi_types_generic.h 中找到
我们还可以配置分配给管理 Wi-Fi的硬件资源。例如,在本教程中,我们使用 esp_wifi_set_ps() 禁止使用任何省电模式,以最大限度地接收和传输Wi-Fi数据包。然而,有些场景要求我们设置特定的电源安全类型,例如那些需要省电模式和射频共存的模式。
请注意,上述代码中的 xEventGroupWaitBits() 是一个阻止过程,直到事件处理程序设置其中一个位。文件可以在这里找到。
熟悉 FreeRTOS 概念将有助于了解程序的行为,请参阅此处的文档。对于高级使用案例,可以将 Wi-Fi 例程作为RTOS 任务运行,以避免在等待 Wi-Fi 连接建立时阻止应用程序的其余部分。
Wi-Fi 和 IP 事件的事件处理程序
在本例中,我们创建了简单的事件处理程序,以记录与 Wi-Fi(在 wifi_event_t 下定义)或 IP 层(在 ip_event_t 下定义)相关的事件。
建立 Wi-Fi 时,需要注意的重要事件是:
- WIFI_EVENT_STA_START:STA 的配置已完成
- WIFI_EVENT_STA_CONNECTED:STA 与AP建立了连接
- WIFI_EVENT_STA_DISCONNECTED:STA 连接超时或与 AP 失去连接
- IP_EVENT_STA_GOT_IP:乐鑫芯片收到了 AP 分配的 IP 地址,Wi-Fi 连接已完成。
第 5 步:关闭 Wi-Fi 和清理
要断开与 Wi-Fi 的连接,只需按照以下顺序调用 Wi-Fi APIs:
1. esp_wifi_disconnect()
2. esp_wifi_stop()
3. esp_wifi_deinit()
我们需要清除驱动程序、网络接口,并取消注册事件处理程序。
因此,tutorial_disconnect() 和 tutorial_deinit() 实现过程如下:
esp_err_t tutorial_disconnect(void)
{
if (s_wifi_event_group) {
vEventGroupDelete(s_wifi_event_group);
}
return esp_wifi_disconnect();
}
esp_err_t tutorial_deinit(void)
{
esp_err_t ret = esp_wifi_stop();
if (ret == ESP_ERR_WIFI_NOT_INIT) {
ESP_LOGE(TAG, "Wi-Fi stack not initialized");
return ret;
}
ESP_ERROR_CHECK(esp_wifi_deinit());
ESP_ERROR_CHECK(esp_wifi_clear_default_wifi_driver_and_handlers(tutorial_netif));
esp_netif_destroy(tutorial_netif);
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, ESP_EVENT_ANY_ID, ip_event_handler));
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler));
return ESP_OK;
}
第 6 步:整合所有步骤
现在,我们已经根据所需的配置完成了 Wi-Fi 连接的实施。为了演示 Wi-Fi 功能,我们将进行以下操作:
1. 初始化
2. 建立 Wi-Fi 连接
3. 建立连接后,打印出有关AP的信息
4. 终止 Wi-Fi 连接
5. 释放用于 Wi-Fi 连接的资源
我们鼓励您在不参考答案的情况下尝试编码 wifi_tutorial.c!完整的代码片段可以在下一节中找到。
以下是教程的演示:
代码参考
第一部分:使用 example_connect() 函数
// simple_connect.c
#include <stdio.h>
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "protocol_examples_common.h"
#include "esp_wifi.h"
#define TAG "simple_connect_example"
void app_main(void)
{
ESP_LOGI(TAG, "Hello from ESP32!");
// System initialization
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
// Establish Wi-Fi connection
ESP_ERROR_CHECK(example_connect());
// Print out Access Point Information
wifi_ap_record_t ap_info;
ESP_ERROR_CHECK(esp_wifi_sta_get_ap_info(&ap_info));
ESP_LOGI(TAG, "--- Access Point Information ---");
ESP_LOG_BUFFER_HEX("MAC Address", ap_info.bssid, sizeof(ap_info.bssid));
ESP_LOG_BUFFER_CHAR("SSID", ap_info.ssid, sizeof(ap_info.ssid));
ESP_LOGI(TAG, "Primary Channel: %d", ap_info.primary);
ESP_LOGI(TAG, "RSSI: %d", ap_info.rssi);
// Disconnect from Wi-Fi
ESP_ERROR_CHECK(example_disconnect());
}
第二部分:使用 Wi-Fi API
// tutorial.h
#pragma once
#include "esp_err.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
esp_err_t tutorial_init(void);
esp_err_t tutorial_connect(char* wifi_ssid, char* wifi_password);
esp_err_t tutorial_disconnect(void);
esp_err_t tutorial_deinit(void);
// tutorial.c
#include "tutorial.h"
#include <inttypes.h>
#include <string.h>
#include "freertos/event_groups.h"
#define TAG "tutorial"
#define WIFI_AUTHMODE WIFI_AUTH_WPA2_PSK
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static const int WIFI_RETRY_ATTEMPT = 3;
static int wifi_retry_count = 0;
static esp_netif_t *tutorial_netif = NULL;
static esp_event_handler_instance_t ip_event_handler;
static esp_event_handler_instance_t wifi_event_handler;
static EventGroupHandle_t s_wifi_event_group = NULL;
static void ip_event_cb(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "Handling IP event, event code 0x%" PRIx32, event_id);
switch (event_id)
{
case (IP_EVENT_STA_GOT_IP):
ip_event_got_ip_t *event_ip = (ip_event_got_ip_t *)event_data;
ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event_ip->ip_info.ip));
wifi_retry_count = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
break;
case (IP_EVENT_STA_LOST_IP):
ESP_LOGI(TAG, "Lost IP");
break;
case (IP_EVENT_GOT_IP6):
ip_event_got_ip6_t *event_ip6 = (ip_event_got_ip6_t *)event_data;
ESP_LOGI(TAG, "Got IPv6: " IPV6STR, IPV62STR(event_ip6->ip6_info.ip));
wifi_retry_count = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
break;
default:
ESP_LOGI(TAG, "IP event not handled");
break;
}
}
static void wifi_event_cb(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "Handling Wi-Fi event, event code 0x%" PRIx32, event_id);
switch (event_id)
{
case (WIFI_EVENT_WIFI_READY):
ESP_LOGI(TAG, "Wi-Fi ready");
break;
case (WIFI_EVENT_SCAN_DONE):
ESP_LOGI(TAG, "Wi-Fi scan done");
break;
case (WIFI_EVENT_STA_START):
ESP_LOGI(TAG, "Wi-Fi started, connecting to AP...");
esp_wifi_connect();
break;
case (WIFI_EVENT_STA_STOP):
ESP_LOGI(TAG, "Wi-Fi stopped");
break;
case (WIFI_EVENT_STA_CONNECTED):
ESP_LOGI(TAG, "Wi-Fi connected");
break;
case (WIFI_EVENT_STA_DISCONNECTED):
ESP_LOGI(TAG, "Wi-Fi disconnected");
if (wifi_retry_count < WIFI_RETRY_ATTEMPT) {
ESP_LOGI(TAG, "Retrying to connect to Wi-Fi network...");
esp_wifi_connect();
wifi_retry_count++;
} else {
ESP_LOGI(TAG, "Failed to connect to Wi-Fi network");
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
break;
case (WIFI_EVENT_STA_AUTHMODE_CHANGE):
ESP_LOGI(TAG, "Wi-Fi authmode changed");
break;
default:
ESP_LOGI(TAG, "Wi-Fi event not handled");
break;
}
}
esp_err_t tutorial_init(void)
{
// Initialize Non-Volatile Storage (NVS)
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
s_wifi_event_group = xEventGroupCreate();
ret = esp_netif_init();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize TCP/IP network stack");
return ret;
}
ret = esp_event_loop_create_default();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to create default event loop");
return ret;
}
ret = esp_wifi_set_default_wifi_sta_handlers();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set default handlers");
return ret;
}
tutorial_netif = esp_netif_create_default_wifi_sta();
if (tutorial_netif == NULL) {
ESP_LOGE(TAG, "Failed to create default WiFi STA interface");
return ESP_FAIL;
}
// Wi-Fi stack configuration parameters
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_cb,
NULL,
&wifi_event_handler));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
ESP_EVENT_ANY_ID,
&ip_event_cb,
NULL,
&ip_event_handler));
return ret;
}
esp_err_t tutorial_connect(char* wifi_ssid, char* wifi_password)
{
wifi_config_t wifi_config = {
.sta = {
// this sets the weakest authmode accepted in fast scan mode (default)
.threshold.authmode = WIFI_AUTHMODE,
},
};
strncpy((char*)wifi_config.sta.ssid, wifi_ssid, sizeof(wifi_config.sta.ssid));
strncpy((char*)wifi_config.sta.password, wifi_password, sizeof(wifi_config.sta.password));
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); // default is WIFI_PS_MIN_MODEM
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); // default is WIFI_STORAGE_FLASH
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_LOGI(TAG, "Connecting to Wi-Fi network: %s", wifi_config.sta.ssid);
ESP_ERROR_CHECK(esp_wifi_start());
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE, pdFALSE, portMAX_DELAY);
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "Connected to Wi-Fi network: %s", wifi_config.sta.ssid);
return ESP_OK;
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGE(TAG, "Failed to connect to Wi-Fi network: %s", wifi_config.sta.ssid);
return ESP_FAIL;
}
ESP_LOGE(TAG, "Unexpected Wi-Fi error");
return ESP_FAIL;
}
esp_err_t tutorial_disconnect(void)
{
if (s_wifi_event_group) {
vEventGroupDelete(s_wifi_event_group);
}
return esp_wifi_disconnect();
}
esp_err_t tutorial_deinit(void)
{
esp_err_t ret = esp_wifi_stop();
if (ret == ESP_ERR_WIFI_NOT_INIT) {
ESP_LOGE(TAG, "Wi-Fi stack not initialized");
return ret;
}
ESP_ERROR_CHECK(esp_wifi_deinit());
ESP_ERROR_CHECK(esp_wifi_clear_default_wifi_driver_and_handlers(tutorial_netif));
esp_netif_destroy(tutorial_netif);
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, ESP_EVENT_ANY_ID, ip_event_handler));
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler));
return ESP_OK;
}
// wifi_tutorial.c
#include <stdio.h>
#include "esp_log.h"
#include "esp_wifi.h"
#include "tutorial.h"
#include "freertos/task.h"
#define TAG "main"
// Enter the Wi-Fi credentials here
#define WIFI_SSID "SSID"
#define WIFI_PASSWORD "PASSWORD"
void app_main(void)
{
ESP_LOGI(TAG, "Starting tutorial...");
ESP_ERROR_CHECK(tutorial_init());
esp_err_t ret = tutorial_connect(WIFI_SSID, WIFI_PASSWORD);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to connect to Wi-Fi network");
}
wifi_ap_record_t ap_info;
ret = esp_wifi_sta_get_ap_info(&ap_info);
if (ret == ESP_ERR_WIFI_CONN) {
ESP_LOGE(TAG, "Wi-Fi station interface not initialized");
}
else if (ret == ESP_ERR_WIFI_NOT_CONNECT) {
ESP_LOGE(TAG, "Wi-Fi station is not connected");
} else {
ESP_LOGI(TAG, "--- Access Point Information ---");
ESP_LOG_BUFFER_HEX("MAC Address", ap_info.bssid, sizeof(ap_info.bssid));
ESP_LOG_BUFFER_CHAR("SSID", ap_info.ssid, sizeof(ap_info.ssid));
ESP_LOGI(TAG, "Primary Channel: %d", ap_info.primary);
ESP_LOGI(TAG, "RSSI: %d", ap_info.rssi);
ESP_LOGI(TAG, "Disconnecting in 5 seconds...");
vTaskDelay(pdMS_TO_TICKS(5000));
}
ESP_ERROR_CHECK(tutorial_disconnect());
ESP_ERROR_CHECK(tutorial_deinit());
ESP_LOGI(TAG, "End of tutorial...");
}
结论
本教程详细介绍了利用乐鑫芯片与接入点建立基本 Wi-Fi 连接的多种方法,深入探讨了在乐鑫芯片上初始化 Wi-Fi 功能所需资源的复杂性,并详尽阐述了进行高级配置的步骤序列。为了更全面地掌握乐鑫芯片的Wi-Fi功能,建议您尝试不同的 Wi-Fi 配置方案,例如 WPA3 或 DPP,以获得必要的工具和知识。
乐鑫芯片的潜力远超基本的 Wi-Fi 连接。我们希望您继续探索其他网络协议的示例,如蓝牙或 Zigbee,或者在 ESP-IDF 框架上进一步构建协议示例,以充分利用乐鑫芯片的强大功能。
错误和故障规避
以下是一些常见的错误:
错误 1:缺少简单连接示例的标头文件
/home/haoyi/esp/simple_connect/main/main.c:3:10: fatal error: protocol_examples_common.h: No such file or directory
3 | #include "protocol_examples_common.h"
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
ninja: build stopped: subcommand failed.
- 确保 idf_component.yml 中的依赖路径是正确的。
- 然后运行 idf.py fullclean,并通过运行再次构建 idf.py build
错误 2:NVS 未初始化
ESP_ERROR_CHECK failed: esp_err_t 0x1101 (ESP_ERR_NVS_NOT_INITIALIZED) at 0x42008226
file: "/IDF/examples/common_components/protocol_examples_common/wifi_connect.c" line 138
func: example_wifi_start
expression: esp_wifi_init(&cfg)
abort() was called at PC 0x40385c3b on core 0
在调用 example_connect() 之前先调用 nvs_flash_init()example_connect()
错误 3:由于状态无效,无法初始化 Wi-Fi
I (487) phy_init: phy_version 1110,9c20f0a,Jul 27 2023,10:42:54
I (527) wifi:mode : sta (48:27:e2:b5:5c:64)
I (527) wifi:enable tsf
E (527) wifi:failed to post WiFi event=2 ret=259
ESP_ERROR_CHECK failed: esp_err_t 0x103 (ESP_ERR_INVALID_STATE) at 0x420089ec
file: "/IDF/examples/common_components/protocol_examples_common/wifi_connect.c" line 183
func: example_wifi_sta_do_connect
expression: esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &example_handler_on_wifi_disconnect, NULL)
abort() was called at PC 0x40386345 on core 0
在调用 example_connect() 之前首先调用 esp_netif_init() 和 esp_event_loop_create_default()。