ESP32-Web-Server编程综合项目1-结合 Web Server 实现 WiFi 配网和网页 OTA 更新

ESP32-Web-Server编程综合项目1-结合 Web Server 实现 WiFi 配网和网页 OTA 更新

概述

前述的内容多是一个个小功能的演示,本章节讲述一些实际项目中使用到的综合项目。

首先要讲述的案例是通过ESP32 上的 Web Server 实现对 ESP32 的 WiFi 配网和网页 OTA 更新功能。

需求及功能解析

项目的主要功能有:

  • 通过菜单控制多网页的切换
  • 在多网页中分别实现 WiFi 配网、控制设备重启、通过网页下发 OTA 更新需要的新固件的功能。

WiFi 配网

当用户初次使用设备时,设备完全不知道要连接的路由器信息,此时可以通过建立一个 SoftAP (什么是 SoftAP 参考:AP、STA的概念以及AP+STA的实现),让用户向连接路由器一样连接该默认的 AP,然后打开配网网页,让 ESP32 连接指定的路由器,最终使得 ESP32 设备能够正常上网。

设备重启

设备配网信息下发后,可以通过网页或者设备的按钮重启设备(重新上电也行),设备将在重启后检测到已经下发的配网信息(网络名称和密码),然后使用该配网信息进行联网。

此外,当设备 OTA 结束时,也需要设备重启以加载新的固件。

OTA 更新

OTA 是更新设备固件的技术,可以认为是通过网络的方式传输固件进行软件更新的一种方法。

在路由器设计中,经常看到路由器的本地网页上支持通过网页上的输入框选中一个新的固件下发给路由器,对路由器的固件进行更新。

在这里插入图片描述

示例解析

目录结构

├── CMakeLists.txt
├── main
│   ├── CMakeLists.txt
│   └── main.c                 User application
├── components
│   └── fs_image└── index.html└── ...
|	└── url_handlers└── url_handlers.c└── ...
└── README.md                  This is the file you are currently reading
  • 目录结构主要包含主目录 main,以及组件目录 components.
  • 其中组件目录components中包含了用于存储网页文件的 fs_image 目录(即前端文件)。

前端代码

多网页菜单设计

components/fs_image/web_image/index.html 中设计三个子菜单:wifimanager、ota、Home:

<div class="topnav-right"><a href="wifimanager">WiFi Manager</a><a href="ota">OTA</a><a href="/">Home</a>
</div>

当点击对应的 href 时,浏览器会向 ESP32 Web Server 发送对该 href 的 Get 请求。

SoftAP 配网界面

components/fs_image/web_image/wifimanager_softap.html 中设计 SoftAP 配网的界面:

<form action="/wifi_config" method="POST"><p><label for="ssid">SSID</label><input type="text" id ="ssid" name="ssid"><br><label for="pass">Password</label><input type="text" id ="pass" name="pass"><br><label for="ip">IP Address</label><input type="text" id ="ip" name="ip" value="192.168.1.xxx"><input type ="submit" value ="Submit"></p>
</form>

上述配网界面非常简单,提供了输入路由器 WiFi 名称和密码的输入框。

当用户输入 WiFi 名称和密码后,将通过 http 的 POST 方法向 /wifi_config 推送配网信息。

WiFi Manager 界面

在配网成功,设备连接路由器后,我们可以像往常一样。让手机或者电脑连接到同一个路由器,然后通过局域网打开 ESP32 设备上的控制网页。

components/fs_image/web_image/wifimanager.html 界面,我们可以重新下发设备要连接的路由器的信息:

<form action="/wifi_config" method="POST"><p><label for="ssid">SSID</label><input type="text" id ="ssid" name="ssid"><br><label for="pass">Password</label><input type="text" id ="pass" name="pass"><br><!-- <label for="ip">IP Address</label><input type="text" id ="ip" name="ip" value="192.168.1.200"> --><input type ="submit" value ="Submit"></p>
</form>
OTA 更新界面

可以通过子菜单跳转到 OTA 更新的网页。

components/fs_image/web_image/ota.html 中,建立了一个 input file 的输入框。,并设置了一个 submit 按钮:

<h2>ESP32 Firmware Update Page</h2><h4 id="latest_firmware"></h4><input type="file" id="selectedFile" accept=".bin" style="display: none;" onchange="myFunction()" />
<input type="button" value="Browse..." onclick="document.getElementById('selectedFile').click();" />
<h3 id="file_info"></h3>
<input type='submit' id="upload_button" value='Update Firmware' onclick="updateFirmware()"><br>
<p>
<button onclick="rebootButton()">Reboot</button>
</p>

选中文件后,通过ota.html 中的 updateFirmware() 实现向 /update 以 POST 方法推送新固件数据:

function updateFirmware() {// Form Datavar formData = new FormData();var fileSelect = document.getElementById("selectedFile");if (fileSelect.files && fileSelect.files.length == 1) {var file = fileSelect.files[0];formData.set("file", file, file.name);document.getElementById("status").innerHTML = "Uploading " + file.name + " , Please Wait...";// Http Requestvar request = new XMLHttpRequest();request.upload.addEventListener("progress", updateProgress);request.open('POST', "/update");request.responseType = "blob";request.send(formData);} else {window.alert('Select A File First')}
}

此外,为了反映 OTA 更新过程的进度,在 ota.html 中的 script 中添加了更新进度的函数:

// progress on transfers from the server to the client (downloads)
function updateProgress(oEvent) {if (oEvent.lengthComputable) {getstatus();} else {window.alert('total size is unknown')}
}

后端代码

后端代码除建立前述基于 spiffs 的 Web Server 外,重点是设计对应前端文件的各个 URL 的 handlers.

这里的 URL 分为两组,一组是还未进行 WiFi 配网(比如设备刚出厂,用户首次使用时)时手机通过连接 ESP32 的 SoftAP 时打开的网页 httpd_uri_array_softap_only[]、一组是配网后,ESP32 设备连接到路由器后,用户通过手机或者电脑连接至同一个路由器时通过局域网打开的网页httpd_uri_array[]

httpd_uri_t httpd_uri_array_softap_only[] = {{"/wifi_config", HTTP_POST, wifi_config_post_handler, rest_context},{"/*", HTTP_GET, softap_wifi_html_get_handler,rest_context}};httpd_uri_t httpd_uri_array[] = {{"/wifimanager", HTTP_GET, wifi_manage_html_get_handler, rest_context},{"/ota", HTTP_GET, ota_html_get_handler, rest_context},{"/wifi_config", HTTP_POST, wifi_config_post_handler, rest_context},{"/update", HTTP_POST, OTA_update_post_handler, rest_context},{"/status", HTTP_POST, OTA_update_status_handler, rest_context},{"/reboot", HTTP_GET, reboot_html_get_handler, rest_context},{"/*", HTTP_GET, rest_common_get_handler,rest_context}//Catch-all callback function for the filesystem, this must be set to the array last one
};if (!s_sta_connected) { // 首次配网时uris = httpd_uri_array_softap_only;uris_len = sizeof(httpd_uri_array_softap_only)/sizeof(httpd_uri_t);
} else { // 已经配过网时uris = httpd_uri_array;uris_len = sizeof(httpd_uri_array)/sizeof(httpd_uri_t);
}for(int i = 0; i < uris_len; i++){if (httpd_register_uri_handler(server, &uris[i]) != ESP_OK) {ESP_LOGE(TAG, "httpd register uri_array[%d] fail", i);}
}
配网处理

配网处理在 main/main.c 中的 wifi_config_post_handler() 中,接收网页端 POST 的路由器 WiFi 的名称和密码,并使用 wifi_config_store() 将其存储在设备上。设备重新上电后将检测到已经存储了WiFi 的名称和密码,然后触发向该路由器的 WiFi 连接。

if (recv_post_data(req, buf) != ESP_OK) {ESP_LOGE(TAG, "recv post data error");return ESP_FAIL;
}str_len = httpd_find_arg(buf, PARAM_INPUT_1, temp_str, sizeof(temp_str));
if ((str_len != -1) && (strlen((char *)temp_str) != 0)) {// snprintf((char *)wifi_config.sta.ssid, 32, "%s", temp_str);memcpy((char *)wifi_config.sta.ssid, temp_str, 32);ESP_LOGI(TAG, "ssid:%s", (char *)wifi_config.sta.ssid);
}memset(temp_str, '\0', sizeof(temp_str));str_len = httpd_find_arg(buf, PARAM_INPUT_2, temp_str, sizeof(temp_str));
if ((str_len != -1) && (strlen((char *)temp_str) != 0)) {memcpy((char *)wifi_config.sta.password, temp_str, 64);ESP_LOGI(TAG, "pwd:%s", (char *)wifi_config.sta.password);
}
if(!wifi_config_store(&wifi_config)) {return ESP_FAIL;
}
关于 WiFi Manager

WiFI 管理主要是负责管理 WiFi 的工作模式,WiFi 配网信息。

默认情况下,设备将建立一个名称为 IoT_Old_Wang、密码为 123456789 、IP 地址默认为 192.168.4.1的 SoftAP,用户可以通过手机或者电脑搜索该热点,然后连接到该默认 AP,并使用浏览器打开 192.168.4.1 网址,查看配网界面。

如果需要更改默认 SoftAP 的信息,可以在编译程序时通过 idf.py menuconfig 打开配置菜单配置下述信息:

在这里插入图片描述

OTA 的后端处理

OTA 更新的后端处理在 main/web_ota.c 中,在 OTA_update_post_handler() 内,主要是接收从网页下发的固件数据:

if (esp_ota_end(ota_handle) == ESP_OK)
{// Lets update the partitionif(esp_ota_set_boot_partition(update_partition) == ESP_OK) {const esp_partition_t *boot_partition = esp_ota_get_boot_partition();// Webpage will request status when complete // This is to let it know it was successfulflash_status = 1;ESP_LOGI("OTA", "Next boot partition subtype %d at offset 0x%x", boot_partition->subtype, boot_partition->address);ESP_LOGI("OTA", "Please Restart System...");}else{ESP_LOGI("OTA", "\r\n\r\n !!! Flashed Error !!!");}
}

接收完固件将设置标志位 flash_status=1,当网页端重新请求固件的 status 时,将在 OTA_update_status_handler()中触发创建一个定时重启设备的定时器:

if (flash_status == 1)
{// We cannot directly call reboot here because we need the // browser to get the ack back. Send message to another task or create a create_a_restart_timer();// xEventGroupSetBits(reboot_event_group, REBOOT_BIT);		
}

示例效果

连接 SoftAP 执行配网

下图分别为连接对应热点,浏览器输入 192.168.4.1 打开配网界面:

在这里插入图片描述

注意:

  • AP 模式下打不开网页就关闭手机的其他网络通路,比如 4G 网络。

  • AP 配网(这里配网成功可以通过 GPIO 控制 LED灯闪烁来提示用户,或者在网页端跳出弹窗提示配置成功)示例这里以 Log 提示用户。

重启设备后,连接电脑到同一个路由器,打开多网页菜单

设备连接到路由器后,登录路由器查看 ESP32 设备的 IP 地址,如下该设备的 IP 地址为 192.168.47.100:

在这里插入图片描述

打开 wifi manager 界面

点击菜单栏的 WiFi Manager 打开wifi manager 界面:

在这里插入图片描述

打开 OTA 界面

点击菜单栏的 OTA 打开 OTA 界面:

在这里插入图片描述

OTA 完成后重启设备

点击 OTA 更新界面的 Browse 按钮选择需要上传的 OTA 新固件,然后点击 Updata Firmware,开始 OTA 更新。OTA 更新后可以重启设备以便于在下次重启设备时加载新的固件。

在这里插入图片描述

流程示意图:

在这里插入图片描述

讨论

1)示例程序中有 version.txt 文件,该文件可以用于控制固件的版本信息。读者可以更改该文件的内容,记录固件的版本信息。

2)示例使用的 ota APIs 可以参考 ESP-IDF OTA 开发指南。

总结

1)本节主要是介绍基于 ESP-IDF 的原始 API,实现综合项目1- 通过Web Server 实现 WiFi 配网和网页 OTA 更新。

2)示例设计了多网页子菜单,实现管理 WiFi 配网、OTA 固件更新、设备重启的功能。更多综合项目敬请期待。

资源链接

1)ESP32-Web-Server ESP-IDF系列博客介绍
2)对应示例的 code 链接 (点击直达代码仓库)

3)下一篇:

(码字不易感谢点赞或收藏)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

探究两个互联网时代的差异,Web 2.0 与 Web 3.0 区别

Web 2.0 的特征 首先我们来了解一下 Web 2.0 的特征都有哪些。 用户生成内容&#xff1a;Web 2.0 时代以用户生成内容为特征&#xff0c;用户可以轻松地在网络上分享、创建和编辑信息。社交媒体平台、博客等网站的兴起使得用户成为信息的创造者&#xff0c;网络逐渐从被动浏览…

增强现实技术革新零售业:提升购物体验的未来技术

增强现实&#xff08;AR&#xff09;技术正在改变零售业的面貌&#xff0c;为消费者提供了全新的购物体验。本文将探讨AR技术在零售行业中的应用&#xff0c;以及它如何改变传统的购物方式。 首先&#xff0c;AR技术允许消费者在现实世界中查看虚拟的产品展示。在服装和家具行业…

使用Redis构建任务队列

文章目录 第1关&#xff1a;先进先出任务队列第2关&#xff1a;优先级任务队列第3关&#xff1a;定时任务队列 第1关&#xff1a;先进先出任务队列 编程要求 在Begin-End区域编写 add_task(task_name) 函数&#xff0c;实现将任务加入队列的功能&#xff0c;具体参数与要求如下…

tomcat控制台中文信息显示乱码

问题现象 我的tomcat版本是10.1版本。 在cmd下启动tomcat&#xff0c;会新打开控制台输出窗口&#xff1a; 控制台窗口输出的中文信息是乱码&#xff1a; 问题原因 产生这个问题的原因是&#xff1a;控制台窗口的编码和输出到控制台窗口的日志信息编码不一致。 查看tomc…

Linux 基础命令

1 Linux 基础 1.1 用户类型 1.2 终端 Terminal 设备终端&#xff1a;键盘&#xff0c;鼠标&#xff0c;显示器 1.2.1 终端类型 1.2.2 查看当前的终端设备 tty 命令可以查看当前所在的终端 范例&#xff1a; # tty 可以查看当前所在的终端 $ tty /dev/pts/1 $ who am i ro…

ChatGPT有什么新奇的使用方式?

2023&#xff0c;ChatGPT几乎席卷了所有行业&#xff0c;并且具有不可测量的巨大潜力等着我们去挖掘。 越来越多人对ChatGPT的应用产生兴趣&#xff0c;知乎上“ChatGPT有什么新奇的使用方式&#xff1f;”这一个热门话题的兴起就是最好的证明。 写作&#xff0c;毫无疑问&…

SmartSoftHelp8,数据库字段详细文档自动生成工具

数据库开发文档自动生成 包括数据库设计详细信息&#xff1a; 数据库字段名称&#xff0c;数据类型&#xff0c;大小&#xff0c;是否主键&#xff0c;说明等 一键自动生成开发需求文档 导出html 格式方便查询 下载地址 https://pan.baidu.com/s/1zBgeYsqWnSlNgiKPR2lUYg…

目标检测算法改进系列之添加变核卷积AKConv模块

AKConv变核卷积 KConv的主要思想&#xff1a;AKConv&#xff08;可变核卷积&#xff09;主要提供一种灵活的卷积机制&#xff0c;允许卷积核具有任意数量的参数和采样形状。这种方法突破了传统卷积局限于固定局部窗口和固定采样形状的限制&#xff0c;从而使得卷积操作能够更加…

【iOS控件】—— UIPickerView的使用

【iOS控件】—— UIPickerView的使用 一. 简述UIPickerView1. 什么是UIPickerView2. UIPickerView遵守的协议 二. 测试Demo三. 总结 一. 简述UIPickerView 先看一下UIPickerView的效果图&#xff1a; 1. 什么是UIPickerView UIPickerView是iOS平台上的一个用户界面元素&am…

Excel导入组件的封装以及使用页面点击弹出该弹框

封装的组件 <template><el-dialogwidth"500px"title"员工导入":visible"showExcelDialog"close"$emit(update:showExcelDialog, false)"><el-row type"flex" justify"center"><div class&q…

JVM:强软弱虚四种引用

下面依次解释五种引用 一、强引用 把一个对象赋值给一个引用变量&#xff0c;就相当于把这个对象的强引用放到变量中。 只要对象可达&#xff0c; GC一定不会回收这个对象&#xff08;A1&#xff09; 二、软引用 当一个对象&#xff08;A2&#xff09;没有强引用时&#xff…

python 自主学习笔记

文章目录 前言相关教程模板字符串JavaScriptC#Python 临时变量C#的ValueTuplePython字典 自定义模块化封装的文件路径问题解决方案 暂时结束 前言 最近在学halcon&#xff0c;机器视觉&#xff0c;越学越发现&#xff0c;python是无法避免的语言。因为python用途实在是太广了。…

ChaoJi充电连接装置典型试验案例分析 GB/T 20234.1充电连接装置型式试验变化分析

GB/T 20234.1充电连接装置典型试验变化分析 1、ChaoJi充电连接装置典型试验案例分析 1.1、大功率直流充电接口 1.2、枪线尺寸、重量、面积数据对比 1.3、枪线温升对比试验 1.4、chaoji 枪线温升试验 1.5、chaoji枪线防护等级试验 1.6、GB/T 20234.4项目列表 1.7、小结 ✓ 通…

37. 解数独

题目描述 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请参考示例图&#xff09; …

基于Java SSM框架+Vue实现旅游资源网站项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架Vue实现旅游资源网站演示 摘要 本论文主要论述了如何使用JAVA语言开发一个旅游资源网站 &#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述旅游…

Elasticsearch:什么是向量数据库?

向量数据库定义 向量数据库是将信息存储为向量的数据库&#xff0c;向量是数据对象的数值表示&#xff0c;也称为向量嵌入。 它利用这些向量嵌入的强大功能来对非结构化数据和半结构化数据&#xff08;例如图像、文本或传感器数据&#xff09;的海量数据集进行索引和搜索。 向…

MATLAB Simulink +STM32硬件在环 (HIL)实现例程测试

MATLAB Simulink STM32硬件在环 &#xff08;HIL&#xff09;实现例程测试 &#x1f4cd;相关篇《STM32CubeMxMATLAB Simulink点灯程序》✨本例程没有使用到STM32CubeMX来创建工程&#xff08;在Simulink 中不是选择的STM32xxxbased类型的&#xff09;。 &#x1f516;STM32xxx…

基于SSM的网上手机销售系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

llama.cpp部署通义千问Qwen-14B

llama.cpp是当前最火热的大模型开源推理框架之一&#xff0c;支持了非常多的LLM的量化推理&#xff0c;生态比较完善&#xff0c;是个人学习和使用的首选。最近阿里开源了通义千问大语言模型&#xff0c;在众多榜单上刷榜了&#xff0c;是当前最炙手可热的开源中文大语言模型。…

go语言学习-并发编程(并发并行、线程协程、通道channel)

1、 概念 1.1 并发和并行 并发:具有处理多个任务的能力 (是一个处理器在处理任务)&#xff0c;cpu处理不同的任务会有时间错位&#xff0c;比如有A B 两个任务&#xff0c;某一时间段内在处理A任务&#xff0c;这时A任务需要停止运行一段时间&#xff0c;那么会切换到处理B任…