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

基于ESP32 IDF的WebServer实现以及OTA固件升级实现记录(三)

          经过前面两篇的前序铺垫,对webserver以及restful api架构有了大体了解后本篇描述下最终的ota实现的代码以及调试中遇到的诡异bug。

         eps32的实际ota实现过程其实esp32官方都已经基本实现好了,我们要做到无非就是把要升级的固件搬运到对应ota flash分区里面,相对来说esp32 ota已经很完善了。esp32的ota相关接口可参见官网的相关ota文档。本文主要讲解基于http post方式的本地webserver的ota实现。

        1、网页端web实现,主要完成固件选择以及上传post功能,该部分可以参考开源web,如esp32-vue3demo/vue-project/src/views/Upgrade.vue at main · lbuque/esp32-vue3demo · GitHub

<template><form method='POST' action='/api/v1/update' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form>
</template><script lang="ts" setup ></script><style scoped lang="scss"></style>

如上代码的效果如下,主要是有一个文件选择框可以选择要升级的固件,点击update后即会向web后台url:/api/v1/updata进行post请求传输要升级的固件给运行webserver的后台即esp32,该部分即完成了待升级固件的网络传输。

        2、esp32 webserver的post文件请求接收以及实际的ota flash写入。

           ota过程需要先找到要写入的ota分区,然后接收文件后按esp32的ota api写入ota分区即可,文件传输完成后即可启动esp32进行固件升级。升级代码如下:

/* URI handler for light brightness control */httpd_uri_t light_brightness_post_uri = {.uri = "/api/v1/update",.method = HTTP_POST,.handler = light_brightness_post_handler,.user_ctx = rest_context};httpd_register_uri_handler(server, &light_brightness_post_uri);

注册一个用于接收post文件的rest api,回调名懒得改,实际项目中按编码要求进行修改。

static esp_err_t light_brightness_post_handler(httpd_req_t *req)
{esp_err_t err;/* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */esp_ota_handle_t update_handle = 0 ;const esp_partition_t *update_partition = NULL;char filepath[FILE_PATH_MAX];ESP_LOGI(REST_TAG, "light_brightness_post_handler");/* Skip leading "/upload" from URI to get filename *//* Note sizeof() counts NULL termination hence the -1 */const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path,req->uri + sizeof("/upload") - 1, sizeof(filepath));ESP_LOGI(REST_TAG, "Receiving file : %s...", filename);/* Retrieve the pointer to scratch buffer for temporary storage */char *buf = ((struct file_server_data *)req->user_ctx)->scratch;int received;/* Content length of the request gives* the size of the file being uploaded */int remaining = req->content_len;while (remaining > 0) {if(remaining == req->content_len){update_partition = esp_ota_get_next_update_partition(NULL);assert(update_partition != NULL);ESP_LOGI(REST_TAG, "Writing to partition subtype %d at offset 0x%"PRIx32,update_partition->subtype, update_partition->address);err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);if (err != ESP_OK) {ESP_LOGE(REST_TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));esp_ota_abort(update_handle);}ESP_LOGI(REST_TAG, "esp_ota_begin succeeded");}ESP_LOGI(REST_TAG, "Remaining size : %d", remaining);/* Receive the file part by part into a buffer */if ((received = httpd_req_recv(req, buf, MIN(remaining, SCRATCH_BUFSIZE))) <= 0) {if (received == HTTPD_SOCK_ERR_TIMEOUT) {/* Retry if timeout occurred */continue;}/* In case of unrecoverable error,* close and delete the unfinished file*/ESP_LOGE(REST_TAG, "File reception failed!");/* Respond with 500 Internal Server Error */httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file");return ESP_FAIL;}err = esp_ota_write( update_handle, buf, received);if (err != ESP_OK) {esp_ota_abort(update_handle);}/* Keep track of remaining size of* the file left to be uploaded */remaining -= received;ESP_LOGI(REST_TAG, " remaining = %d, received = %d", remaining, received);}/* Close file upon upload completion */ESP_LOGI(REST_TAG, "File reception complete");/* Redirect onto root to see the updated file list */httpd_resp_set_status(req, "303 See Other");httpd_resp_set_hdr(req, "Location", "/");
#ifdef CONFIG_EXAMPLE_HTTPD_CONN_CLOSE_HEADERhttpd_resp_set_hdr(req, "Connection", "close");
#endifhttpd_resp_sendstr(req, "File uploaded successfully");ESP_LOGI(REST_TAG, "======endota======");err = esp_ota_end(update_handle);if (err != ESP_OK) {if (err == ESP_ERR_OTA_VALIDATE_FAILED) {ESP_LOGE(REST_TAG, "Image validation failed, image is corrupted");}ESP_LOGE(REST_TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err));}err = esp_ota_set_boot_partition(update_partition);if (err != ESP_OK) {ESP_LOGE(REST_TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err));}ESP_LOGI(REST_TAG, "Prepare to restart system!");esp_restart();return ESP_OK;}

        大体代码的含义就是接收post请求后持续读取文件知道传输完成,同时传输过程中写ota分区的flash。

        实际按如上代码进行build后即可进行ota升级。运行起来一切都ok,不过没有意外下还是出现了意外,web页面选择固件进行升级后esp32端接收到的bin文件总是格式不对,出现了如下错误,

错误提示传输的文件不是bin文件格式,因此怀疑是post传输问题,通过web的开发者工具查看post的文件大小果真和实际文件大小不一致,

此时还没法确定是esp32嵌入式端代码问题还是web端前端代码问题,后面找了curl工具,

直接用命令行接口进行post试了后可以正常升级,post过来的大小也是实际的文件大小。至此基本可以确认是前端web页面 post的时候自己传输头等有修改了content-length导致其比实际的bin文件更大导致esp32接收的bin文件错误。

经过后面修改发现只有用原始的xmlrequest才不会像html或者axios那样会导致传输的content-length会比实际的bin文件大从而导致ota固件升级失败的问题。

改善后的web端的post关键代码如下:

 upload() {// your upload logic here using XMLHttpRequestconst uploadPath = "/upload/" + this.filePath;const fileInput = document.getElementById("newfile").files;// add your upload logic using XMLHttpRequest here// be sure to use "this.filePath" and "fileInput" from Vue data// Exampleconst file = fileInput[0];const xhttp = new XMLHttpRequest();xhttp.onreadystatechange = function() {if (xhttp.readyState == 4) {if (xhttp.status == 200) {document.open();document.write(xhttp.responseText);document.close();} else if (xhttp.status == 0) {alert("Server closed the connection abruptly!");location.reload();} else {alert(xhttp.status + " Error!\n" + xhttp.responseText);location.reload();}}};xhttp.open("POST", "/api/v1/update", true);xhttp.send(file);}

webserver ota传输升级没问题的log截图

esp32 的webserver ota的源代码放github上,有需要的自行取用,iot-lorawan (iot-lorawan) · GitHub

关于web端为何用xtmlrequest进行post的时候content-length才能是正确的bin文件大小,而axios或者html自带的post都会导致该字段长度偏大的目前原因还不得而知。

相关文章:

基于ESP32 IDF的WebServer实现以及OTA固件升级实现记录(三)

经过前面两篇的前序铺垫&#xff0c;对webserver以及restful api架构有了大体了解后本篇描述下最终的ota实现的代码以及调试中遇到的诡异bug。 eps32的实际ota实现过程其实esp32官方都已经基本实现好了&#xff0c;我们要做到无非就是把要升级的固件搬运到对应ota flash分区里面…...

116-基于5VLX110T FPGA FMC接口功能验证6U CPCI平台

一、板卡概述 本板卡是Xilinx公司芯片V5系列芯片设计信号处理板卡。由一片Xilinx公司的XC5VLX110T-1FF1136 / XC5VSX95T-1FF1136 / XC5VFX70T-1FF1136芯片组成。FPGA接1片DDR2内存条 2GB&#xff0c;32MB Nor flash存储器&#xff0c;用于存储程序。外扩 SATA、PCI、PCI expres…...

Android - Json/Gson

Json数据解析 json对象&#xff1a;花括号开头和结尾&#xff0c;中间是键值对形式————”属性”:属性值”” json数组&#xff1a;中括号里放置 json 数组&#xff0c;里面是多个json对象或者数字等 JSONObject 利用 JSONObject 解析 1.创建 JSONObject 对象&#xff0c;传…...

盲信号处理的发展现状

盲源分离技术最早在上个世纪中期提出&#xff0c;在1991年Herault和Jutten提出基于反馈神经网络的盲源分离方法&#xff0c;但该方法缺乏理论基础&#xff0c;后来Tong和Liu分析了盲源分离问题的可辨识性和不确定性&#xff0c;Cardoso于1993年提出了基于高阶统计的联合对角化盲…...

二轴机器人装箱机:重塑物流效率,精准灵活,引领未来装箱新潮流

在现代化物流领域&#xff0c;高效、精准与灵活性无疑是各大企业追求的核心目标。而在这个日益追求自动化的时代&#xff0c;二轴机器人装箱机凭借其较佳的性能和出色的表现&#xff0c;正逐渐成为装箱作业的得力助手&#xff0c;引领着未来装箱新潮流。 一、高效&#xff1a;重…...

使用python做飞机大战

代码地址: 点击跳转...

Python面向对象编程:派生

本套课在线学习视频&#xff08;网盘地址&#xff0c;保存到网盘即可免费观看&#xff09;&#xff1a; ​​https://pan.quark.cn/s/69d1cc25d4ba​​ 面向对象编程&#xff08;OOP&#xff09;是一种编程范式&#xff0c;它通过将数据和操作数据的方法封装在一起&#xff0…...

华为仓颉编程语言

目录 一、引言 二、仓颉编程语言概述 三、技术特征 四、应用场景 五、社区支持 六、结论与展望 一、引言 随着信息技术的快速发展&#xff0c;编程语言作为软件开发的核心工具&#xff0c;其重要性日益凸显。近年来&#xff0c;华为公司投入大量研发资源&#xff0c;成功…...

【微信小程序开发实战项目】——如何制作一个属于自己的花店微信小程序(2)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…...

解锁数据资产的无限潜能:深入探索创新的数据分析技术,挖掘其在实际应用场景中的广阔价值,助力企业发掘数据背后的深层信息,实现业务的持续增长与创新

目录 一、引言 二、创新数据分析技术的发展 1、大数据分析技术 2、人工智能与机器学习 3、可视化分析技术 三、创新数据分析技术在实际应用场景中的价值 1、市场洞察与竞争分析 2、客户细分与个性化营销 3、业务流程优化与风险管理 4、产品创新与研发 四、案例分析 …...

Bridging nonnull in Objective-C to Swift: Is It Safe?

Bridging nonnull in Objective-C to Swift: Is It Safe? In the world of iOS development, bridging between Objective-C and Swift is a common practice, especially for legacy codebases (遗留代码库) or when integrating (集成) third-party libraries. One importa…...

算法训练 | 图论Part1 | 98.所有可达路径

目录 98.所有可达路径 深度搜索法 98.所有可达路径 题目链接&#xff1a;98. 所有可达路径 文章讲解&#xff1a;代码随想录 深度搜索法 代码一&#xff1a;邻接矩阵写法 #include <iostream> #include <vector> using namespace std; vector<vector<…...

【JVM基础篇】垃圾回收

文章目录 垃圾回收常见内存管理方式手动回收&#xff1a;C内存管理自动回收(GC)&#xff1a;Java内存管理自动、手动回收优缺点 应用场景垃圾回收器需要对哪些部分内存进行回收&#xff1f;不需要垃圾回收器回收需要垃圾回收器回收 方法区的回收代码测试手动调用垃圾回收方法Sy…...

Spark join数据倾斜调优

Spark中常见的两种数据倾斜现象如下 stage部分task执行特别慢 一般情况下是某个task处理的数据量远大于其他task处理的数据量&#xff0c;当然也不排除是程序代码没有冗余&#xff0c;异常数据导致程序运行异常。 作业重试多次某几个task总会失败 常见的退出码143、53、137…...

YOLOv5初学者问题——用自己的模型预测图片不画框

如题&#xff0c;我在用自己的数据集训练权重模型的时候&#xff0c;在训练完成输出的yolov5-v5.0\runs\train\exp2目录下可以看到&#xff0c;在训练测试的时候是有输出描框的。 但是当我引用训练好的best.fangpt去进行预测的时候&#xff0c; 程序输出的图片并没有描框。根据…...

【linux学习---1】点亮一个LED---驱动一个GPIO

文章目录 1、原理图找对应引脚2、IO复用3、IO配置4、GPIO配置5、GPIO时钟使能6、总结 1、原理图找对应引脚 从上图 可以看出&#xff0c; 蜂鸣器 接到了 BEEP 上&#xff0c; BEEP 就是 GPIO5_IO05 2、IO复用 查找IMX6UL参考手册 和 STM32一样&#xff0c;如果某个 IO 要作为…...

Redis分布式锁代码实现详解

引言 在分布式系统中&#xff0c;资源竞争和数据一致性问题常常需要通过锁机制来解决。Redis作为一个高性能的键值存储系统&#xff0c;因其提供的原子操作、丰富的数据结构以及网络延迟低等特点&#xff0c;成为了实现分布式锁的理想选择。本文将详细介绍如何使用Redis来实现…...

Day01-02-gitlab

Day01-02-gitlab 1. 什么是gitlab2. Gitlab vs Github/Gitee3. Gitlab 应用场景4. 架构5. Gitlab 快速上手指南5.0 安装要求5.1 安装Gitlab组件5.3 配置访问url5.6 初始化5.8 登录与查看5.9 汉化5.10 设置密码5.11 目录结构5.12 删除5.13 500 vs 5025.14 重置密码 6. Gitlab用户…...

PyCharm远程开发配置(2024以下版本)

目录 PyCharm远程开发配置 1、清理远程环境 1.1 点击Setting 1.2 进入Interpreter 1.3 删除远程环境 1.4 删除SSH 2、连接远程环境 2.1 点击Close Project 2.2 点击New Project 2.3 项目路径设置 2.4 SSH配置 2.5 选择python3解释器在远程环境的位置 2.6 配置远程…...

解决Ucharts在小程序上的层级过高问题

<qiun-wx-ucharts canvas2d"{{true}}" type"pie" opts"{{rectificationRateOpts}}" chartData"{{rectificationRateData}}" /> 开启2d渲染即可解决&#xff08;在小程序开发工具上看着层级还是高&#xff0c;但是在手机上是正常…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

centos 7 部署awstats 网站访问检测

一、基础环境准备&#xff08;两种安装方式都要做&#xff09; bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats&#xff0…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权

摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题&#xff1a;安全。文章将详细阐述认证&#xff08;Authentication) 与授权&#xff08;Authorization的核心概念&#xff0c;对比传统 Session-Cookie 与现代 JWT&#xff08;JS…...

Neko虚拟浏览器远程协作方案:Docker+内网穿透技术部署实践

前言&#xff1a;本文将向开发者介绍一款创新性协作工具——Neko虚拟浏览器。在数字化协作场景中&#xff0c;跨地域的团队常需面对实时共享屏幕、协同编辑文档等需求。通过本指南&#xff0c;你将掌握在Ubuntu系统中使用容器化技术部署该工具的具体方案&#xff0c;并结合内网…...

Monorepo架构: Nx Cloud 扩展能力与缓存加速

借助 Nx Cloud 实现项目协同与加速构建 1 &#xff09; 缓存工作原理分析 在了解了本地缓存和远程缓存之后&#xff0c;我们来探究缓存是如何工作的。以计算文件的哈希串为例&#xff0c;若后续运行任务时文件哈希串未变&#xff0c;系统会直接使用对应的输出和制品文件。 2 …...