前言
ota升级是嵌入式应用的一个重要功能,能够让千里之外的用户不需烧录等开发知识就能轻松升级程序。
之前在第一家公司的时候,产品基于stm32, 所有的应用升级都是直接拿的烧录器去烧录,当时还不知道有这个东西,以至于某次产品出产,程序出了问题,没法直接给客户bin文件去解决,只能把产品批量退回来,调试解决完,一个个去重新烧录。。。。。后面我跟面试官去讨论这段经历的时候面试官都是惊呆的。
esp32已经封装好了bootloader,自带wifi功能,esp-idf本身提供了很多例程,包括webserver,配置wifi, 软件加密,文件系统, ota等,很好上手,十分适合做不需要太多引脚的物联网消费应用。
如果要在stm32上实现类似的功能就要自己配置bootloader, 移植lwip等网络栈,会比较麻烦。
在公司就见到这么一个esp32应用, ota, wifi, 文件系统,网页配置一应俱全,正好花点时间把相关的功能熟悉一遍,扩宽技术。
实战
原理
网页ota的升级流程是,点击按钮上传页面,浏览器会向esp32的服务器发送POST请求,里面包含用于升级的文件,esp32服务器接收到这个文件后,经过版本和完整性验证无误后,写入自己的ota分区,然后设置启动分区到这个ota分区,重启,重启完后芯片就会运行在ota分区中写入的程序。
对于ota, esp32本身提供了两个例程: simple_ota和native_ota, 两个都是作为http客户端向服务器下载程序。
区别在于,前者已经把整个从http下载和升级的过程封装在了esp_https_ota
,代码十分简短。
esp_http_client_config_t config = {
.url = CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL, // http://192.168.1.4:8080/blink.bin
...
};
...
esp_https_ota_config_t ota_config = {
.http_config = &config,
};
ESP_LOGI(TAG, "Attempting to download update from %s", config.url);
esp_err_t ret = esp_https_ota(&ota_config);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "OTA Succeed, Rebooting...");
esp_restart();
} else {
ESP_LOGE(TAG, "Firmware upgrade failed");
}
while (1) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
而后者过程就比较详细了,能够访问到ota分区以及bin的数据结构,将bin通过esp_ota_write
写入ota分区之前, 还包括从http循环读取数据,版本验证等一大堆流程,比较繁琐。
我要做的是向服务器上传程序,让esp32接触到这个程序来进行升级做不到上述需求,simple_ota没法接触到bin,做不到这个需求,只有native_ota可以做。
webserver方面,我直接用的esp-idf的**file_serving**例程去改。
配置分区表
Flash大小配置为4MB,分区采用自定义分区,节省空间起见,把factory删掉,让bootloader在ota_0分区启动程序。
分区表如下:
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs,data, nvs, 0x9000, 16K,
otadata, data, ota, 0xd000, 8K,
phy_init, data, phy, 0xf000, 4K,
ota_0, app, ota_0, , 1M,
ota_1, app, ota_1, , 1M,
storage, data, spiffs, , 1M,
弄懂分区表花了不少时间,不需要配的过大,主要看设置的flash大小以及编译出的程序大小,我编译出来的程序大概1M左右,出来的factory默认就是1M,因此ota_0和ota_1都是1M,spiffs我就设置成了1M,加上nvs,otadata和phy_init, 不超过4M。
根据文档,分区表的用到的一些特性记录如下:
- 在使用 OTA 功能时,应用程序应至少拥有 2 个 OTA 应用程序分区(
ota_0
和ota_1
)。 ota
(0) 即 OTA 数据分区 ,用于存储当前所选的 OTA 应用程序的信息。这个分区的大小需要设定为 0x2000(即8K,esp32一个扇区0x1000=4k)。- 如果你希望在 OTA 项目中预留更多 flash,可以删除 factory 分区,转而使用 ota_0 分区。
- 强烈建议为 NVS 分区分配至少 0x3000 字节空间。
取消大文件限制
file_serving例程默认有200kb的大小限制,超过这个限制的文件都无法被上传,需要修改file_server.c配置的文件大小,我这里随便设置到了3MB。
/* Max size of an individual file. Make sure this
* value is same as that set in upload_script.html */
#define MAX_FILE_SIZE (3024*1024) // 3 KB
#define MAX_FILE_SIZE_STR "3MB"
请求头设置
sdkconfig设置Component config → HTTP Server
Max HTTP Request Header Length 从默认512设置成4096,避免出现请求头超过报错的问题。
测试页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tron6000 File Server</title>
<style>
.logo {
font-size: 25px;
text-align: left;
margin-bottom: 10px;
}
.version {
font-size: 15px;
color: gray;
text-align: left;
margin-bottom: 10px;
}
#progressContainer {
margin-top: 10px;
display: none;
}
#uploadProgress {
width: 200px;
height: 20px;
}
#percentage {
margin-left: 10px;