目录
1.创建产品和设备
2.准备工作
2.1 获取基础工程
2.2 基本知识概述
2.2.1 OTA升级流程
2.2.2 主题和数据格式
(1)设备上报版本号
①请求主题(设备 -> 阿里云):
②响应主题(阿里云->设备):
(2)阿里云推送升级包信息
①请求主题(阿里云 -> 设备):
② 响应主题(设备 -> 阿里云):
(3)设备上报升级进度
①请求主题(设备 -> 阿里云):
②响应主题(阿里云->设备):
3.编写、修改代码
3.1 重新配置分区
(1)配置分区文件
3.2 编写OTA cJSCON代码
3.2.1 生成主题
3.2.2 设备上报版本号
(1)调用设备上报版本号函数
(2)编写设备上报版本号函数
①增加上报版本号的固定cJSON对象
②向参数中添加版本号对象
(3)编写获取当前版本号函数
(4)设置版本号
3.2.4 获取升级包信息
(1)订阅主题
(2)解析收到的cJSON格式数据
3.2.3 设备上报升级进度
(1)调用设备上报升级进度函数
(2)编写设备上报升级进度函数
①增加上报版本号的固定cJSON对象
②向参数中添加版本号对象
3.3 编写OTA 任务代码
3.3.1 新建文件文组
3.3.2 编写OTA任务
(1)创建OTA任务
(2)编写OTA任务函数
3.3.3 完整代码
4.验证功能
(1)生成升级文件
(2)烧录初始代码
(3)升级文件上传到阿里云
(4)验证升级包
(5)查看升级结果
5.注意事项
6.参考文档
7.源码下载
1.创建产品和设备
参考ESP32接入阿里云物联网平台(MQTT-TLS连接通信),基于VSCode环境下的ESP-IDF开发(附源码)-CSDN博客
2.准备工作
2.1 获取基础工程
从https://download.csdn.net/download/Freddy_Ssc/90628093?spm=1001.2014.3001.5503下载基础工程。
2.2 基本知识概述
2.2.1 OTA升级流程
(1)设备上报版本号
(2)阿里云推送升级包信息(或设备主动拉取升级包信息,二选一,本实验选择前者)
推送的消息中包含了固件的下载地址"url",获取到下载地址后,调用esp_https_ota函数启动升级,函数使用HTTPS协议下载升级包,自动完成升级。
(3)设备上报升级进度(此处只有成功或失败)
(4)升级完成后重启设备,设备上报版本号(同步骤(1))
2.2.2 主题和数据格式
(1)设备上报版本号
①请求主题(设备 -> 阿里云):
/ota/device/inform/${YourProductKey}/${YourDeviceName}
数据格式示例:
{"id": "123", //每个消息ID在当前设备中具有唯一性"params": {"version": "1.0.1", //OTA模块版本"module": "MCU" //OTA模块名,该参数如果不填,表示默认版本号default}
}
②响应主题(阿里云->设备):
/sys/{productKey}/{deviceName}/thing/ota/firmware/get_reply
(2)阿里云推送升级包信息
①请求主题(阿里云 -> 设备):
/ota/device/upgrade/${YourProductKey}/${YourDeviceName}
数据格式示例(通过HTTPS协议单文件下载):
{"id": "123","code": 200,"data": {"size": 93796291,"sign": "f8d85b250d4d787a9f483d89a974***","version": "10.0.1.9.20171112.1432","isDiff": 1,"url": "https://the_firmware_url","signMethod": "MD5","md5": "f8d85b250d4d787a9f48***","module": "MCU","extData":{"key1":"value1","key2":"value2","_package_udi":"{\"ota_notice\":\"升级底层摄像头驱动,解决视频图像模糊的问题。\"}"}}
}
② 响应主题(设备 -> 阿里云):
无。
(3)设备上报升级进度
①请求主题(设备 -> 阿里云):
/ota/device/progress/${YourProductKey}/${YourDeviceName}
数据格式:
- 升级成功示例:
{"id": "123","params": {"step": "0","desc": "OTA升级成功!","module": "MCU"}
}
- 升级失败示例:
{"id": "123","params": {"step": "-1","desc": "OTA升级失败,请求不到升级包信息。","module": "MCU"}
}
②响应主题(阿里云->设备):
无。
3.编写、修改代码
3.1 重新配置分区
(1)配置分区文件
从\Espressif\frameworks\esp-idf-v5.3.2\components\partition_table路径中复制一个partitions_two_ota.csv文件到工程路径,并将factory、ota_0、ota_1三者的size改成合适大小,如下所示:
# 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, , 0x4000,
otadata, data, ota, , 0x2000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 1200K,
ota_0, app, ota_0, , 1200K,
ota_1, app, ota_1, , 1200K,
(2)配置menuconfig文件
方式1:通过ESP-IDF配置
①执行命令idf.py menuconfig -> Serial flasher config -> Flash size -> 选择 (X) 16MB -> 按s保存;(根据实际情况选择flash大小)
②执行命令idf.py menuconfig -> Partition Table -> Partition Table -> Custom partition table CSV -> ../ -> (partitions.csv) Custom partition CSV file -> 将名称改为 partitions_two_ota.csv -> 按s保存
方式2:通过VSCode配置
3.2 编写OTA cJSCON代码
3.2.1 生成主题
以宏定义方式生成三个OTA相关的主题:
//OTA Topic
#define ALIOT_OTA_TOPIC_UPLOAD_VERSION "/ota/device/inform/"ALIOT_PRODUCTKEY"/"ALIOT_DEVICENAME //上报版本号
#define ALIOT_OTA_TOPIC_GET_PACKAGE "/ota/device/upgrade/"ALIOT_PRODUCTKEY"/"ALIOT_DEVICENAME //下发升级包信息
#define ALIOT_OTA_TOPIC_UPLOAD_PROGRESS "/ota/device/progress/"ALIOT_PRODUCTKEY"/"ALIOT_DEVICENAME //上报升级进度
3.2.2 设备上报版本号
(1)调用设备上报版本号函数
(2)编写设备上报版本号函数
void aliot_post_ota_version(const char *version)
{//创建物模型描述结构体ALIOT_DM_DES *dm = aliot_malloc_des(ALIOT_OTA_VERSION);//设置物模型结构体里面的属性值aliot_set_ota_version(dm, version);//将生成cJSON的对象转换为字符串aliot_dm_serialize(dm);//发布属性消息ESP_LOGI(TAG, "publish payload:%s", dm->dm_js_str);esp_mqtt_client_publish(mqtt_handle, ALIOT_OTA_TOPIC_UPLOAD_VERSION, dm->dm_js_str, strlen(dm->dm_js_str), 1, 0);//释放物模型描述结构体aliot_dm_free(dm);
}
①增加上报版本号的固定cJSON对象
②向参数中添加版本号对象
//设置OTA版本号
void aliot_set_ota_version(ALIOT_DM_DES *dm, const char *version)
{if (dm){cJSON* params_js = cJSON_GetObjectItem(dm->dm_js, "params");//获取对象paramsif (params_js){cJSON_AddStringToObject(params_js, "version", version);//向params对象中添加属性值}}
}
(3)编写获取当前版本号函数
//获取当前应用版本号
const char* get_app_version(void)
{static char app_version[32] = {0};if (app_version[0] == 0){//获取当前分区的基本信息const esp_partition_t* running = esp_ota_get_running_partition();//获取更多的分区信息esp_app_desc_t running_desc;esp_ota_get_partition_description(running, &running_desc);snprintf(app_version, sizeof(app_version), "%s", running_desc.version);}return app_version;
}
(4)设置版本号
在工程根目录的cMakeList.txt中加入set(PROJECT_VER "1.0.1") 设置当前版本号:
3.2.4 获取升级包信息
(1)订阅主题
在MQTT事件回调函数中当设备连接MQTT服务器后,订阅阿里云推送跟新文件信息的主题:
(2)解析收到的cJSON格式数据
在MQTT事件回调函数中当收到MQTT数据事件后,解析cJSON格式数据,如果是阿里云推送的升级包信息,则获取信息中升级包的https下载地址,同时注册升级进度上报函数,并启动OTA升级任务:
3.2.3 设备上报升级进度
(1)调用设备上报升级进度函数
上述3.2.2的步骤(2)中注册了上报升级进度的函数,具体调用时间后面再讲。
(2)编写设备上报升级进度函数
void aliot_post_ota_progress(int code)
{//创建物模型描述结构体ALIOT_DM_DES *dm = aliot_malloc_des(ALIOT_OTA_PROGRESS);//设置物模型结构体里面的属性值aliot_set_ota_progress(dm, code);//将生成cJSON的对象转换为字符串aliot_dm_serialize(dm);//发布消息上报进度ESP_LOGI(TAG, "publish payload:%s", dm->dm_js_str);esp_mqtt_client_publish(mqtt_handle, ALIOT_OTA_TOPIC_UPLOAD_PROGRESS, dm->dm_js_str, strlen(dm->dm_js_str), 1, 0);//释放物模型描述结构体aliot_dm_free(dm);
}
①增加上报版本号的固定cJSON对象
②向参数中添加版本号对象
//设置OTA进度 注意:升级进度只能是0(成功)或-1(失败)
void aliot_set_ota_progress(ALIOT_DM_DES *dm, int progress)
{if (dm){cJSON* params_js = cJSON_GetObjectItem(dm->dm_js, "params");//获取对象paramsif (params_js){char str_progress[10] = {0};snprintf(str_progress, sizeof(str_progress), "%d", progress);cJSON_AddStringToObject(params_js, "step", str_progress);//向params对象中添加属性值if (progress == 0)cJSON_AddStringToObject(params_js, "desc", "OTA升级成功!");else// if(code == -1)cJSON_AddStringToObject(params_js, "desc", "OTA升级失败!请求不到升级信息包。");}}
}
3.3 编写OTA 任务代码
3.3.1 新建文件文组
在工程目录下创建一个components文件夹,再在此文件夹下创建一个ota文件夹,然后创建aliot_ota.c、aliot_ota.h、cMakeList.txt文件,并在cMakeList.txt中添加以下代码,将组件注册到工程中:
#把组件注册到工程中
idf_component_register(SRCS "aliot_ota.c" #源文件INCLUDE_DIRS "." #路径REQUIRES esp_https_ota esp_partition mbedtls #需要用到的其他组件
)
3.3.2 编写OTA任务
(1)创建OTA任务
esp_err_t aliot_ota_start(void)
{if (is_current_ota) //防止重复启动OTAreturn ESP_FAIL;is_current_ota = true;//创建任务xTaskCreatePinnedToCore(aliot_ota_task, "aliot_ota", 8192, NULL, 4, NULL, 1);return ESP_OK;
}
(2)编写OTA任务函数
核心函数是:esp_err_t esp_https_ota(const esp_https_ota_config_t *ota_config)
调用此函数后,此函数会确认https的连接,从地址里读取镜像数据,完成OTA固件升级。因此需先完成参数ota_config的配置,再调用即可。
3.3.3 完整代码
aliot_ota.h完整代码:
#ifndef _ALIOT_OTA_H_
#define _ALIOT_OTA_H_
#include "esp_err.h"//ota完成回调函数
typedef void (*aliot_ota_finish_callback_t)(int code);//设置阿里云OTA的https下载地址
esp_err_t aliot_ota_config(const char* url, aliot_ota_finish_callback_t f);//启动阿里云OTA
esp_err_t aliot_ota_start(void);#endif //_ALIOT_OTA_H_
aliot_ota.c完整代码:
#include "aliot_ota.h"
#include "esp_log.h"
#include "esp_https_ota.h"
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_http_client.h"//客户端向服务器请求升级包用
#include "esp_crt_bundle.h"//证书包头文件#define TAG "ALIOT_OTA"//下载地址
static char aliot_url[256];//回调函数
static aliot_ota_finish_callback_t s_ota_finish_f = NULL;//当前是否处于OTA过程
static bool is_current_ota = false;//任务
void aliot_ota_task(void* param)
{esp_err_t ota_finish_err = ESP_OK;ESP_LOGI(TAG, "ALIOT ota start......");//初始化http客户端esp_http_client_config_t http_config = {.url = aliot_url,.timeout_ms = 10*1000,.crt_bundle_attach = esp_crt_bundle_attach,//签名证书,由于使用阿里云物联网自签名证书在此有问题,因此使用ESP内置的根证书.keep_alive_enable = true,//是否保持连接};//OTA配置初始化esp_https_ota_config_t ota_config = {.http_config = &http_config,};//此函数会确认https的连接,从地址里读取镜像数据,完成OTA固件升级ota_finish_err = esp_https_ota(&ota_config);if (ota_finish_err == ESP_OK){if (s_ota_finish_f)s_ota_finish_f(0); //上报升级进度,升级成功!vTaskDelay(pdMS_TO_TICKS(1500));ESP_LOGI(TAG, "OTA success! Restart......");esp_restart();//重启}else{if (s_ota_finish_f)s_ota_finish_f(-1); //上报升级进度,升级失败!ESP_LOGI(TAG, "OTA fail!");}is_current_ota = false;vTaskDelete(NULL);
}//设置阿里云OTA的https下载地址
esp_err_t aliot_ota_config(const char* url, aliot_ota_finish_callback_t f)
{snprintf(aliot_url, sizeof(aliot_url), "%s", url);s_ota_finish_f = f;return ESP_OK;
}//启动阿里云OTA
esp_err_t aliot_ota_start(void)
{if (is_current_ota) //防止重复启动OTAreturn ESP_FAIL;is_current_ota = true;//创建任务xTaskCreatePinnedToCore(aliot_ota_task, "aliot_ota", 8192, NULL, 4, NULL, 1);return ESP_OK;
}
4.验证功能
(1)生成升级文件
将版本号设置为1.0.1,编译生成bin文件,并将其改名为esp32s3_v1.0.1.bin。
(2)烧录初始代码
将版本号设置为1.0.0,编译并烧录到设备中,打开串口监视工具,等待升级。
(3)升级文件上传到阿里云
(4)验证升级包
(5)查看升级结果
阿里云平台显示升级失败:
查看日志却表明升级成功了:
ESP32S3显示升级成功,但也提示了一些错误信息,大概是连接中断的意思,应该不是大问题:
版本号也是对的,说明确实是升级成功了:
至于为啥阿里云显示升级失败我也不清楚,可能就是升级复位后MQTT断开连接了,我之前用ESP32测试过的,但现在烧录之前那份代码也不行了,有大佬知道怎么解决吗?
5.注意事项
- 在OTA升级结束后加防止回滚函数:esp_ota_mark_app_valid_cancel_rollback();
6.参考文档
使用TLS加密设备和物联网平台的MQTT通信_物联网平台(IoT)-阿里云帮助中心
MQTT协议接入的物联网设备OTA升级流程_物联网平台(IoT)-阿里云帮助中心
【【2024最新版 ESP32教程(基于ESP-IDF)】ESP32入门级开发课程 更新中 中文字幕】
7.源码下载
https://download.csdn.net/download/Freddy_Ssc/90633389