基于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都会导致该字段长度偏大的目前原因还不得而知。

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

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

相关文章

2.3 主程序和外部IO交互 (文件映射方式)----IO Server实现

2.3 主程序和外部IO交互 &#xff08;文件映射方式&#xff09;----IO Server C实现 效果显示 1 内存共享概念 基本原理&#xff1a;以页面为单位&#xff0c;将一个普通文件映射到内存中&#xff0c;达到共享内存和节约内存的目的&#xff0c;通常在需要对文件进行频繁读写时…

手写一个类似@RequestParam的注解(用来接收请求体的参数)

一、本文解决的痛点 按照大众认为的开发规范&#xff0c;一般post类型的请求参数应该传在请求body里面。但是我们有些post接口只需要传入一个字段&#xff0c;我们接受这种参数就得像下面这样单独创建一个类&#xff0c;类中再添加要传入的基本类型字段&#xff0c;配合Reques…

LLM指令微调Prompt的最佳实践(二):Prompt迭代优化

文章目录 1. 前言2. Prompt定义3. 迭代优化——以产品说明书举例3.1 产品说明书3.2 初始Prompt3.3 优化1: 添加长度限制3.4 优化2: 细节纠错3.5 优化3: 添加表格 4. 总结5. 参考 1. 前言 前情提要&#xff1a; 《LLM指令微调Prompt的最佳实践&#xff08;一&#xff09;&#…

nexus未开启匿名访问Anonymous Access,访问maven元数据maven-metadata,报401未授权Unauthorized错误

一、背景 下午在调试nexus的时候&#xff0c;其他同事不小心把匿名访问停用了&#xff0c;导致客户端android打包的时候&#xff0c;报错&#xff1a; Received status code 401 from server: Unauthorized。 访问http://192.168.xx.xx:8081/repository/public/com/xxx/xxxcor…

【软件测试】单元测试、系统测试、集成测试详解

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、单元测试的概念 单元测试是对软件基本组成单元进行的测试&#xff0c;如函数或一个类的方法…

PCL 点云最小图割(前景、背景点云提取)

点云最小图割 一、概述1.1 概念1.2 算法原理二、代码示例三、运行结果🙋 结果预览 一、概述 1.1 概念 最小图割算法(pcl::MinCutSegmentation):是一种基于图论的对象分割方法,主要用于点云数据的处理和分析。该算法将点云数据表示为一个图结构,其中点云中的点作为图的节…

【Java】微博系统设计:怎么应对热点事件的突发访问压力?

一、问题解析 微博&#xff08;microblog&#xff09;是一种允许用户即时更新简短文本&#xff08;比如140个字符&#xff09;&#xff0c;并可以公开发布的微型博客形式。今天我们就来开发一个面向全球用户、可以支持10亿级用户体量的微博系统&#xff0c;系统名称为“Weitte…

不同系统间数据交换要通过 api 不能直接数据库访问

很多大数据开发提供数据给外部系统直接给表结构&#xff0c;这是不好的方式。在不同系统间进行数据交换时&#xff0c;通过API&#xff08;应用程序编程接口&#xff09;而非直接访问数据库是现代系统集成的一种最佳实践。 目录 为什么要通过API进行数据交换如何通过API进行数据…

UG NX二次开发(C#)-根据草图创建拉伸特征(UFun+NXOpen)

文章目录 1、前言2、在UG NX中创建草图,然后创建拉伸特征3、基于UFun函数的实现4、基于NXOpen的实现代码1、前言 UG NX是基于特征的三维建模软件,其中拉伸特征是一个很重要的特征,有读者问如何根据草图创建拉伸特征,我在这篇博客中讲述一下草图创建拉伸特征的UG NX二次开发…

分布式链路追踪Micrometer Tracing和ZipKin基础入门与实践

【1】概述 在分布式与微服务场景下&#xff0c;我们需要解决如下问题&#xff1a; 在大规模分布式与微服务集群下&#xff0c;如何实时观测系统的整体调用链路情况。 在大规模分布式与微服务集群下&#xff0c;如何快速发现并定位到问题。 在大规模分布式与微服务集群下&…

AI网络爬虫006:从当当网批量获取图书信息

文章目录 一、目标二、输入内容三、输出内容一、目标 用户输入一个图书名称,然后程序自动从当当网批量获取图书信息 查看相关元素在源代码中的位置: 二、输入内容 第一步:在deepseek中输入提示词: 你是一个Python爬虫专家,一步步的思考,完成以下网页爬取的Python脚本任…

法制史学习笔记(个人向) Part.3

5. 三国两晋南北朝法律制度 以下为三国魏晋南北朝直到唐代的历史发展脉络图&#xff1a; #mermaid-svg-6AVVMjllKTBaBbRO {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-6AVVMjllKTBaBbRO .error-icon{fill:#55222…

【Python实战因果推断】14_线性回归的不合理效果4

目录 Debiasing Step Denoising Step Standard Error of the Regression Estimator Debiasing Step 回想一下&#xff0c;最初由于混杂偏差&#xff0c;您的数据看起来是这样的、 随着信贷额度的增加&#xff0c;违约率呈下降趋势&#xff1a; 根据 FWL 定理&#xff0c;您可…

c文件读写

格式输入输出 文件指针 FILE *pf 文件打开 fopen("文件名",打开方式)  文件名&#xff1a;普通字符串 打开方式&#xff1a;读、写、文本文件、二进制文件 rt、wt、at、rb、wb、ab、rt、wt、at、rb、wb、ab (r 为读&#xff0c;w 为写&#xff0c; 为读写&…

解锁机器学习潜力的钥匙:深度剖析交叉验证集的应用与魅力

一、为何需要交叉验证集 在构建机器学习模型时&#xff0c;我们通常会面临一个关键问题&#xff1a;如何确保模型在新数据上的表现与在训练数据上一样出色&#xff1f;这涉及到模型的泛化能力——即模型对未见过的数据做出准确预测的能力。传统的训练集/测试集划分方法虽然简单…

亿纬锂能社招入职通用职业能力测评大易题库及薪资待遇

一、亿纬锂能薪资待遇 1. **平均工资**&#xff1a;根据职朋职业圈的数据&#xff0c;惠州亿纬锂能股份有限公司的平均工资为10924元/月。网易新闻的报道则提到&#xff0c;亿纬锂能的月收入平均值为16598元。 2. **工资区间**&#xff1a;在亿纬锂能&#xff0c;工资收入有多…

使用dot来画流程图

Dot是一种图形描述语言&#xff0c;属于Graphviz软件的一部分。Graphviz是一个用于可视化图形&#xff08;图表、网络图等&#xff09;的开源工具集。使用Dot语言&#xff0c;你可以创建并描述节点和边&#xff0c;从而生成图形。以下是如何使用Dot语言画图的基本步骤&#xff…

【CSAPP】-attacklab实验

目录 实验目的与要求 实验原理与内容 实验设备与软件环境 实验过程与结果&#xff08;可贴图&#xff09; 实验总结 实验目的与要求 1. 强化机器级表示、汇编语言、调试器和逆向工程等方面基础知识&#xff0c;并结合栈帧工作原理实现简单的栈溢出攻击&#xff0c;掌握其基…

游游的水果大礼包(枚举)

题目链接&#xff1a;https://ac.nowcoder.com/acm/problem/255193 题解 题目解析 就拿第一个例子来看&#xff0c;当选择组成1个一号礼包和1个二号礼包时最大的价值是3元&#xff0c;而选择2个二号礼包时&#xff0c;最大的价值是4元&#xff0c;因此选择2个二号礼包。 算法…