【ESP32】打造全网最强esp-idf基础教程——16.SmartConfig一键配网

SmartConfig一键配网

一、SmartConfig知识扫盲
       在讲STA课程的时候,我们用的是代码里面固定的SSID和密码去连接热点,但实际应用中不可能这么弄,我们得有办法把家里的WiFi SSID和密码输入到设备里面去,对于带屏带输入设备还好,因为可以人为手动输入,但很多IOT设备都不具备这种能力,因此我们需要其他方法。把SSID和密码告诉给设备,让设备能正确连接WiFi热点接入到物联网的过程,称为配网。

       配网方式有很多种,比如AP配网、蓝牙配网,还有本课介绍的SmartConfig一键配网,SmartConfig对用户来说操作是最简单的配网方式,其配网原理比较巧妙,我们来看下SmartConfig的基本原理到底如何。
       首先要让WiFi芯片处于混杂模式下,监听网络中的所有报文;手机APP将SSID和密码编码到 UDP 报文中,通过广播包或组播报发送,智能硬件接收到 UDP 报文后解码,得到正确的 SSID 和密码,然后主动连接指定 SSID 的路由完成连接。

       具体是如何接收报文的?另外在802.11协议中,MAC帧数据域是加密的,设备没有连上WiFi是无法读取这部分内容的。具体帧格式如下图,我们只关注MAC帧中的数据域(MSDU) 

       解析我们知道这部分数据的长度,这部分数据是由20字节IPv4头部+8字节UDP报文头部+UDP内容组成的IP报文,假如IP报文长度为500字节,则UDP内容长度为500-20-8=472字节,这里我们定义,500字节称为明文长度。 

       我们再制定一个定义,密文长度=明文长度+算法常量,算法常量往往是一个固定值,由APP和WIFI设备默认。 

       假如算法常量是10。现在手机APP要传输1234这个数据,只需要在UDP报文内容中填充(1234-20(Ipv4头)-8(UDP报头)-10(算法常量))个字节即可(内容任意),也就是  

        IP报文总长1224
       Ipv4头:20字节
       UDP头:8字节
       UDP内容:1196字节

       也就是说我们通过UDP广播发送1196个字节就行,内容任意。

       当设备收到这个UDP报文后需要解码,先得到IP报文长度1224,然后我们要加上算法常量10,得到1234,因此设备最终获得了1234这个数据。 

       那么对于设备来说,如何知道这个UDP广播包就是SmartConfig发出的呢?这里涉及到一个前导码的概念,当设备WIFI开启混杂模式时,会在所处环境中快速切换各条信道来抓取每个信道中的数据包,当遇到正在发送前导码数据包的信道时,锁定该信道并继续接收广播数据,直到收到足够的数据来解码出其中的WiFi密码然后连接WiFi,因此前导码一般由几个特殊的字节组成,方便和其他UDP包区分。 

       假设手机APP要发送”test”四个字符,算法常量为16,流程如下:
      1)APP连续发送3个UDP广播包,数据为均为前导码。
      2)APP发送1个UDP广播包,IP报文数据长度为’t’-16。
      3)APP发送1个UDP广播包,IP报文数据长度为’e’-16。
      4)APP发送1个UDP广播包,IP报文数据长度为’s’-16。
      5)APP发送1个UDP广播包,IP报文数据长度为’t’-16。
      6)APP切换WIFI信道重复上述步骤

       上述是数据传输的基本原理,但由于每一家厂商的算法常量、传输内容格式、前导码等都不一样,因此不同厂家之间的SmartConfig一般无法通用。

二、ESP32中的SmartConfig
       通过查看esp-idf的源码,发现ESP32上的SmartConfig实现是看不到源码的,但不妨碍我们使用,而使用方式也比较简单,当然需要配合APP来使用,乐鑫官方也提供了demo版本的APP,这个是开源的,我们可以集成到自己的APP应用中,下载地址是: 

       安卓:
https://github.com/EspressifApp/EsptouchForAndroid/releases/tag/v2.0.0/esptouch-v2.0.0.apk

       IOS:
https://apps.apple.com/cn/app/espressif-esptouch/id1071176700

       接下来看下ESP32源码,源码位于esp32-board/wifi_smartconfig
       由于在实际应用工程中,进入SmartConfig一般都是长按某个按键,因此这个例程中也把之前按键短按长按处理的例程搬过来用了,长按3秒触发SmartConfig。

       app_main()如下 

//按键事件组
static EventGroupHandle_t s_pressEvent;
#define SHORT_EV    BIT0    //短按
#define LONG_EV     BIT1    //长按
#define BTN_GPIO    GPIO_NUM_39

/** 长按按键回调函数
 * @param 无
 * @return 无
*/
void long_press_handle(void)
{
    xEventGroupSetBits(s_pressEvent,LONG_EV);
}
void app_main(void)
{
    nvs_flash_init();           //初始化NVS
    initialise_wifi();          //初始化wifi
    s_pressEvent = xEventGroupCreate();
    button_config_t btn_cfg = 
    {
        .gpio_num = BTN_GPIO,       //gpio号
        .active_level = 0,          //按下的电平
        .long_press_time = 3000,    //长按时间
        .short_cb = NULL,           //短按回调函数
        .long_cb = smartconfig_start             //长按回调函数
    };
    button_event_set(&btn_cfg);     //添加按键响应事件处理
    EventBits_t ev;
    while(1)
    {
        ev = xEventGroupWaitBits(s_pressEvent,LONG_EV,pdTRUE,pdFALSE,portMAX_DELAY);
        if(ev & LONG_EV)
        {
            smartconfig_start();    //检测到长按事件,启动smartconfig
        }
    }
}

       app_main()中注册了长按按键事件,主循环中检测到了长按事件,执行smartconfig_start函数,启动SmartConfig。 

       接下来看下main/wifi_smartconfig.c文件对SmartConfig的处理 

/** 启动smartconfig
 * @param 无
 * @return 无
*/
void smartconfig_start(void)
{
    if(!s_is_smartconfig)
    {
        s_is_smartconfig = true;
        esp_wifi_disconnect();
        xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);
    }
}

       smartconfig_start函数里面,会断开wifi连接然后启用一个smartconfig任务,s_is_smartconfig标志是SmartConfig运行标志,防止重复执行SmartConfig。 

/** smartconfig处理任务
 * @param 无
 * @return 无
*/
static void smartconfig_example_task(void * parm)
{
    EventBits_t uxBits;
    ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH_V2) );           //设定SmartConfig版本
    smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_smartconfig_start(&cfg) ); //启动SmartConfig
    while (1) {
        uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY);
        if(uxBits & CONNECTED_BIT) {
            ESP_LOGI(TAG, "WiFi Connected to ap");
        }
        if(uxBits & ESPTOUCH_DONE_BIT) {    //收到smartconfig配网完成通知
            ESP_LOGI(TAG, "smartconfig over");
            esp_smartconfig_stop();         //停止smartconfig配网
            write_nvs_ssid(s_ssid_value);   //将ssid写入NVS
            write_nvs_password(s_password_value);   //将password写入NVS
            s_is_smartconfig = false;       
            vTaskDelete(NULL);              //退出任务
        }
    }
}

       在smartconfig_example_task中会设定SmartConfig的版本,可以选V1、V2、AirKiss(微信用的),版本之间不兼容,我这边选用了V2,然后esp_smartconfig_start(&cfg)启动SmartConfig,后面会监听两个事件,一个是WiFi连接成功事件,一个是SmartConfig完成事件,当SmartConfig完成后,我们把SSID和密码保存到NVS中,然后退出任务,结束整个SmartConfig流程。 

       

/** 各种网络事件的回调函数
 * @param arg 自定义参数
 * @param event_base 事件类型
 * @param event_id 事件标识ID,不同的事件类型都有不同的实际标识ID
 * @param event_data 事件携带的数据
*/
static void event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        if(s_ssid_value[0] != 0)
            esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        //WIFI断开连接后,再次发起连接
        if(!s_is_smartconfig)
            esp_wifi_connect();
        //清除连接标志位
        xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        //获取到IP,置位连接事件标志位
        xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) {
        //smartconfig 扫描完成
        ESP_LOGI(TAG, "Scan done");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) {
        //smartconfig 找到对应的通道
        ESP_LOGI(TAG, "Found channel");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) {
        //smartconfig 获取到SSID和密码
        ESP_LOGI(TAG, "Got SSID and password");

        smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
        wifi_config_t wifi_config;
        uint8_t ssid[33] = { 0 };
        uint8_t password[65] = { 0 };
        //从event_data中提取SSID和密码
        bzero(&wifi_config, sizeof(wifi_config_t));
        memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
        memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));
        wifi_config.sta.bssid_set = evt->bssid_set;
        if (wifi_config.sta.bssid_set == true) {
            memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
        }
        memcpy(ssid, evt->ssid, sizeof(evt->ssid));
        memcpy(password, evt->password, sizeof(evt->password));
        ESP_LOGI(TAG, "SSID:%s", ssid);
        ESP_LOGI(TAG, "PASSWORD:%s", password);
        snprintf(s_ssid_value,33,"%s",(char*)ssid);
        snprintf(s_password_value,65,"%s",(char*)password);
        //重新连接WIFI
        ESP_ERROR_CHECK( esp_wifi_disconnect() );
        ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
        esp_wifi_connect();
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
        //smartconfig 已发起回应
        xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
    }
}

       在事件处理回调函数中,包含了对SmartConfig的处理,比较关键的是SC_EVENT_GOT_SSID_PSWDSC_EVENT_SEND_ACK_DONE事件,SC_EVENT_GOT_SSID_PSWD事件表示已经获取到SSID和密码了,接下来我们可以发起连接。SC_EVENT_SEND_ACK_DON事件表示SmartConfig完成,配网可以结束了,通知SmartConfig任务退出。 

       由此可见,esp-idf对SmartConfig功能进行了高度封装,我们基本不用做复杂的处理就可以使用,十分方便,完整的代码请看esp32-board/wifi_smartconfig,idf.py build+idf.py flash烧录到开发板后,就可以运行。另外大家可以看下官方的demo APP

 

       左边是V1版本,右边是V2版本,在进入APP的时候可以选择,使用的时候,需要手机连接当前的2.4G WiFi,然后输入密码,点击确定的时候就开始了,开发板上长按按键3秒,查看串口打印开始了SmartConfig即可松手,过一会就会自动的完成配网。 

最后附上相关资料:

ESP32教程资料链接:
https://pan.baidu.com/s/1kCjD8yktZECSGmHomx_veg?pwd=q8er 
提取码:q8er 

配套源码下载地址:
esp32-board: esp32开发板配套的经典例程

鉴于实验需要开发板的支持,我也设计了一款ESP32开发板,包含部分传感器模块,1.69寸LCD高亮屏,Type-C一键下载,方便大家学习和做各种实验。开发板链接如下:

https://item.taobao.com/item.htm?ft=t&id=802401650392&spm=a21dvs.23580594.0.0.4fee645eXpkfcp&skuId=5635015963649
 

​​

请大家多多支持。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/790155.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

15.x86游戏实战-汇编指令jmp call ret

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 本次游戏没法给 内容参考于:微尘网络安全 工具下载: 链接:https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

【服务器】在Linux查看运行的Python程序,并找到特定的Python程序

在Linux查看运行的Python程序并找到特定的Python程序 写在最前面1. 使用ps命令查看所有Python进程查看详细信息 2. 使用pgrep命令查找Python进程ID 3. 使用top或htop命令使用top命令使用htop命令 4. 使用lsof命令查找Python进程打开的文件 5. 使用nvidia-smi命令查看GPU使用情况…

Cancer Cell | 卞修武院士/时雨/平轶芳团队揭示胶质瘤巨噬细胞研究全新视角

胶质母细胞瘤(glioblastoma,GBM)作为最常见的恶性脑肿瘤之一,其肿瘤微环境高度复杂且侵袭性极强。肿瘤相关巨噬细胞(TAMs)在胶质瘤发展和免疫抑制中扮演着关键角色,尽管靶向TAMs的治疗策略已经取…

68.SAP FICO - 记账码学习

目录 定义 用途 配置步骤 定义记账码 - OB41 配置会计科目类型 在会计中,“借”和“贷”是记账符号,代表了记账的方向。而在SAP中却没有大家熟知的记账符号“借”和“贷”,那SAP中如何录入凭证呢?其实,SA…

MT3054 搭积木

1.思路&#xff1a; 把二维矩阵转化成一维编号&#xff0c;之后将编号使用并查集&#xff0c;看最后是否在同一个集合中即可。 2.代码&#xff1a; #include <bits/stdc.h> using namespace std; const int N 1e3 10; int n, m, cnt, root; int fa[N * N]; int dx[…

09.C2W4.Word Embeddings with Neural Networks

往期文章请点这里 目录 OverviewBasic Word RepresentationsIntegersOne-hot vectors Word EmbeddingsMeaning as vectorsWord embedding vectors Word embedding processWord Embedding MethodsBasic word embedding methodsAdvanced word embedding methods Continuous Bag-…

网络编程:各协议头(数据报格式)

一、mac头 二、ip头 protocol——tcp/udp &#xff08;7&#xff09;TTL——生存时间 三、tcp头 四、udp头

昇思大模型——MindFormers的使用----从零开始安装配置环境

MindSpore Transformers套件的目标是构建一个大模型训练、微调、评估、推理、部署的全流程开发套件&#xff0c;提供业内主流的Transformer类预训练模型和SOTA下游任务应用&#xff0c;涵盖丰富的并行特性。期望帮助用户轻松的实现大模型训练和创新研发。 MindSpore Transform…

centos更换yum源、安装Docker和换源

所有操作都是在root权限下做的&#xff0c;切换root用户 命令&#xff1a;su root 使用ls /etc/yum*查看所有的关于yum的文件的路径 先安装wget 命令&#xff1a;yum install wget -y 命令&#xff1a;cd /etc/yum.repos.d进去&#xff0c;以便于操作 我们需要配置的是Cen…

DNS 杂谈

一、定义 DNS&#xff08;Domain Name System&#xff09;&#xff0c;域名系统&#xff0c;该系统记录域名和Ip地址的相互映射关系。用户访问互联网时&#xff0c;通过域名地址得到对应的IP地址&#xff0c;这个过程称为域名解析。DNS运行于UDP协议之上&#xff0c;使用的端口…

react-类组件1

类组件&#xff1a; import { Component } from "react";class App extends Component {constructor() {super();this.state {message: "xxxxx",};}render() {return (<div><div>{this.state.message}</div></div>);} }export d…

Animate软件基础:重命名图层或文件夹

默认情况下&#xff0c;Animate 会按照创建顺序向新图层分配名称&#xff1a;图层 1、图层 2&#xff0c;依此类推。为了更好地反映图层的内容&#xff0c;可以对图层进行重命名。 如果需要对图层或图层文件夹进行重命名&#xff0c;请执行下列操作之一&#xff1a; 双击时间轴…

第三课网关作用

实验拓扑图&#xff1a; 基础配置&#xff1a; PC1的基础配置 PC2的基础配置&#xff1a; PC4的基础配置 AR1添加PC4网段: 并且添加pc1,pc2的网段。 并且添加pc1,pc2的网段。 原理&#xff1a;PC4先把数据交给100.100.100.1&#xff0c;交给了路由器&#xff0c;路由器再把数…

图文讲解IDEA如何导入JDBC驱动包

前言 学习JDBC编程,势必要学会如何导入驱动包,这里笔者用图文的方式来介绍 视频版本在这里 50秒教你怎么导入驱动包然后进行JDBC编程的学习_哔哩哔哩_bilibili 忘记录音频了,大伙凑合着看 下载驱动包 https://mvnrepository.com/artifact/mysql/mysql-connector-java 去中…

推荐3款电脑必备专业软件,错过拍大腿

SolveigMM Video Splitter SolveigMM Video Splitter是一款功能强大的视频编辑工具&#xff0c;主要用于视频的无损剪切和合并。该软件支持多种常见的视频格式&#xff0c;如AVI、WMV、ASF、MP3、WMA等。此外&#xff0c;它还支持AVCHD、MPEG-2、WebM、FLV等格式&#xff0c;并…

2024春秋杯网络安全联赛夏季赛-PWN

文章目录 stdout测试setvbuf(stdout, 0LL, 2, 0LL)绕过或者输出直到缓冲区满使用system("/bin/sh")或者onegadget即使setvbuf(stdout, 0LL, 0, 0LL);也能立即有回显参考[https://starrysky1004.github.io/2024/07/05/2024-shu-qi-xue-xi-ji-lu/#toc-heading-4](https…

软件工程(下)

目录 需求工程 概述 需求获取 分层 获取方法 项目管理维度 需求开发---需求分析 UML&#xff08;统一建模语言&#xff09;&#xff1a;平台无关、语言无关 UML 41视图 需求的定义、验证、跟踪、变更 需求定义 需求验证 需求跟踪 需求变更管理 软件系统建模 软件…

传知代码-多行人姿态检测系统

代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 概述 本项目创新在于采用多级网络串联工作来进行目标的行为分析&#xff0c;并使用在视频监控领域&#xff0c;可部署在任何有需要的人员流动密集场所(如医院&#xff0c;机场&#xff0c;养老院等)或者用于空巢…

ubuntu18虚拟机克隆后不能上网和磁盘损坏问题

小学期直接给学弟学妹们自己配好的克隆的虚拟机&#xff0c;结果出现了这两种问题&#xff0c;参考了网上好多资料&#xff0c;太多了忘了存了&#xff0c;花了好久的时间才解决&#xff0c;这里记录一下。 磁盘损坏问题&#xff1a; 网络无法连同问题&#xff0c;ip addr发现…

硅谷甄选二(登录)

一、登录路由静态组件 src\views\login\index.vue <template><div class"login_container"><!-- Layout 布局 --><el-row><el-col :span"12" :xs"0"></el-col><el-col :span"12" :xs"2…