1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
目的
很多时候我们会有因为bug修复、功能增加等情况需要对已投产使用的设备更新固件,这种情况下再使用工具通过串口烧录固件就不是那么方便了,比较常用的是通过网络或SD卡进行固件升级。 在Arduino core for the ESP32中有两个库可以实现固件升级功能: ArduinoOTA 和 Update ,官方例程中用的最多的是 Update ,这篇文章也将以这个库作为说明。 基础说明 ESP32常见的模块都搭载了4M的Flash,但是固件真正可用的空间通常没有那么大,因为Flash往往被划分成了多个区域用作不同用途: 上图就是默认情况下Flash的划分,固件可用区域为1.2M,SPIFFS占用1.5M,还剩约1.3M未使用,这1.3M其实就是用来临时存放用于升级的固件的,参考下面分布: 正常使用时固件和SPIFFS中间是一片空白的区域,当需要升级固件时会将新固件拷贝到这个空白区域中,在下次系统启动时新固件会覆盖旧固件然后运行新固件。 使用演示 在演示固件更新功能前我们首先准备一个用作更新的固件,按下图方式生成固件: 通过SD卡更新固件 从SD卡进行固件升级算是最简单的了,将固件重命名为 update.bin(不是必须的,只要代码中读取的文件名能匹配上就行) ,然后复制到SD卡中,然后按照下文方式连接SD卡: 《使用Arduino开发ESP32(13):SD卡的使用》 完成电路连接后就可以使用下面代码测试了: #include #include void setup() { Serial.begin(115200); Serial.println(); if (!SD_MMC.begin()) //挂载SD卡 { Serial.println("存储卡挂载失败"); return; } File updateBin = SD_MMC.open("/update.bin"); // 读取update.bin文件 if (!updateBin) { Serial.println("读取update.bin失败"); return; } if (updateBin.isDirectory()) { Serial.println("update.bin是一个文件夹"); updateBin.close(); return; } size_t updateSize = updateBin.size(); // 获取固件大小 if (updateSize <= 0) { Serial.println("文件为空"); return; } Serial.println("固件大小为" + String(updateSize) + "字节"); Serial.println("开始固件升级"); if (!Update.begin(updateSize)) // 开始固件升级,检查空可用间大小,如果正在升级或空间不够则返回false { Serial.println("升级不可用"); return; } size_t written = Update.writeStream(updateBin); // 将数据写入到OTA区域 // 使用writeStream方法的话前面Update.begin()必须填写要更新的固件大小 Serial.println("写入" + String(written) + "字节到OTA区域"); if (!Update.end()) // 完成数据写入,设置在系统重启后自动将OTA区域固件移动到Sketch区域 { Serial.println("升级失败 Error #: " + String(Update.getError())); return; } updateBin.close(); // 关闭update.bin文件 //SD_MMC.remove("/update.bin"); // 移除update.bin文件 Serial.println("升级操作完成,模块将在5秒后重启以完成固件升级"); delay(5000); ESP.restart(); // 重启设备 } void loop() { } 上面演示中代码看着蛮多行,大多是都是文件读取、检测的代码,真正固件更新的操作就 Update.begin() 、 Update.writeStream() 、 Update.end() 这几步。 通过网页更新固件 通过网页更新也不复杂,和上面通过SD卡升级需要涉及SD卡使用相关内容一样,通过网页更新需要涉及网页和服务器相关内容,可以参考下面文章: 《使用Arduino开发ESP32(09):WebServer使用演示与说明》 使用下面代码可以测试通过网页进行更新固件的操作: #include #include #include const char *ssid = "********"; const char *password = "********"; WebServer server(80); // 以下为网页文件 String indexhtml = String("") + "n" + "n" + " n" + " "n" + "n" + " n" + "n"; bool shouldreboot = false; // 重启标志,固件升级操作完成后设置该标志准备重启设备 void handleResponse() //回调函数 { server.sendHeader("Connection", "close"); server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK"); } void handleFileupload() //回调函数 { HTTPUpload &upload = server.upload(); // 文件上传对象 if (upload.status == UPLOAD_FILE_START) // 文件上传开始 { Serial.printf("开始上传文件: %sn", upload.filename.c_str()); if (!Update.begin()) // 开始固件升级,检查空可用间大小,如果正在升级或空间不够则返回false { Update.printError(Serial); } } else if (upload.status == UPLOAD_FILE_WRITE) // 文件读写中 { if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) // 将文件数据写入到OTA区域 { Update.printError(Serial); } } else if (upload.status == UPLOAD_FILE_END) // 文件上传完成 { Serial.println("写入" + String(upload.totalSize) + "字节到OTA区域"); if (!Update.end(true)) // 完成数据写入,设置在系统重启后自动将OTA区域固件移动到Sketch区域 // Update.begin不指定大小时这里设置true { Update.printError(Serial); } Serial.println("升级操作完成,模块将在5秒后重启以完成固件升级"); shouldreboot = true; } else { Serial.printf("固件上传失败: status=%dn", upload.status); } } void setup() { Serial.begin(115200); Serial.println(); WiFi.mode(WIFI_STA); WiFi.setSleep(false); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("Connected"); Serial.print("IP Address:"); Serial.println(WiFi.localIP()); server.on("/", HTTP_GET, []() { server.sendHeader("Connection", "close"); server.send(200, "text/html", indexhtml); // 发送网页 }); server.on("/update", HTTP_POST, handleResponse, handleFileupload); // 绑定回调函数 server.begin(); //启动服务器 Serial.println("Web server started"); } void loop() { server.handleClient(); //处理来自客户端的请求 if (shouldreboot) { delay(5000); ESP.restart(); // 重启设备 } } 上面演示中代码看着也蛮多行,大多是都是网页文本、回调函数、网络连接等代码,真正固件更新的操作就 Update.begin() 、 Update.write() 、 Update.end() 这几步。和上一个例子不同的是这里使用Update.begin()时未指定需求空间的大小,所以在后面Update.end()中指定了true参数。 |
|
|
|
只有小组成员才能发言,加入小组>>
3310 浏览 9 评论
2991 浏览 16 评论
3492 浏览 1 评论
9055 浏览 16 评论
4086 浏览 18 评论
1174浏览 3评论
603浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
596浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2333浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1894浏览 2评论
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-21 20:23 , Processed in 1.085276 second(s), Total 78, Slave 59 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号