【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(); //初始化NVSinitialise_wifi(); //初始化wifis_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) ); //启动SmartConfigwhile (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写入NVSwrite_nvs_password(s_password_value); //将password写入NVSs_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);//重新连接WIFIESP_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_PSWD和SC_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
请大家多多支持。
相关文章:
【ESP32】打造全网最强esp-idf基础教程——16.SmartConfig一键配网
SmartConfig一键配网 一、SmartConfig知识扫盲 在讲STA课程的时候,我们用的是代码里面固定的SSID和密码去连接热点,但实际应用中不可能这么弄,我们得有办法把家里的WiFi SSID和密码输入到设备里面去,对于带屏带输入设备还…...
MD5加密和注册页面的编写
MD5加密 1.导入包 npm install --save ts-md5 2.使用方式 import { Md5 } from ts-md5; //md5加密后的密码 const md5PwdMd5.hashStr("123456").toUpperCase(); 遇见的问题及用到的技术 注册页面 register.vue代码 <template><div class"wappe…...
【Android组件】封装加载弹框
📖封装加载弹框 ✅1. 构造LoadingDialog✅2. 调用LoadingDialog 效果: ✅1. 构造LoadingDialog 构造LoadingDialog类涉及到设计模式中的建造者模式,进行链式调用,注重的是构建的过程,设置需要的属性。 步骤一&#x…...
Spring源码二十:Bean实例化流程三
上一篇Spring源码十九:Bean实例化流程二中,我们主要讨论了单例Bean创建对象的主要方法getSingleton了解到了他的核心流程无非是:通过一个简单工厂的getObject方法来实例化bean,当然spring在实例化前后提供了扩展如:bef…...
前端导出文件时,后端代码出错如何将错误信息返回给前端展示
功能说明:前端导出excel时,后端出现异常,比如sql异常,或者创建excel时出现的异常,希望将这些异常信息返回给前端查看。 框架:vue3 axios Springboot 实现难度分析:前端导出excel,…...
解决Spring Boot应用中的内存优化问题
解决Spring Boot应用中的内存优化问题 大家好,我是微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 1. Spring Boot应用的内存管理 在开发和部署Spring Boot应用时,有效地管理内存是确保应用性能和稳…...
shark云原生-日志体系-filebeat高级配置(适用于生产)-更新中
文章目录 1. filebeat.inputs 静态日志收集器2. filebeat.autodiscover 自动发现2.1. autodiscover 和 inputs2.2. 如何配置生效2.3. Providers 提供者2.4. Providers kubernetes2.5. 配置 templates2.5.1. kubernetes 自动发现事件中的变量字段2.5.2 配置 templates 2.6. 基于…...
响应式设计的双璧:WebKit 支持 CSS Flexbox 和 Grid 布局深度解析
响应式设计的双璧:WebKit 支持 CSS Flexbox 和 Grid 布局深度解析 在现代网页设计中,响应式布局是实现跨设备兼容性的关键。CSS Flexbox 和 Grid 作为 CSS 布局的两大支柱,提供了强大的工具来构建灵活和复杂的用户界面。WebKit,作…...
Linux软件包管理
一、软件包管理 1.什么是软件包 一般在window系统的.exe是软件按转包 2.linux系统下的软件包安装方式 PRM 软件包安装 软件名称.rpmYUM 包管理工具 yum intall 软件名称 -y源码安装 下载源代码---编译---安装 很麻烦,稳定 3.二进制软件包 二进制 4.获取*.rpm…...
如何分辨AI生成的内容?AI生成内容检测工具对比实验
检测人工智能生成的文本对各个领域的组织都提出了挑战,包括学术界和新闻界等。生成式AI与大语言模型根据短描述来进行内容生成的能力,产生了一个问题:这篇文章/内容/作业/图像到底是由人类创作的,还是AI创作的?虽然 LL…...
Clion中怎么切换不同的程序运行
如下图,比如这个文件夹下面有那么多的项目: 那么我想切换不同的项目运行怎么办呢?如果想通过下图的Edit Configurations来设置是不行的: 解决办法: 如下图,选中项目的CMakeLists.txt,右键再点击…...
【C++初阶】C++入门(下)
【C初阶】C入门(下) 🥕个人主页:开敲🍉 🥕所属专栏:C🥭 🌼文章目录🌼 6. 引用 6.1 引用的概念 6.2 引用特性 6.3 常引用 6.4 使用场景 6.5 传值、传引用效率…...
【3】迁移学习模型
【3】迁移学习模型 文章目录 前言一、安装相关模块二、训练代码2.1. 管理预训练模型2.2. 模型训练代码2.3. 可视化结果2.4. 类别函数 总结 前言 主要简述一下训练代码 三叶青图像识别研究简概 一、安装相关模块 #xingyun的笔记本 print(xingyun的笔记本) %pip install d2l %…...
【工具分享】FOFA——网络空间测绘搜索引擎
文章目录 FOFA介绍FOFA语法其他引擎 FOFA介绍 FOFA官网:https://fofa.info/ FOFA(Fingerprinting Organizations with Advanced Tools)是一款网络空间测绘的搜索引擎,它专注于帮助用户收集和分析互联网上的设备和服务信息。FOFA…...
[嵌入式 C 语言] 按位与、或、取反、异或
若协议中如下图所示: 注意: 长度为1,表示1个字节,也就是0xFF,也就是 1111 1111 (这里0xFF只是单纯表示一个数,也可以是其他数,这里需要注意的是1个字节的意思) 一、按位…...
Android --- 运行时Fragment如何获取Activity中的数据,又如何将数据传递到Activity中呢?
1.通过 getActivity() 方法获取 Activity 实例: 在 Fragment 中,可以通过 getActivity() 方法获取当前 Fragment 所依附的 Activity 实例。然后可以调用 Activity 的公共方法或者直接访问 Activity 的字段来获取数据。 // 在 Fragment 中获取 Activity…...
Java后端开发(十三)-- Java8 stream的 orElse(null) 和 orElseGet(null)
orElse(null)表示如果一个都没找到返回null。【orElse()中可以塞默认值。如果找不到就会返回orElse中你自己设置的默认值。】 orElseGet(null)表示如果一个都没找到返回null。【orElseGet()中可以塞默认值。如果找不到就会返回orElseGet中你自己设置的默认值。】 区别就…...
L2 LangGraph_Components
参考自https://www.deeplearning.ai/short-courses/ai-agents-in-langgraph,以下为代码的实现。 这里用LangGraph把L1的ReAct_Agent实现,可以看出用LangGraph流程化了很多。 LangGraph Components import os from dotenv import load_dotenv, find_do…...
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-…...
硅谷甄选二(登录)
一、登录路由静态组件 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…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合
作者:来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布,Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明,Elastic 作为 …...
Spring事务传播机制有哪些?
导语: Spring事务传播机制是后端面试中的必考知识点,特别容易出现在“项目细节挖掘”阶段。面试官通过它来判断你是否真正理解事务控制的本质与异常传播机制。本文将从实战与源码角度出发,全面剖析Spring事务传播机制,帮助你答得有…...
零基础在实践中学习网络安全-皮卡丘靶场(第十一期-目录遍历模块)
经过前面几期的内容我们学习了很多网络安全的知识,而这期内容就涉及到了前面的第六期-RCE模块,第七期-File inclusion模块,第八期-Unsafe Filedownload模块。 什么是"遍历"呢:对学过一些开发语言的朋友来说应该知道&…...
篇章一 论坛系统——前置知识
目录 1.软件开发 1.1 软件的生命周期 1.2 面向对象 1.3 CS、BS架构 1.CS架构编辑 2.BS架构 1.4 软件需求 1.需求分类 2.需求获取 1.5 需求分析 1. 工作内容 1.6 面向对象分析 1.OOA的任务 2.统一建模语言UML 3. 用例模型 3.1 用例图的元素 3.2 建立用例模型 …...
