当前位置: 首页 > article >正文

ESP32的IDF开发学习-WiFi的开启、配置与连接

前言

本章节将实现如何使用ESP32的WiFi功能,尽可能的详细地介绍

简介

ESP32中的wifi支持双工作模式

  • Station(STA)模式:连接到路由器或其他AP设备,可通过esp_wifi_set_mode(WIFI_MODE_STA)设置。
  • SoftAP模式:创建本地热点,允许其他设备直连,最大支持 10台设备 同时接入(默认配置)。
  • 混合模式STA+AP 模式并行运行(WIFI_MODE_APSTA),例如设备既能连接云端服务器,又能作为配置热点供手机调试。
    同时,ESP32也做了低功耗优化和射频性能提升
  • 低功耗优化
    ESP32-S3引入了 Light-sleep/Modem-sleep 精细功耗管理:
    • Modem-sleep:WiFi空闲时保持TCP/IP连接,仅射频模块休眠,电流低至 1 mA 左右,适合持续在线但数据发送不频繁的场景(如传感器定时上报)。
  • 射频性能提升
    • 外置天线支持:通过IPEX接口连接高增益天线,解决复杂环境(如金属外壳设备)信号衰减问题。
    • 抗干扰增强:优化了2.4GHz频段的共存算法,降低蓝牙/WiFi同时运行时的通信冲突风险。
      其搭载的搭载 512 KB SRAM320 KB ROM,双核Xtensa LX7处理器(主频240 MHz),在处理高并发TCP连接(如同时维护多个WebSocket)时减少丢包率。

初始化步骤

初始化流程本质上是为TCP/IP协议栈、事件管理、驱动层、应用层之间的协作搭建桥梁。以下是官方推荐的核心步骤及其作用解析:

  1. s1.1:启动LwIP核心任务
    esp_netif_init();  // 初始化LwIP协议栈,建立网络数据传输的「高速公路」
    
    • 关键作用:初始化TCP/IP协议栈(LwIP),为后续WiFi通信提供IP层支持。
    • 注意点:LwIP任务的优先级需高于应用任务,避免网络数据堆积。
  2. s1.2:创建系统事件中枢
    esp_event_loop_create(); // 初始化事件循环——WiFi连接的「神经系统」
    
    • 事件分工示例
      • WiFi层事件(如连接成功):WIFI_EVENT
      • IP层事件(如获取IP地址):IP_EVENT
    • 回调传递范式:建议在事件回调中将核心状态(如IP_EVENT_STA_GOT_IP)通过队列发送给应用任务,实现分层解耦。
  3. s1.3:绑定网络接口实例
    • STA模式绑定
      esp_netif_create_default_wifi_sta();  // 为Station模式挂载「虚拟网卡」
      
    • AP模式绑定
      esp_netif_create_default_wifi_ap();  // 为AP模式分配IP地址池(默认192.168.4.1)
      
    • 背后的网络架构:此步骤会在LwIP中创建netif结构体,管理IP路由、DNS等核心参数。
  4. s1.4:初始化WiFi驱动引擎
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&cfg);  // 启动WiFi驱动的「引擎舱」
    
    • 隐藏的并发控制:该函数内部通过RTOS任务管理射频信号调度,开发者无需直接操作802.11帧。
  5. s1.5:启动应用任务
    xTaskCreate(app_main_task, "App_Task", 4096, NULL, 5, NULL);
    
    • 优先级设定技巧:应用任务优先级应低于事件任务(如默认优先级为5时,事件任务可设为6),避免事件处理被阻塞。

代码编写

头文件

#define WIFI_MODE  0 // 0:STA,链接热点, 1:AP,允许别人链接
#define SSID "LEE"
#define PASSWORD "wcyz1927"
#define AP_SSID "XBOT"
#define AP_PASSWORD "XBOT20241240"
#define MAX_AP_SCAN_NUM 10 //扫描WiFi数
#define WIFI_AUTO_RECONNECT_MAX_RETRIES 5
#define WIFI_AUTO_RECONNECT_BASE_DELAY_MS 10000
  
void configure_wifi(void);
void wifi_connect(void);
void wifi_scan(void);

配置wifi

我们需要按照步骤来配置WiFi的相关设置

/**
 * 配置WiFi功能
 *
 * 本函数负责初始化WiFi模块,根据编译选项配置为AP或STA模式,并注册相应的事件处理器
 * 它还负责启动WiFi模块,使设备能够连接到WiFi网络
 */
void configure_wifi(void)
{
    // 初始化底层驱动和TCP/IP协议栈
    ESP_ERROR_CHECK(esp_netif_init());
    // 初始化事件循环
    esp_event_loop_create_default();
    // 根据编译选项选择WiFi模式
    #if WIFI_MODE
    esp_netif_create_default_wifi_ap();
    #else
    esp_netif_create_default_wifi_sta();
    #endif
  
    // 初始化WiFi配置
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&cfg);
    ESP_LOGI(TAG, "wifi_init_success");
  
    // 新增事件循环注册
    esp_event_handler_instance_t wifi_event_handle, ip_event_handle;
    // 注册WiFi事件处理器
    esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID,&wifi_event_handler_STA, NULL, &wifi_event_handle);
    esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID,&wifi_event_handler_AP, NULL, NULL);
    // 注册IP事件处理器(STA获取IP时触发)
    esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP,&wifi_event_handler_STA, NULL, &ip_event_handle);
    ESP_LOGI(TAG, "wifi_init_success");
  
    // 根据编译选项配置WiFi为AP或STA模式
    #if WIFI_MODE
        wifi_config_t ap_config = {
            .ap = {
                .ssid = AP_SSID,
                .password = AP_PASSWORD,
                .max_connection = 5,
                .authmode = WIFI_AUTH_WPA2_PSK
            },
        };
        esp_wifi_set_mode(WIFI_MODE_AP);
        esp_wifi_set_config(WIFI_IF_AP, &ap_config);
        ESP_LOGI(TAG, "wifi_AP_config");
    #else
        wifi_config_t wifi_config = {
            .sta = {
                .ssid = SSID,
                .password = PASSWORD
            },
        };
        esp_wifi_set_mode(WIFI_MODE_STA);
        esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
        ESP_LOGI(TAG, "wifi_STA_config");
    #endif
  
    // 启动WiFi模块
    esp_wifi_start();
}

这里有两个注册函数,主要用于记录当前wifi的状态

/**
 * WIFI事件处理函数(AP模式)
 * 处理AP模式下的WIFI事件
 * @param arg 事件处理函数的上下文
 * @param event_base 事件基类
 * @param event_id 事件ID
 * @param event_data 事件数据
 */
static void wifi_event_handler_AP(void *arg, esp_event_base_t event_base,int32_t event_id, void *event_data) {
    // 处理AP模式下的客户端连接事件
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STACONNECTED) {
        // 获取客户端连接事件数据
        wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t*) event_data;
        // 记录设备连接日志
        ESP_LOGI(TAG, "设备已连接! MAC: %02X:%02X:%02X:%02X:%02X:%02X",
                 event->mac[0], event->mac[1], event->mac[2],
                 event->mac[3], event->mac[4], event->mac[5]);
    }
    // 处理AP模式下的客户端断开事件
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STADISCONNECTED) {
        // 获取客户端断开事件数据
        wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t*) event_data;
        // 记录设备断开日志
        ESP_LOGI(TAG, "设备已断开. MAC: %02X:%02X:%02X:%02X:%02X:%02X",
                 event->mac[0], event->mac[1], event->mac[2],
                 event->mac[3], event->mac[4], event->mac[5]);
    }
}
/**
 * WIFI事件处理函数(STA模式)
 * 处理STA模式下的WIFI事件
 * @param arg 事件处理函数的上下文
 * @param event_base 事件基类
 * @param event_id 事件ID
 * @param event_data 事件数据
 */
static void wifi_event_handler_STA(void *arg, esp_event_base_t event_base,int32_t event_id, void *event_data) {
    // 处理WIFI事件
    if (event_base == WIFI_EVENT) {
        switch (event_id) {
            case WIFI_EVENT_STA_START:
                // 记录WIFI驱动就绪日志
                ESP_LOGI(TAG, "WiFi驱动就绪");
                break;
            case WIFI_EVENT_STA_CONNECTED:
                // 记录成功连接到热点日志
                ESP_LOGI(TAG, "已连接到热点");
                break;
            case WIFI_EVENT_STA_DISCONNECTED:
                // 记录热点断开日志并启用自动重连
                ESP_LOGW(TAG, "热点断开, 尝试重连...");
                WIFI_AUTO_RECONNECT_ENABLED = 1;
                break;
        }
    // 处理IP事件
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        // 获取IP事件数据
        ip_event_got_ip_t *event = (ip_event_got_ip_t*) event_data;
        // 记录成功获取IP日志
        ESP_LOGI(TAG, "成功获取IP:" IPSTR, IP2STR(&event->ip_info.ip));
    }
}

wifi扫描

注意,扫描和连接不能在一起使用

/**
 * 打印认证模式
 * @param authmode WIFI认证模式
 */
static void print_auth_mode(wifi_auth_mode_t authmode) {
    // 根据认证模式打印相应信息
    switch (authmode) {
        case WIFI_AUTH_OPEN: ESP_LOGI("SCAN", "Auth Mode \tOpen"); break;
        case WIFI_AUTH_WEP: ESP_LOGI("SCAN", "Auth Mode \tWEP"); break;
        case WIFI_AUTH_WPA_PSK: ESP_LOGI("SCAN", "Auth Mode \tWPA-PSK"); break;
        case WIFI_AUTH_WPA2_PSK: ESP_LOGI("SCAN", "Auth Mode \tWPA2-PSK"); break;
        case WIFI_AUTH_WPA3_PSK: ESP_LOGI("SCAN", "Auth Mode \tWPA3-PSK"); break;
        default: ESP_LOGI("SCAN", "Auth Mode \tUnknown"); break;
    }
}
  

/**
 * 打印加密类型
 * @param pairwise Pairwise加密类型
 * @param group Group加密类型
 */
static void print_cipher_type(wifi_cipher_type_t pairwise, wifi_cipher_type_t group) {
    // 打印Pairwise加密类型
    ESP_LOGI("SCAN", "Pairwise Cipher \t%s",
             (pairwise == WIFI_CIPHER_TYPE_NONE) ? "None" :
             (pairwise == WIFI_CIPHER_TYPE_WEP40) ? "WEP40" :
             (pairwise == WIFI_CIPHER_TYPE_WEP104) ? "WEP104" :
             (pairwise == WIFI_CIPHER_TYPE_TKIP) ? "TKIP" :
             (pairwise == WIFI_CIPHER_TYPE_CCMP) ? "CCMP" : "Unknown");
    // 打印Group加密类型
    ESP_LOGI("SCAN", "Group Cipher \t\t%s",
             (group == WIFI_CIPHER_TYPE_NONE) ? "None" :
             (group == WIFI_CIPHER_TYPE_WEP40) ? "WEP40" :
             (group == WIFI_CIPHER_TYPE_WEP104) ? "WEP104" :
             (group == WIFI_CIPHER_TYPE_TKIP) ? "TKIP" :
             (group == WIFI_CIPHER_TYPE_CCMP) ? "CCMP" : "Unknown");
}

/**
 * 扫描可用的WiFi接入点(AP)
 * 此函数启动WiFi扫描,获取并打印周围可用的WiFi热点信息
 * 它首先检查WIFI_MODE宏是否未定义,如果未定义则开始扫描
 * 扫描完成后,根据发现的AP数量动态分配内存,并打印每个AP的详细信息
 */
void wifi_scan(void)
{
    #if !WIFI_MODE
        ESP_ERROR_CHECK(esp_wifi_scan_start(NULL, true));
  
        // 获取AP数量
        uint16_t ap_count = 0;
        ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
  
        // 实际扫描到的AP数量可能超过预分配内存
        uint16_t max_ap = (ap_count > MAX_AP_SCAN_NUM) ? MAX_AP_SCAN_NUM : ap_count;
        wifi_ap_record_t *ap_records = malloc(sizeof(wifi_ap_record_t) * max_ap);
        if (!ap_records) {
            ESP_LOGE("SCAN", "内存分配失败");
            return;
        }
  
        // 获取AP列表
        uint16_t ap_num = max_ap;
        ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_num, ap_records));
  
        // 打印结果
        ESP_LOGI("SCAN", "发现 %d 个WiFi热点:", ap_num);
        for (int i = 0; i < ap_num; i++) {
            ESP_LOGI("SCAN", "--- AP %d ---", i + 1);
            ESP_LOGI("SCAN", "SSID: \t\t%s", ap_records[i].ssid);
            ESP_LOGI("SCAN", "RSSI: \t\t%d dBm", ap_records[i].rssi);
            print_auth_mode(ap_records[i].authmode);
            if (ap_records[i].authmode != WIFI_AUTH_WEP) {
                print_cipher_type(ap_records[i].pairwise_cipher, ap_records[i].group_cipher);
            }
            ESP_LOGI("SCAN", "Channel: \t%d", ap_records[i].primary);
        }
  
        free(ap_records); // 释放内存
    #else
        ESP_LOGI("SCAN", "WiFi AP scan not supported");
        return;
    #endif
}

wifi链接与重连机制

/**
 * 连接WiFi网络
 * 此函数根据WIFI_MODE宏的定义来决定是否执行连接操作
 * 在实际连接之前,会检查WIFI_MODE宏是否未定义
 * 如果未定义,则打印连接信息并尝试连接WiFi
 */
void wifi_connect(void)
{
    #if !WIFI_MODE
        ESP_LOGI(TAG, "wifi_connect");
        esp_wifi_connect();
        wifi_auto_reconnect();
    #endif
}
/**
 * 启用WIFI自动重连功能
 */
void wifi_auto_reconnect(void) {
    // 在非WIFI模式下注册WIFI事件处理器
    #if !WIFI_MODE
    esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL);
    esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL);    
    // 记录自动重连启用日志
    ESP_LOGI("WIFI", "自动重连已启用, 最大重试次数:%d", WIFI_AUTO_RECONNECT_MAX_RETRIES);
    #endif
}
// 定义当前重连尝试次数
static uint8_t WIFI_AUTO_RECONNECT_RETRIES_NOW=0;
// 定义是否启用自动重连
static uint8_t WIFI_AUTO_RECONNECT_ENABLED = 0;
  
/**
 * WIFI事件处理函数
 * 处理WIFI断连和重连逻辑
 * @param arg 事件处理函数的上下文
 * @param event_base 事件基类
 * @param event_id 事件ID
 * @param event_data 事件数据
 */
static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
    // 处理STA模式下的断连事件
    if (event_id == WIFI_EVENT_STA_DISCONNECTED) {
        // 检查是否启用自动重连且重连次数未超过最大值
        if (WIFI_AUTO_RECONNECT_ENABLED && WIFI_AUTO_RECONNECT_RETRIES_NOW < WIFI_AUTO_RECONNECT_MAX_RETRIES) {
            // 增加重连尝试次数
            WIFI_AUTO_RECONNECT_RETRIES_NOW++;
            // 记录重连尝试日志
            ESP_LOGI("WIFI", "尝试重连第%d次", WIFI_AUTO_RECONNECT_RETRIES_NOW);
            // 立即触发重连
            esp_wifi_connect();
        } else {
            // 记录重连失败日志
            ESP_LOGI("WIFI", "重连失败,已达最大次数:%d", WIFI_AUTO_RECONNECT_MAX_RETRIES);
        }
    // 处理STA模式下成功获取IP事件
    } else if (event_id == IP_EVENT_STA_GOT_IP) {
        // 清零重连尝试次数
        WIFI_AUTO_RECONNECT_RETRIES_NOW = 0;
        // 禁用自动重连
        WIFI_AUTO_RECONNECT_ENABLED = 0;
    }
}

代码地址

代码开源地址


http://www.kler.cn/a/579074.html

相关文章:

  • Android OKHttp缓存模块原理分析
  • JVM类加载器面试题及原理
  • 云原生(六十) | Web源码迁移部署
  • C++ 滑动窗口
  • 玩转ChatGPT:GPT 深入研究功能
  • 当代体育科技杂志社《当代体育科技》编辑部2025年第2期目录
  • 完整例子和调用关系qt OpenGL
  • Electron-Forge + Vue3 项目初始化
  • Qt常用控件之表格QTableWidget
  • 【leetcode100】组合总和Ⅱ
  • 【MySQL】事务|概念|如何回滚|基本特性|MySQL事务隔离性具体怎么实现的
  • 如何不重启,生效windows环境变量
  • 高效Android MQTT封装工具:简化物联网开发,提升性能与稳定性
  • 基于Linux环境部署和使用ElasticSearch搜索引擎
  • C# 开发工具Visual Studio下载和安装
  • ubuntu20.04已安装 11.6版本 cuda,现需要通过源码编译方式安装使用 cuda 加速的 ffmpeg 步骤
  • 【MySQL】(4) 表的操作
  • Spring Boot 内置工具类,功能齐全!!
  • MoonSharp 文档一
  • 2025年最新可用!Docker/DockerHub 国内镜像源/加速列表