4G_module/examples/aliyun/src/cm_demo_aliyun_ota.c

568 lines
22 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 这个例程适用于`Linux`这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建3个线程
*
* + 一个线程用于保活长连接
* + 一个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调
* + 一个线程用于从网络上HTTP下载待升级的固件, 这个线程由接收消息线程得到OTA升级的MQTT消息后启动
*
* 需要用户关注或修改的部分, 已用 `TODO` 在注释中标明
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_ota_api.h"
#include "aiot_mqtt_api.h"
#include "cm_os.h"
#include "cm_sys.h"
#include "cm_demo_uart.h"
#define CM_ALIYUN_OTA_OK 0
/** 服务器OTA升级方式选择"云端主动推送升级" */
//#define CM_ALIYUN_OTA_ACTIVE_PUSH
/** 阿里云OTA消息事件ID*/
typedef enum
{
CM_ALIYUN_OTA_UPGRADE_START = 0,
CM_ALIYUN_OTA_CHECK_FAIL,
CM_ALIYUN_OTA_UPGRADE_UNKNOWN
} cm_aliyun_ota_event_e;
/** 阿里云OTA队列消息*/
typedef struct{
cm_aliyun_ota_event_e event; //事件ID
void *arg; //用户参数
} cm_aliyun_ota_msg_t;
static osMessageQueueId_t s_aliyun_ota_queue = NULL;
/* TODO: 替换为自己设备的三元组 */
static char *product_key = "${YourProductKey}";
static char *device_name = "${YourDeviceName}";
static char *device_secret = "${YourDeviceSecret}";
#define printf(...) do{ cm_demo_printf("Line[%d]: ",__LINE__); cm_demo_printf(__VA_ARGS__); cm_demo_printf("\r\n");}while(0);
/*
TODO: 替换为自己实例的接入点
对于企业实例, 或者2021年07月30日之后含当日开通的物联网平台服务下公共实例
mqtt_host的格式为"${YourInstanceId}.mqtt.iothub.aliyuncs.com"
其中${YourInstanceId}: 请替换为您企业/公共实例的Id
对于2021年07月30日之前不含当日开通的物联网平台服务下公共实例
需要将mqtt_host修改为: mqtt_host = "${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com"
其中, ${YourProductKey}请替换为设备所属产品的ProductKey。可登录物联网平台控制台在对应实例的设备详情页获取。
${YourRegionId}:请替换为您的物联网平台设备所在地域代码, 比如cn-shanghai等
该情况下完整mqtt_host举例: a1TTmBPIChA.iot-as-mqtt.cn-shanghai.aliyuncs.com
详情请见: https://help.aliyun.com/document_detail/147356.html
*/
static char *mqtt_host = "${YourInstanceId}.mqtt.iothub.aliyuncs.com";
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
static osThreadId_t g_mqtt_process_thread = NULL; /* 用于MQTT的长连接保活线程 */
static void __cm_aliyun_ota_response(int res);
/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
*
* 例如: [1578463098.611][LK-0309] pub: /ota/device/upgrade/a13FN5TplKq/ota_demo
*
* 上面这条日志的code就是0309(十六进制), code值的定义见core/aiot_state_api.h
*
*/
/* 日志回调函数, SDK的日志会从这里输出 */
static int32_t __demo_state_logcb(int32_t code, char *message)
{
/* 下载固件的时候会有大量的HTTP收包日志, 通过code筛选出来关闭 */
if (STATE_HTTP_LOG_RECV_CONTENT != code) {
//printf("%s", message);
}
return 0;
}
/* 用户通过 aiot_ota_setopt() 注册的OTA消息处理回调, 如果SDK收到了OTA相关的MQTT消息, 会自动识别, 调用这个回调函数 */
static void __demo_ota_recv_handler(void *ota_handle, aiot_ota_recv_t *ota_msg, void *userdata)
{
switch (ota_msg->type) {
case AIOT_OTARECV_FOTA: {
if (NULL == ota_msg->task_desc || ota_msg->task_desc->protocol_type != AIOT_OTA_PROTOCOL_HTTPS) {
break;
}
printf("OTA target firmware version: %s, size: %u Bytes \r\n", ota_msg->task_desc->version,
ota_msg->task_desc->size_total);
/*
* 非常重要!!!
*
* 该部分为ASR从服务器下载ota升级文件并执行ota的接口调用步骤,用户请不要修改
*
* 1. 使用cm_fota_set_url接口配置阿里云服务器OTA文件下载url,该url配置掉电保存;
* 2. 使用cm_fota_exec_upgrade接口执行升级;
*
* 注意:
* 1. 执行升级后模组会进行多次重启,且升级异常后无法还原至升级前的版本,请在FOTA升级前先使用单个模组调试,保证升级无异常后再进行大规模升级;
* 2. 执行升级后url有效期为24小时,且升级异常后无法还原至升级前的版本,模组必须在升级前保证模组的网络,电源,SIM流量剩余等,防止升级失败无法恢复;
*
*/
extern int cm_fota_set_url(char *url);
extern int cm_fota_exec_upgrade(void);
extern void cm_fota_res_callback_register(void *cb);
/** 底层注册回调函数,ASR大系统验证FOTA url有效性结果反馈 */
cm_fota_res_callback_register(__cm_aliyun_ota_response);
int ret = 0;
ret = cm_fota_set_url(ota_msg->task_desc->url);
if(ret != 0)
{
printf("set url err\r\n");
return;
}
printf("set url: %s\r\n", ota_msg->task_desc->url);
osDelay(2); //延迟2s
/**
* 芯片执行ota升级任务,芯片将首先验证url有消息,理论上有效会重启进入最小系统并执行ota包下载与升级,
* 失败则会通过
*/
ret = cm_fota_exec_upgrade();
if(ret != 0)
{
printf("exec upgrade err\r\n");
return;
}
/** 芯片执行ota升级任务,通知关闭连接并释放资源 */
if(s_aliyun_ota_queue)
{
cm_aliyun_ota_msg_t ota_data = {0};
ota_data.event = CM_ALIYUN_OTA_UPGRADE_START;
osMessageQueuePut(s_aliyun_ota_queue, &ota_data, 0, 0x0U);
}
break;
}
default:
break;
}
}
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
static void __demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
{
switch (event->type) {
/* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
case AIOT_MQTTEVT_CONNECT: {
printf("AIOT_MQTTEVT_CONNECT\r\n");
/* TODO: 处理SDK建连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络状况被动断连后, 自动发起重连已成功 */
case AIOT_MQTTEVT_RECONNECT: {
printf("AIOT_MQTTEVT_RECONNECT\r\n");
/* TODO: 处理SDK重连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
case AIOT_MQTTEVT_DISCONNECT: {
char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
("heartbeat disconnect");
printf("AIOT_MQTTEVT_DISCONNECT: %s\r\n", cause);
/* TODO: 处理SDK被动断连, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
default: {
}
}
}
/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */
static void __demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
{
switch (packet->type) {
case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {
printf("heartbeat response\r\n");
/* TODO: 处理服务器对心跳的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_SUB_ACK: {
printf("suback, res: -0x%04X, packet id: %d, max qos: %d\r\n",
-packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
/* TODO: 处理服务器对订阅请求的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_PUB: {
printf("pub, qos: %d, topic: %.*s\r\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);
printf("pub, payload: %.*s\r\n", packet->data.pub.payload_len, packet->data.pub.payload);
/* TODO: 处理服务器下发的业务报文 */
}
break;
case AIOT_MQTTRECV_PUB_ACK: {
printf("puback, packet id: %d\r\n", packet->data.pub_ack.packet_id);
/* TODO: 处理服务器对QoS1上报消息的回应, 一般不处理 */
}
break;
default: {
}
}
}
/* 执行aiot_mqtt_process的线程, 包含心跳发送和QoS1消息以及接收mqtt信息重发 */
static void *__demo_mqtt_process_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (1) {
aiot_mqtt_process(args);
res = aiot_mqtt_recv(args);
if (res < STATE_SUCCESS) {
osDelay(200);
}
osDelay(1000);
}
return NULL;
}
/**
* @brief OTA升级检测失败回调函数
*
* @param [in] ver : 模组返回升级结果错误码
*
* @return void
*
* @details 由于OC SDK与ASR芯片升级逻辑的物理隔离,需要回调通知执行结果:
* 理论上这个接口不会上报升级检测成功信息,仅当检测失败时执行,检测成功后模组将自动重启升级
*
*/
static void __cm_aliyun_ota_response(int res)
{
if(CM_ALIYUN_OTA_OK != res)
{
printf("ota checked failed, please try after confirm\r\n");
/**
* TO DO
*/
}
}
/**
* @brief 阿里云OTA升级Demo
*
* @param void
*
* @return
* = 0 - 成功 \n
* < 0 - 失败
*
* @details 运行该用例需要配置实例的接入点mqtt_host以及设备的product_key,device_name,device_secret,配置与阿里云平台配置保持一致,另外:
* 1. 执行升级后模组会进行多次重启,且升级异常后无法还原至升级前的版本,请在FOTA升级前先使用单个模组调试,保证升级无异常后再进行大规模升级;
* 2. 执行升级后url有效期为24小时,且升级异常后无法还原至升级前的版本,模组必须在升级前保证模组的网络,电源,SIM流量剩余等,防止升级失败无法恢复;
* 3. 升级后需要客户自行版本管理并调用cm_aliyun_ota_version_report接口向阿里云平台上报版本号
*/
void cm_aliyun_ota_test(unsigned char **cmd, int len)
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
uint16_t port = 443; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
void *ota_handle = NULL;
char *cur_version = NULL;
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(__demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA; /* 使用RSA证书校验MQTT服务端 */
cred.max_tls_fragment = 16384; /* 最大的分片长度为16K, 其它可选值还有4K, 2K, 1K, 0.5K */
cred.sni_enabled = 1; /* TLS建连时, 支持Server Name Indicator */
cred.x509_server_cert = ali_ca_cert; /* 用来验证MQTT服务端的RSA根证书 */
cred.x509_server_cert_len = strlen(ali_ca_cert); /* 用来验证MQTT服务端的RSA根证书长度 */
/* 创建1个MQTT客户端实例并内部初始化默认参数 */
mqtt_handle = aiot_mqtt_init();
if (NULL == mqtt_handle) {
printf("aiot_mqtt_init failed\r\n");
return;
}
/* 配置MQTT服务器地址 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
/* 配置MQTT服务器端口 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
/* 配置设备productKey */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
/* 配置设备deviceName */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
/* 配置设备deviceSecret */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
/* 配置网络连接的安全凭据, 上面已经创建好了 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
/* 配置MQTT默认消息接收回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)__demo_mqtt_default_recv_handler);
/* 配置MQTT事件回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)__demo_mqtt_event_handler);
/* 与MQTT例程不同的是, 这里需要增加创建OTA会话实例的语句 */
ota_handle = aiot_ota_init();
if (NULL == ota_handle) {
printf("aiot_ota_init failed\r\n");
aiot_mqtt_deinit(&mqtt_handle);
return;
}
/* 用以下语句, 把OTA会话和MQTT会话关联起来 */
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_MQTT_HANDLE, mqtt_handle);
/* 用以下语句, 设置OTA会话的数据接收回调, SDK收到OTA相关推送时, 会进入这个回调函数 */
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_RECV_HANDLER, __demo_ota_recv_handler);
/* 与服务器建立MQTT连接 */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
aiot_mqtt_deinit(&mqtt_handle);
aiot_ota_deinit(&ota_handle);
printf("aiot_mqtt_connect failed: -0x%04X\r\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
return;
}
/* TODO: 非常重要!!!
*
* cur_version 要根据用户实际情况, 改成从设备的配置区获取, 要反映真实的版本号, 而不能像示例这样写为固定值
*
* 1. 如果设备从未上报过版本号, 在控制台网页将无法部署升级任务
* 2. 如果设备升级完成后, 上报的不是新的版本号, 在控制台网页将会显示升级失败
*
* 注意:这里的上报版本号为触发升级时向阿里云平台上报的当前版本,升级后请调用cm_aliyun_ota_version_report接口
* 像阿里云平台report升级后的版本而不是再次使用该接口。
*
*/
/* 演示MQTT连接建立起来之后, 就可以上报当前设备的版本号了 */
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_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */
osThreadAttr_t mqtt_process_attr = {0};
mqtt_process_attr.name = "mqtt_process_task";
mqtt_process_attr.stack_size = 1024 * 4;
mqtt_process_attr.priority = osPriorityNormal;
g_mqtt_process_thread = osThreadNew((osThreadFunc_t)__demo_mqtt_process_thread, mqtt_handle,&mqtt_process_attr);
if (g_mqtt_process_thread == NULL) {
printf("pthread_create __demo_mqtt_process_thread failed: %d\n", res);
return;
}
/**
* 主动向服务器请求OTA升级,
* 注意:服务器OTA升级方式选择"云端主动推送升级"时才调用,否则不需要调用该部分,
* 服务器将主动下发OTA升级task
*/
#ifndef CM_ALIYUN_OTA_ACTIVE_PUSH
{
osDelay(200);
aiot_ota_query_firmware(ota_handle);
}
#endif
cm_aliyun_ota_msg_t ota_data = {0};
s_aliyun_ota_queue = osMessageQueueNew(5, sizeof(cm_aliyun_ota_msg_t), NULL);
/* 主循环进入休眠 */
while (1) {
osMessageQueueGet(s_aliyun_ota_queue, &ota_data, NULL, osWaitForever); //阻塞
switch(ota_data.event)
{
case CM_ALIYUN_OTA_UPGRADE_START:
/** 退出循环阻塞,客户检测模组状态后可重 */
goto quit_ota;
break;
default:
break;
}
continue;
quit_ota:
break;
}
if(s_aliyun_ota_queue)
{
osMessageQueueDelete(s_aliyun_ota_queue);
s_aliyun_ota_queue = NULL;
}
/* 终止MQTT Receive线程,释放资源 */
if(g_mqtt_process_thread)
{
osThreadTerminate(g_mqtt_process_thread);
g_mqtt_process_thread = NULL;
}
/* 断开MQTT连接, 一般不会运行到这里 */
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_disconnect failed: -0x%04X\r\n", -res);
goto exit;
}
exit:
while (1) {
/* 销毁MQTT实例, 一般不会运行到这里 */
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\r\n", -res);
return;
} else {
break;
}
}
/* 销毁OTA实例, 一般不会运行到这里 */
aiot_ota_deinit(&ota_handle);
return;
}
/**
* @brief 阿里云平台上报OTA版本号,用于OTA升级完成后向服务器上报升级后版本
*
* @param [in] ver : 待上报的设备版本号字符串
*
* @return
* = 0 - 成功 \n
* < 0 - 失败
*
* @details 用于OTA升级完成后向服务器上报升级后版本,上报完成后即可断开连接并释放资源
* 客户可以在初始化时版本管理过程中上报版本
*/
int cm_aliyun_ota_version_report(const char *ver)
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
uint16_t port = 443; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
void *ota_handle = NULL;
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(__demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA; /* 使用RSA证书校验MQTT服务端 */
cred.max_tls_fragment = 16384; /* 最大的分片长度为16K, 其它可选值还有4K, 2K, 1K, 0.5K */
cred.sni_enabled = 1; /* TLS建连时, 支持Server Name Indicator */
cred.x509_server_cert = ali_ca_cert; /* 用来验证MQTT服务端的RSA根证书 */
cred.x509_server_cert_len = strlen(ali_ca_cert); /* 用来验证MQTT服务端的RSA根证书长度 */
/* 创建1个MQTT客户端实例并内部初始化默认参数 */
mqtt_handle = aiot_mqtt_init();
if (NULL == mqtt_handle) {
printf("aiot_mqtt_init failed\r\n");
return -1;
}
/* 配置MQTT服务器地址 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
/* 配置MQTT服务器端口 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
/* 配置设备productKey */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
/* 配置设备deviceName */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
/* 配置设备deviceSecret */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
/* 配置网络连接的安全凭据, 上面已经创建好了 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)__demo_mqtt_default_recv_handler);
/* 配置MQTT事件回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)__demo_mqtt_event_handler);
/* 与MQTT例程不同的是, 这里需要增加创建OTA会话实例的语句 */
ota_handle = aiot_ota_init();
if (NULL == ota_handle) {
printf("aiot_ota_init failed\r\n");
aiot_mqtt_deinit(&mqtt_handle);
return -2;
}
/* 用以下语句, 把OTA会话和MQTT会话关联起来 */
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_MQTT_HANDLE, mqtt_handle);
/* 用以下语句, 设置OTA会话的数据接收回调, SDK收到OTA相关推送时, 会进入这个回调函数 */
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_RECV_HANDLER, __demo_ota_recv_handler);
/* 与服务器建立MQTT连接 */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
aiot_mqtt_deinit(&mqtt_handle);
aiot_ota_deinit(&ota_handle);
printf("aiot_mqtt_connect failed: -0x%04X\r\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
return -3;
}
/* 上报设备版本号,上报完成后断开连接并释放资源 */
res = aiot_ota_report_version(ota_handle, (char *)ver);
if (res < STATE_SUCCESS) {
printf("aiot_ota_report_version failed: -0x%04X\r\n", -res);
}
/* 断开MQTT连接, 一般不会运行到这里 */
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_disconnect failed: -0x%04X\r\n", -res);
goto exit;
}
exit:
while (1) {
/* 销毁MQTT实例, 一般不会运行到这里 */
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\r\n", -res);
return -1;
} else {
break;
}
}
/* 销毁OTA实例, 一般不会运行到这里 */
aiot_ota_deinit(&ota_handle);
return 0;
}