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

news2025/1/17 0:49:22

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 Data
    var 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 Request
        var 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 partition
    if(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 successful
        flash_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.coloradmin.cn/o/1280914.html

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

相关文章

送女朋友一个猜数字小游戏,猜对了会显示爱心(给你心爱的他或她一个惊喜)

起因是我在学习C语言完成老师布置C语言写一个猜数字的作业&#xff0c;突发奇想&#xff0c;能不能在这个猜对了之后弹出一个不一样的页面&#xff0c;然后就试试看能不能实现。基本思路是这样的&#xff1a; 1&#xff1a;先写一个C语言的猜数字的小游戏&#xff0c;在我上个文…

DevEco Studio 调整开发工具中的字体大小与行高

我们打开编辑器 选择 左上角 File 下的 Settings 将左侧菜单栏 编辑 展开 我们在编辑下面 选择 Font 然后 如下图指向的两个位置 我们可以调整它的字体大小和行高 设置好之后 右下角 点击 Apply 应用 然后点击 OK即可 当然 你按着 Ctrl 然后鼠标滚动 也可以像浏览器那样 拉…

Google Colab 现已支持直接使用 transformers 库

Google Colab&#xff0c;全称 Colaboratory&#xff0c;是 Google Research 团队开发的一款产品。在 Colab 中&#xff0c;任何人都可以通过浏览器编写和执行任意 Python 代码。它尤其适合机器学习、数据分析和教育目的。从技术上来说&#xff0c;Colab 是一种托管式 Jupyter …

面试官:说说Vue中Proxy与Object.defineProperty的用法与区别

前言 面试时&#xff0c;我们说完Vue响应式原理&#xff0c;或者Vue2和Vue3的区别时&#xff0c;通常会引出Vue3使用了Proxy来优化响应式&#xff0c;而面试官会继续深挖&#xff1a;说说Proxy与Object.defineProperty的区别。 我们不能只说Proxy直接代理一个对象&#xff0c…

数据结构之二叉树及面试题讲解

&#x1f495;"从前种种譬如昨日死&#xff1b;从后种种譬如今日生"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;数据结构之二叉树及面试题讲解 一.概念 1.树的定义 树是一种非线性的数据结构&#xff0c;是由n个结点组成的一种非线性集合&…

10. 哈希表

哈希表(hash table)&#xff0c;又称散列表&#xff0c;其通过建立键 key 与值 value 之间的映射&#xff0c;实现高效的元素查询。具体而言&#xff0c;我们向哈希表输入一个键 key &#xff0c;则可以在 \(O(1)\) 时间内获取对应的值 value 。 给定 n 个学生&#xff0c;每个…

OpenGL ES入门教程(三)之为平面桌子添加混合色

OpenGL ES入门教程&#xff08;三&#xff09;之为平面桌子添加渐变色 前言零、OpenGL ES实现混合色的原理一、修改绘制的桌子结构1. 三角形扇介绍2. 基于三角形扇结构绘制平面桌子 二、为每个顶点添加颜色属性三、修改着色器1. 顶点着色器2. 片段这色器 四、绘制具有混合颜色的…

Springboot依赖注入时重复初始化Bean的问题

前言 最近做项目&#xff0c;发现了springboot2.7.x在参数initiate的时候可以反复初始化&#xff0c;而且首次异常后&#xff0c;第二次成功居然也可以启动&#xff0c;通过查看源代码发现了问题根源&#xff0c;且在springboot高版本3.x&#xff0c;就出现了了Configuration的…

springmvc+mybatis+mysql8+idea+jqgrid前端

一、背景 主要是为了学习jqgrid前端技术,熟练一下前后端交互数据 二、效果图 访问地址:http://localhost:8080/cr/views/jqGridDemo.jsp 三、代码展示 控制层JqGridController.java @Controller @RequestMapping("/jqgrid") public class JqGridController {pr…

openEuler学习04-ssl升级到openssl-1.1.1w

当前环境ssl的版本是 1.1.1f &#xff0c;计划升级到openssl-1.1.1w [roottest ~]# more /etc/os-release NAME"openEuler" VERSION"20.03 (LTS-SP3)" ID"openEuler" VERSION_ID"20.03" PRETTY_NAME"openEuler 20.03 (LTS-SP3)&q…

Selenium自动化测试:通过cookie绕过验证码的操作

验证码的处理 对于web应用&#xff0c;很多地方比如登录、发帖都需要输入验证码&#xff0c;类型也多种多样&#xff1b;登录/核心操作过程中&#xff0c;系统会产生随机的验证码图片&#xff0c;进行验证才能进行后续操作 ​解决验证码的方法如下&#xff1a; 1、开发做个万…

OpenTelemetry系列 - 第2篇 Java端接入OpenTelemetry

目录 一、架构说明二、方式1 - 自动化2.1 opentelemetry-javaagent.jar&#xff08;Java8 &#xff09;2.2 使用opentelemetry-javaagent.jar完成自动注入2.3 配置opentelemetry-javaagent.jar2.4 使用注解&#xff08;WithSpan, SpanAttribute&#xff09;2.5.1 代码集成WithS…

在Excel中,只需点击几下,就能只复制和粘贴可见单元格

你可以在Excel中隐藏列、行或单元格&#xff0c;以使数据输入或分析更容易。但是&#xff0c;当你复制并粘贴一个包含隐藏单元格的单元格区域时&#xff0c;它们会突然重新出现。 你可能没有意识到&#xff0c;但有一种方法可以只复制和粘贴Microsoft Excel中的可见单元格。只…

SpringMVC常用注解和用法总结

目标&#xff1a; 1. 熟悉使用SpringMVC中的常用注解 目录 前言 1. Controller 2. RestController 3. RequestMapping 4. RequestParam 5. PathVariable 6. SessionAttributes 7. CookieValue 前言 SpringMVC是一款用于构建基于Java的Web应用程序的框架&#xff0c;它通…

FPC和PCB有哪些区别?

现在电子技术越来越先进&#xff0c;CPU可以做到5nm工艺&#xff0c;电路板可以做到几十层&#xff0c;可折叠屏应用多款手机中。 什么是FPC&#xff1f; FPC&#xff1a;Flexible Printed Circuit&#xff0c;柔性电路板&#xff0c;又被称为“软板” FPC 以聚酰亚胺或聚酯薄…

HBASE命令行查看中文字符

问题记录 中文显示的是编码字符不方便查看value\xE5\xB8\xB8\xE5\xAE\x89\xE5\xAE\x891修改前中文显示&#xff1a; 解决方法 1、列族 : 列名 : toString ’2、列族 : 列名 : c(org.apache.hadoop.hbase.util.Bytes).toString ’ scan karry:student,{COLUMNS > [info:…

团队怎么高效制作问卷?

制作调查问卷时并不是一个人就能单独完成&#xff0c;通常情况下&#xff0c;完成一份调查问卷往往需要一个团队的成员参与&#xff0c;相互协作&#xff0c;共同完成。不过&#xff0c;多人协作经常会遇到协作壁垒&#xff0c;导致效率低下&#xff0c;那团队怎么才能高效协作…

K210开发板之VSCode开发环境使用中添加或删除文件(编译失败时)需要注意事项

在最初开始接触&#xff0c;将VScode和编译环境搭载好后&#xff0c;就开始运行第一个程序了&#xff0c;为了后续方便开发测试&#xff0c;这里我自己对照官方提供的例子&#xff0c;自己调试&#xff0c;写了一个简单的文件系统 后续&#xff0c;所有关于开发的源文件都在...…

【无标题】我们只能用成功来摧毁我们,原来的自己只会破败自己的事情。

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

从“AI证件照”到“AI译制片”,爆款AIGC应用的商业化迷思

让郭德纲飙英文、让霉霉说中文的翻译视频生成工具HeyGen和掀起AI证件照热潮的“妙鸭相机”一样&#xff0c;在一阵疯狂刷屏之后&#xff0c;又迅速在各大群里销声匿迹了。 十月份&#xff0c;由HeyGen制作的各种明星跨语言翻译视频&#xff0c;在全网疯传&#xff0c;大家震撼于…