通过 ESP-IDF 连接 Wi-Fi 入门指南

学习目标

本教程将深入探讨如何通过 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()。

参考资料:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值