本文以C Link SDK中的Demo文件./demos/mota_basic_demo.c
为例,介绍如何调用Link SDK的API,帮助设备端使用MQTT协议,下载仅含单个升级文件的OTA升级包,实现设备的OTA升级。
背景信息
- OTA升级功能的更多信息,请参见OTA升级概述。
- OTA升级功能基于MQTT接入,开发过程中涉及MQTT接入的代码说明,请参见MQTT接入。
步骤一:初始化OTA功能
- 添加头文件。
……
……
#include "aiot_ota_api.h"
#include "aiot_mqtt_download_api.h"
……
- 配置底层依赖和日志输出。
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* TODO: 如果要关闭日志, 则将该函数实现为空, 如果要减少日志, 可根据code选择不打印
* 例如: [1578463098.611][LK-0309] pub: /ota/device/upgrade/a13FN******/ota_demo
* 上述该条日志的code为0309(十六进制), code值的定义见core/aiot_state_api.h
*/
/* 日志回调函数, SDK的日志从此处输出 */
int32_t demo_state_logcb(int32_t code, char *message)
{
printf("%s", message);
return 0;
}
- 调用aiot_ota_init,创建OTA客户端实例,并初始化默认参数。
ota_handle = aiot_ota_init();
if (NULL == ota_handle) {
goto exit;
}
步骤三:上报设备当前版本号
设备建立MQTT连接后,调用aiot_ota_report_version,上报当前设备的版本号。物联网平台根据版本号,判断是否需要升级。
以下示例代码中,OTA升级前设备上报的版本号为1.0.0
,在实际业务中,您需从设备的配置区获取实际的版本号,并执行编写代码。
注意
设备进行OTA升级前,需至少上报一次版本号。
cur_version = "1.0.0";
res = aiot_ota_report_version(ota_handle, cur_version);
if (res < STATE_SUCCESS) {
printf("aiot_ota_report_version failed: -0x%04X\r\n", -res);
}
步骤四:接收升级指令
- 在物联网平台添加升级包,并发起升级任务后,物联网平台向设备端下发升级指令。
- 设备端调用aiot_mqtt_recv接收消息,当消息被识别为OTA升级指令后,调用回调函数
user_ota_recv_handler
,执行对应的处理逻辑。
- 编写回调函数的处理逻辑。
您可以参考以下内容,编写回调函数的处理逻辑:
- 物联网平台通过Topic
/ota/device/upgrade/${ProductKey}/${DeviceName}
,向设备下发OTA升级包指令。
${ProductKey}和${DeviceName}的详细说明,请参见获取设备认证信息。
- OTA升级指令的类型为AIOT_OTARECV_FOTA。
void user_ota_recv_handler(void *ota_handle, aiot_ota_recv_t *ota_msg, void *userdata)
{
uint32_t request_size = 10 * 1024;
switch (ota_msg->type) {
case AIOT_OTARECV_FOTA: {
if (NULL == ota_msg->task_desc || ota_msg->task_desc->protocol_type != AIOT_OTA_PROTOCOL_MQTT) {
break;
}
……
……
}
- OTA升级指令消息的Alink数据格式说明,请参见物联网平台推送OTA升级包信息。
- OTA升级指令消息的数据结构类型为aiot_ota_recv_t,Link SDK自动解析收到的升级指令消息。
- 您可以参考示例代码,编写回调函数的处理逻辑,请参见步骤五的步骤1至2。
步骤五:下载升级包,进行OTA升级
注意 设备端收到物联网平台推送的升级包消息后,不会自动下载升级包,您需要调用Link SDK提供的API启动下载。
触发函数user_ota_recv_handler
后,基于MQTT协议,下载器发起下载请求,然后接收物联网平台返回的升级包,进行OTA升级。
- 初始化下载器。
调用aiot_mqtt_download_init,创建download
客户端实例,并初始化默认参数。void user_ota_recv_handler(void *ota_handle, aiot_ota_recv_t *ota_msg, void *userdata)
{
……
……
printf("OTA target firmware version: %s, size: %u Bytes\r\n", ota_msg->task_desc->version,
ota_msg->task_desc->size_total);
void *md_handler = aiot_mqtt_download_init();
……
……
}
- 配置下载参数。
调用aiot_mqtt_download_setopt,配置下载任务的相关参数。
- 示例代码:
aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_TASK_DESC, ota_msg->task_desc);
/* 设置下载一包的大小,对于资源受限设备可以调整该值大小 */
aiot_mqtt_download_setopt(md_handler, AIOT_DLOPT_DATA_REQUEST_SIZE, &request_size);
/* 分段下载升级包,或仅下载升级包的片段时,每个分段的起始和终止位置的字节序号。
* 若设置范围区间下载,单包报文的数据有CRC校验,但SDK将不进行完整文件MD5校验,
* 默认下载全部文件,单包报文的数据有CRC校验,且SDK将进行完整文件MD5校验
* 若取消下列代码注释,表示仅从文件的第10字节开始下载,下载至第50千字节的第10字节为止。/
// uint32_t range_start = 10, range_end = 50 * 1024 + 10;
// aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_RANGE_START, &range_start);
// aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_RANGE_END, &range_end);
aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_RECV_HANDLE, user_download_recv_handler);
g_dl_handle = md_handler;
- 相关参数:
配置项 |
示例 |
说明 |
AIOT_MDOPT_TASK_DESC |
ota_msg->task_desc |
设置下载任务。 |
AIOT_DLOPT_DATA_REQUEST_SIZE |
request_size |
每次向物联网平台请求的单包数据大小。 |
AIOT_MDOPT_RANGE_START |
range_start |
分段下载升级包,或仅下载升级包的片段时,每个分段的起始和终止位置的字节序号。
如果不设置,表示一次下载全部文件。
例如,一个1024字节的升级包分两次下载,两次的参数可分别设置为:
- 第一次:
AIOT_DLOPT_RANGE_START=0 ,AIOT_DLOPT_RANGE_END=511
- 第二次:
AIOT_DLOPT_RANGE_START=512 ,AIOT_DLOPT_RANGE_END=1023
|
AIOT_MDOPT_RANGE_END |
range_end |
AIOT_MDOPT_RECV_HANDLE |
user_download_recv_handler |
配置OTA升级包内容的回调。
设备发起下载请求后,物联网平台返回升级包内容的消息时,触发该回调函数。
|
- 调用aiot_mqtt_download_process,向物联网平台发送下载请求。
while (1) {
aiot_mqtt_process(mqtt_handle);
aiot_mqtt_recv(mqtt_handle);
if(g_dl_handle != NULL) {
int32_t res = aiot_mqtt_download_process(g_dl_handle);
……
……
}
}
- 物联网平台接收下载请求后,向设备返回升级包信息,设备接收消息后,触发回调函数
user_download_recv_handler
。您需编写回调函数的处理逻辑,将下载的升级包数据固化至本地。
void user_download_recv_handler(void *handle, const aiot_mqtt_download_recv_t *packet, void *userdata)
{
uint32_t data_buffer_len = 0;
/* 目前只支持packet->type为AIOT_MDRECV_DATA_RESP的情况 */
if (!packet || AIOT_MDRECV_DATA_RESP != packet->type) {
return;
}
/* 应在此实现文件本地固化的操作 */
FILE *file = fopen("mota_demo.bin", "ab");
fwrite(packet->data.data_resp.data, packet->data.data_resp.data_size, sizeof(int8_t), file);
fclose(file);
data_buffer_len = packet->data.data_resp.data_size;
printf("download %03d%% done, +%d bytes\r\n", packet->data.data_resp.percent, data_buffer_len);
}
- 升级包文件下载完成后,调用aiot_mqtt_download_deinit退出程序。
while (1) {
aiot_mqtt_process(mqtt_handle);
aiot_mqtt_recv(mqtt_handle);
if(g_dl_handle != NULL) {
int32_t res = aiot_mqtt_download_process(g_dl_handle);
if(STATE_MQTT_DOWNLOAD_SUCCESS == res) {
/* 升级成功,可在此处重启并且上报新的版本号 */
printf("mqtt download ota success \r\n");
aiot_mqtt_download_deinit(&g_dl_handle);
break;
} else if(STATE_MQTT_DOWNLOAD_FAILED_RECVERROR == res
|| STATE_MQTT_DOWNLOAD_FAILED_TIMEOUT == res
|| STATE_MQTT_DOWNLOAD_FAILED_MISMATCH == res) {
printf("mqtt download ota failed \r\n");
aiot_mqtt_download_deinit(&g_dl_handle);
break;
}
}
}
步骤七:断开连接
调用aiot_mqtt_disconnect,向物联网平台发送断开连接的报文,然后断开网络连接。
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
return -1;
}
步骤八:退出OTA程序
调用aiot_ota_deinit,销毁OTA客户端实例,释放资源。
aiot_ota_deinit(&ota_handle);
后续步骤
- 例程文件配置完成后,需进行编译,生成可执行文件
./demos/mota-basic-demo
。
更多信息,请参见编译与运行。
- 关于运行结果的详细说明,请参见运行日志。