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

【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_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
 

​​

请大家多多支持。

相关文章:

【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组件】封装加载弹框

&#x1f4d6;封装加载弹框 ✅1. 构造LoadingDialog✅2. 调用LoadingDialog 效果&#xff1a; ✅1. 构造LoadingDialog 构造LoadingDialog类涉及到设计模式中的建造者模式&#xff0c;进行链式调用&#xff0c;注重的是构建的过程&#xff0c;设置需要的属性。 步骤一&#x…...

Spring源码二十:Bean实例化流程三

上一篇Spring源码十九&#xff1a;Bean实例化流程二中&#xff0c;我们主要讨论了单例Bean创建对象的主要方法getSingleton了解到了他的核心流程无非是&#xff1a;通过一个简单工厂的getObject方法来实例化bean&#xff0c;当然spring在实例化前后提供了扩展如&#xff1a;bef…...

前端导出文件时,后端代码出错如何将错误信息返回给前端展示

功能说明&#xff1a;前端导出excel时&#xff0c;后端出现异常&#xff0c;比如sql异常&#xff0c;或者创建excel时出现的异常&#xff0c;希望将这些异常信息返回给前端查看。 框架&#xff1a;vue3 axios Springboot 实现难度分析&#xff1a;前端导出excel&#xff0c…...

解决Spring Boot应用中的内存优化问题

解决Spring Boot应用中的内存优化问题 大家好&#xff0c;我是微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 1. Spring Boot应用的内存管理 在开发和部署Spring Boot应用时&#xff0c;有效地管理内存是确保应用性能和稳…...

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 布局深度解析

响应式设计的双璧&#xff1a;WebKit 支持 CSS Flexbox 和 Grid 布局深度解析 在现代网页设计中&#xff0c;响应式布局是实现跨设备兼容性的关键。CSS Flexbox 和 Grid 作为 CSS 布局的两大支柱&#xff0c;提供了强大的工具来构建灵活和复杂的用户界面。WebKit&#xff0c;作…...

Linux软件包管理

一、软件包管理 1.什么是软件包 一般在window系统的.exe是软件按转包 2.linux系统下的软件包安装方式 PRM 软件包安装 软件名称.rpmYUM 包管理工具 yum intall 软件名称 -y源码安装 下载源代码---编译---安装 很麻烦&#xff0c;稳定 3.二进制软件包 二进制 4.获取*.rpm…...

如何分辨AI生成的内容?AI生成内容检测工具对比实验

检测人工智能生成的文本对各个领域的组织都提出了挑战&#xff0c;包括学术界和新闻界等。生成式AI与大语言模型根据短描述来进行内容生成的能力&#xff0c;产生了一个问题&#xff1a;这篇文章/内容/作业/图像到底是由人类创作的&#xff0c;还是AI创作的&#xff1f;虽然 LL…...

Clion中怎么切换不同的程序运行

如下图&#xff0c;比如这个文件夹下面有那么多的项目&#xff1a; 那么我想切换不同的项目运行怎么办呢&#xff1f;如果想通过下图的Edit Configurations来设置是不行的&#xff1a; 解决办法&#xff1a; 如下图&#xff0c;选中项目的CMakeLists.txt&#xff0c;右键再点击…...

【C++初阶】C++入门(下)

【C初阶】C入门&#xff08;下&#xff09; &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f955;所属专栏&#xff1a;C&#x1f96d; &#x1f33c;文章目录&#x1f33c; 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官网&#xff1a;https://fofa.info/ FOFA&#xff08;Fingerprinting Organizations with Advanced Tools&#xff09;是一款网络空间测绘的搜索引擎&#xff0c;它专注于帮助用户收集和分析互联网上的设备和服务信息。FOFA…...

[嵌入式 C 语言] 按位与、或、取反、异或

若协议中如下图所示&#xff1a; 注意&#xff1a; 长度为1&#xff0c;表示1个字节&#xff0c;也就是0xFF&#xff0c;也就是 1111 1111 &#xff08;这里0xFF只是单纯表示一个数&#xff0c;也可以是其他数&#xff0c;这里需要注意的是1个字节的意思&#xff09; 一、按位…...

Android --- 运行时Fragment如何获取Activity中的数据,又如何将数据传递到Activity中呢?

1.通过 getActivity() 方法获取 Activity 实例&#xff1a; 在 Fragment 中&#xff0c;可以通过 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&#xff0c;以下为代码的实现。 这里用LangGraph把L1的ReAct_Agent实现&#xff0c;可以看出用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…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

uniapp 字符包含的相关方法

在uniapp中&#xff0c;如果你想检查一个字符串是否包含另一个子字符串&#xff0c;你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的&#xff0c;但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

mac:大模型系列测试

0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何&#xff0c;是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试&#xff0c;是可以跑通文章里面的代码。训练速度也是很快的。 注意…...

全面解析数据库:从基础概念到前沿应用​

在数字化时代&#xff0c;数据已成为企业和社会发展的核心资产&#xff0c;而数据库作为存储、管理和处理数据的关键工具&#xff0c;在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理&#xff0c;到社交网络的用户数据存储&#xff0c;再到金融行业的交易记录处理&a…...

书籍“之“字形打印矩阵(8)0609

题目 给定一个矩阵matrix&#xff0c;按照"之"字形的方式打印这个矩阵&#xff0c;例如&#xff1a; 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为&#xff1a;1&#xff0c;…...