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

ESP32_WiFi连接

一、WiFi连接

1.ESP32 WiFi功能简介

  • ESP32硬件支持2.4G频段的WiFi和BLE 4.2;
  • ESP32对WiFi的驱动支持十分完善,我们不需要花费过多精力去研究底层实现,可以将更多的精力放在自己的应用设计上;

2.ESP32 WiFi的三种工作方式

  • STA(Station)模式,也称为站点模式或客户端模式,也是ESP32最常用的模式:
    • 在这种模式下,ESP32作为WiFi客户端,主动连接到现有的WiFi接入点(AP,Access Point),从而实现与设备或网络的通信。
    • 例如:ESP32通过连接路由器的WiFi来连接网络;
  • SoftAP模式,机软接入点模式:
    • 此时ESP32设备自身充当WiFi接入点,允许其他WiFi客户端连接到它,可以将其看作一个小型的无线热点;
    • 例如:在没有现成的WiFi网络环境中,让手机或其他设备连接到ESP32创建的无线网络中,实现设备间的近距离通信,而无需外部的WiFi网络;
  • Station + SoftAP模式:
    • 这种模式结合了上述两种模式的特点,ESP32设备既可以作为WiFi客户端连接到其他接入点,同时又能作为软接入点为其他设备提供WiFi接入服务;
    • 例如:一些物联网场景中,ESP32可以连接到家庭网络以获取网络连接,同时又可以作为附近一些低功耗传感器设备提供本地的WiFi接入,实现数据的收集和上传;

3.ESP32 STA模式代码实现

  • ESP-IDF中在esp-idf/examples/wifi/getting_started/station路径下有对应的示例代码
    在这里插入图片描述
  • 加注释代码
    #include <stdio.h>
    #include "nvs_flash.h"      
    #include "esp_wifi.h"
    #include "esp_event.h"
    #include "esp_log.h"
    #include "esp_err.h"       //? 错误检查的头文件
    
    #define CON_SSID    	"ling"
    #define CON_PASSWORD    "527628..."
    #define TAG "wifi_sta"
    // #define MY_CFG_ORDER
    
    void event_handle(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
    {
        if(event_base == WIFI_EVENT)
        {
            switch (event_id)
            {
            case WIFI_EVENT_STA_START:          //? STA工作模式已启动
                esp_wifi_connect();             //? 连接WiFi
                break;
            case WIFI_EVENT_STA_CONNECTED:      //? EPS32已经成功连接到路由器
                ESP_LOGI(TAG, "esp32 connected to api!");
                break;
            case WIFI_EVENT_STA_DISCONNECTED:   //? 断连
                esp_wifi_connect();
            default:
                break;
            }
        }
        else if(event_base == IP_EVENT)
        {
            switch (event_id)
            {
            case IP_EVENT_STA_GOT_IP:           //? EPS32获取到无线路由器分配的IP,此时ESP32才真正的连接到路由器(如果路由器能连接到互联网,ESP32也能连接到互联网)
                ESP_LOGI(TAG, "esp32 get ip address");
                break;
            default:
                break;
            }
        }
    }
    
    void app_main(void)
    {   
        #ifndef MY_CFG_ORDER
    	    /*  1.初始化NVS
    	        默认状态下,当我们使用一组SSID和密码连接WiFi成功后,ESP-IDF的底层会帮我们把这组SSID和密码保存到NVS中;
    	        下次系统重启后,当进入STA模式并进行连接时,就会用这组SSID和密码进行连接;
    	    */ 
    	    ESP_ERROR_CHECK(nvs_flash_init());      //? 宏ESP_ERROR_CHECK,用来检查NVS初始化是否有问题;
    	    //  2.初始化TCP/IP协议栈(ESP-IDF中使用的时LWIP)
    	    ESP_ERROR_CHECK(esp_netif_init());      //? netif--->net interface,网络接口的缩写;
    	    /*  3.创建事件系统循环
    	        WiFi连接过程中会产生各种的事件,这些事件都是通过回调函数来通知我们的
    	    */
    	    ESP_ERROR_CHECK(esp_event_loop_create_default());
    	    //  4.创建STA
    	    esp_netif_create_default_wifi_sta();    //? 该函数会返回一个STA网卡对象,这里我们使用不到,可以忽略该返回值;
    	    /*  5.初始化WiFi
    	        定义一个WiFi初始化结构体,将其设置到WiFi初始化函数中;
    	        该步骤会设置WiFi的缓冲区数量,加密功能等,我们按默认功能设置就可以;
    	    */
    	    wifi_init_config_t wifi_init_cfg = WIFI_INIT_CONFIG_DEFAULT();
    	    ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_cfg));
    	    //  6.注册事件响应
    	    esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, event_handle, NULL);      //? 注册WiFi事件回调
    	    esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handle, NULL);     //? 注册IP事件回调(连接到路由器,获取到IP地址后就会触发该事件)
    	    //  7.WiFi参数配置
    	    wifi_config_t wifi_config = {
    	        .sta = {
    	            .ssid = CON_SSID,                               //? SSID,服务器集标识符,即WiFi名称;
    	            .password = CON_PASSWORD,                       //? WiFi密码;
    	            .threshold.authmode = WIFI_AUTH_WPA2_PSK,       //? 配置加密模式,目前常用的时WPA2_PSK;
    	            .pmf_cfg.capable = true,                        //? 是否启用保护管理帧,启用后可以增强安全性;
    	            .pmf_cfg.required = false,                      //? 是否只和有保护管理帧功能的设备通信;
    	        },
    	    };
    	    esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
    	    //  8.设置WiFi模式
    	    esp_wifi_set_mode(WIFI_MODE_STA);
    	    //  9.启动WiFi
    	    esp_wifi_start();
    	    
        #else
    	    /*  1.初始化NVS
    	        默认状态下,当我们使用一组SSID和密码连接WiFi成功后,ESP-IDF的底层会帮我们把这组SSID和密码保存到NVS中;
    	        下次系统重启后,当进入STA模式并进行连接时,就会用这组SSID和密码进行连接;
    	    */ 
    	    ESP_ERROR_CHECK(nvs_flash_init());      //? 宏ESP_ERROR_CHECK,用来检查NVS初始化是否有问题;
    	    //  2.初始化TCP/IP协议栈(ESP-IDF中使用的时LWIP)
    	    ESP_ERROR_CHECK(esp_netif_init());      //? netif--->net interface,网络接口的缩写;
    	    /*  3.创建事件系统循环
    	        WiFi连接过程中会产生各种的事件,这些事件都是通过回调函数来通知我们的
    	    */
    	    ESP_ERROR_CHECK(esp_event_loop_create_default());
    	    //  6.注册事件响应
    	    esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, event_handle, NULL);      //? 注册WiFi事件回调
    	    esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handle, NULL);     //? 注册IP事件回调(连接到路由器,获取到IP地址后就会触发该事件)
    	    //  4.创建STA
    	    esp_netif_create_default_wifi_sta();    //? 该函数会返回一个STA网卡对象,这里我们使用不到,可以忽略该返回值;
    	    //  8.设置WiFi模式为STA
    	    esp_wifi_set_mode(WIFI_MODE_STA);
    	    /*  5.初始化WiFi
    	        定义一个WiFi初始化结构体,将其设置到WiFi初始化函数中;
    	        该步骤会设置WiFi的缓冲区数量,加密功能等,我们按默认功能设置就可以;
    	    */
    	    wifi_init_config_t wifi_init_cfg = WIFI_INIT_CONFIG_DEFAULT();
    	    ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_cfg));
    	    //  7.WiFi参数配置
    	    wifi_config_t wifi_config = {
    	        .sta = {
    	            .ssid = CON_SSID,                               //? SSID,服务器集标识符,即WiFi名称;
    	            .password = CON_PASSWORD,                       //? WiFi密码;
    	            .threshold.authmode = WIFI_AUTH_WPA2_PSK,       //? 配置加密模式,目前常用的时WPA2_PSK;
    	            .pmf_cfg.capable = true,                        //? 是否启用保护管理帧,启用后可以增强安全性;
    	            .pmf_cfg.required = false,                      //? 是否只和有保护管理帧功能的设备通信;
    	        },
    	    };
    	    esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
    	    //  9.启动WiFi
    	    esp_wifi_start();
        #endif
    
        
    }
    
    

二、SmartConfig 配网

1.配网

  • 上节WiFi连接是把WiFi名称和密码写入代码中进行连接的,但实际应用中不可能这么做,需要把用户自己的WiFi名称和密码输入到设备中。对于没有屏幕输入功能的设备,就需要其他方法把SSID和密码写入到设备中。
  • 通过某种方法,把WiFi的名称和密码高数设备,让设备能够正确连接WiFi的过程,称为配网;
  • 配网的种类
    • AP配网;
    • SmartConfig配网;(相对简单)
    • 蓝牙配网;

2.SmartConfig配网的原理

  • SmartConfig步骤
    • 1.先让芯片处于混杂模式下,监听网络中的所有报文;
    • 2.手机APP将SSID和密码编码到UDP报文中,通过广播报或组播报文的形式发送;
    • 3.芯片收到UDP报文后,解码得到正确的SSID和密码,之后连接到指定的路由;
  • SmartConfig的原理
    • 传输数据的特点——加密
      在802.11无线协议中,MAC帧的数据是加密的,没有连上热点的设备是无法读取数据帧的具体内容,具体帧格式如下图所示
      在这里插入图片描述

    • 数据内容如何表示?——长度
      如果手机发送的是UDP广播博文,则这部分加密报文就是IP报文------>虽然我们无法知道报文的确切内容,但我们可以知道报文的长度;------>SmartConfig就是根据UDP报文的长度传输SSID和Password的;

      • IP报文的组成:IPv4头部(20 Bytes) + UDP报文头部(8 Bytes) + UDP报文;UDP报文的长度称为“明文长度”
        假设IPD报文长度为500 Bytes,则UDP报文长度: 500 - 20 - 8 = 472 Bytes;
      • 密文长度 = 明文长度 + 算法常量(固定值,由APP和WiFi设备默认);
      • 示例:算法常量 = 10;当APP传输“1234”这个数据时,只需要在UDP报文中1196个字节即可【1234 - 20(IPv2头部长度) -8(UDP报文长度) -10(算法常量)】,UDP报文内容可任意填充;
    • 如何区分进行SmartConfig配网的数据——前导码
      当设备在混杂模式时,会在所处环境中快速切换各条信道来抓取每个信道中的数据包------>当遇到正在发送前导码的的信道时,设备锁定该信道并继续接受UDP广播包,直到收到足够的数据来解码出WiFi的SSID和Password;------>为了方便和其他UDP广播包区分,前导码由几个特殊的字节组成------>在发送时,APP先发送3个前导码(3个UDP广播包),之后发送用于SmartConfig的UDP广播包,最后发送3个终止码;

    • SmartConfig的流程:

      1. APP部分:假设手机APP要发送“test”这4个字符,算法常量为16,则具体流程如下
      • (1)APP连续发送3个前导码(3个UDP广播包);
      • (2)APP发送1个UDP广播包,报文总长度为’t’ - 16;
      • (3)APP发送1个UDP广播包,报文总长度为’e’ - 16;
      • (4)APP发送1个UDP广播包,报文总长度为’s’ - 16;
      • (5)APP发送1个UDP广播包,报文总长度为’t’ - 16;
      • (6)APP连续发送3个终止码
      • (7)APP切换WiFi信道重复上述步骤

      2.设备部分

      • (1)设备进入混杂模式,监听网络中所有的报文;
      • (2)连续收到3个前导码报文且来自同一个发射源;
      • (3)持续捕获发射源的数据,直到连续收到3个终止码报文;
      • (4)将捕获到的数据进行解码并缓存;
      • (5)重复2~4步骤,二次验证数据;
      • (6)使用上述步骤得到的WiFi SSID、Password连接WiFi,成功后向APP汇报;

      3.注意:上述是传输的基本原理,但每家厂商的算法常量、前导码、终止码、传输内容格式会不太一样,因此不同厂家的SmartConfig一般是没有办法通用的;

3.ESP32 SmartConfig的APP——ESP Touch下载

  • 乐鑫官网下载链接,需要在Github上下载(网速比较慢),下载后发送到手机安装即可
  • 蓝奏云下载链接,下载密码:ga7x
    在这里插入图片描述
    在这里插入图片描述

4.示例代码(带注释)

#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "esp_wifi.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_smartconfig.h"

#define TAG "wifi_smartconfig"

void event_handle(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
    if(event_base == WIFI_EVENT)
    {
        switch (event_id)
        {
        case WIFI_EVENT_STA_START:          //? STA工作模式已启动
            esp_wifi_connect();             //? 连接WiFi
            break;
        case WIFI_EVENT_STA_CONNECTED:      //? EPS32已经成功连接到路由器
            ESP_LOGI(TAG, "esp32 connected to api!");
            break;
        case WIFI_EVENT_STA_DISCONNECTED:   //? 断连
            esp_wifi_connect();
        default:
            break;
        }
    }
    else if(event_base == IP_EVENT)
    {
        switch (event_id)
        {
        case IP_EVENT_STA_GOT_IP:           //? EPS32获取到无线路由器分配的IP,此时ESP32才真正的连接到路由器(如果路由器能连接到互联网,ESP32也能连接到互联网)
            ESP_LOGI(TAG, "esp32 get ip address");
            break;
        default:
            break;
        }
    }
    else if(event_base == SC_EVENT)
    {
        switch (event_id)
        {
        case SC_EVENT_SCAN_DONE:            //? 扫描完成
            ESP_LOGI(TAG,"sc scan done!");
            break;
        case SC_EVENT_GOT_SSID_PSWD:        //? 收到了APP发过来的SSID和密码
            smartconfig_event_got_ssid_pswd_t *ev = (smartconfig_event_got_ssid_pswd_t *)event_data;
            wifi_config_t wifi_config;
            memset(&wifi_config, 0, sizeof(wifi_config));
            snprintf((char *) wifi_config.sta.ssid, sizeof(wifi_config.sta.ssid), (char *)ev->ssid);
            snprintf((char *) wifi_config.sta.password, sizeof(wifi_config.sta.ssid), (char *)ev->password);
            wifi_config.sta.bssid_set = ev->bssid_set;
            if(wifi_config.sta.bssid_set)   //? 是否设置接入点的MAC地址,共48位,6字节;
                memcpy(wifi_config.sta.bssid, ev->bssid, 6);
            esp_wifi_disconnect();          //? 如果有WiFi连接,先断开连接
            esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
            esp_wifi_connect();
            break;
        case SC_EVENT_SEND_ACK_DONE:        //? 通知手机APP,Smartconfig结束
            esp_smartconfig_stop();         //? 停止Smartconfig
            break;
        default:
            break;
        }
    }
}


void app_main(void)
{
    /*  1.初始化NVS
        默认状态下,当我们使用一组SSID和密码连接WiFi成功后,ESP-IDF的底层会帮我们把这组SSID和密码保存到NVS中;
        下次系统重启后,当进入STA模式并进行连接时,就会用这组SSID和密码进行连接;
    */ 
    ESP_ERROR_CHECK(nvs_flash_init());      //? 宏ESP_ERROR_CHECK,用来检查NVS初始化是否有问题;
    //  2.初始化TCP/IP协议栈(ESP-IDF中使用的时LWIP)
    ESP_ERROR_CHECK(esp_netif_init());      //? netif--->net interface,网络接口的缩写;
    /*  3.创建事件系统循环
        WiFi连接过程中会产生各种的事件,这些事件都是通过回调函数来通知我们的
    */
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    //  4.创建STA
    esp_netif_create_default_wifi_sta();    //? 该函数会返回一个STA网卡对象,这里我们使用不到,可以忽略该返回值;
    /*  5.初始化WiFi
        定义一个WiFi初始化结构体,将其设置到WiFi初始化函数中;
        该步骤会设置WiFi的缓冲区数量,加密功能等,我们按默认功能设置就可以;
    */
    wifi_init_config_t wifi_init_cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_cfg));
    //  6.注册事件响应
    esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, event_handle, NULL);      //? 注册WiFi事件回调
    esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handle, NULL);     //? 注册IP事件回调(连接到路由器,获取到IP地址后就会触发该事件)
    esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, event_handle, NULL);
    //  8.设置WiFi模式
    esp_wifi_set_mode(WIFI_MODE_STA);
    //  9.启动WiFi
    esp_wifi_start();
    // 10.开启SmartConfig
    esp_smartconfig_set_type(SC_TYPE_ESPTOUCH);     //? 设置SmartConfig类型
    smartconfig_start_config_t sc_cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
    esp_smartconfig_start(&sc_cfg);

    return;
}

相关文章:

  • 重叠构造函数 、JavaBean模式、建造者模式、Spring的隐性大手
  • 《Operating System Concepts》阅读笔记:p460-p4470
  • Redis 事件机制详解
  • 第三十一篇 数据仓库(DW)与商业智能(BI)架构设计与实践指南
  • 流量分配的艺术: 如何设计一款负载均衡组件
  • 大数据环境搭建
  • C++之 【模板初阶(函数模板与类模板)】
  • 单表达式倒计时工具:datetime的极度优雅(Kimi)
  • (滑动窗口)算法训练篇11--力扣3.无重复字符的最长字串(难度中等)
  • ENSP学习day9
  • 算法及数据结构系列 - 滑动窗口
  • 2025-03-22 学习记录--C/C++-PTA 练习7-10 查找指定字符
  • js逆向之断点调试
  • 告别低效人工统计!自动计算计划进度
  • LintCode第1712题 - 和相同的二元子数组
  • (一)丶Windows安装RabbitMQ可能会遇到的问题
  • Docker入门篇4:查看容器资源、查看容器详细信息、查看容器日志、查看容器内运行的进程
  • uni-app jyf-parser将字符串转化为html 和 rich-text
  • HTML URL 学习笔记
  • 清洁机器人垃圾物识别与智能分类回收系统研究(大纲)
  • 俄罗斯称已收复库尔斯克州,普京发表讲话
  • 文旅部副部长饶权出任国家文物局局长
  • 偷拍拷贝某轨道车技术信息后撰写论文发表,工程师被判一年有期徒刑
  • “归雁经济”能带来什么?川大商学院调研团队深入乡村与返乡青年人才交流
  • 建设高标准农田主要目标是什么?有哪些安排?两部门有关负责人答问
  • 恒瑞医药一季度营收72亿元,净利增超36%:授权交易推动利润增长