本文以C Link SDK中的Demo文件./demos/cota_basic_demo.c为例,介绍如何调用Link SDK的API,帮助您实现设备的远程配置功能。
背景信息
步骤一:初始化
- 添加头文件。
…… …… #include "aiot_ota_api.h" ……
- 配置底层依赖和日志输出。
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); aiot_state_set_logcb(demo_state_logcb);
- 调用aiot_ota_init,创建OTA客户端实例,并初始化默认参数。
ota_handle = aiot_ota_init(); if (NULL == ota_handle) { printf("aiot_ota_init failed\r\n"); aiot_mqtt_deinit(&mqtt_handle); return -2; }
步骤二:配置功能
调用aiot_ota_setopt,配置以下功能。
- 关联MQTT连接的句柄。重要 在设置远程配置参数前,请确保已配置设备认证信息等相关参数。具体操作,请参见MQTT配置连接参数。
- 示例代码:
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_MQTT_HANDLE, mqtt_handle);
- 相关参数:
配置项 示例 说明 AIOT_OTAOPT_MQTT_HANDLE mqtt_handle 远程配置功能的请求基于MQTT连接,通过该配置项,关联MQTT连接句柄。
- 示例代码:
- 设置远程配置的指令消息的回调。
- 示例代码:
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_RECV_HANDLER, demo_ota_recv_handler);
- 相关参数:
配置项 示例 说明 AIOT_OTAOPT_MQTT_HANDLE demo_ota_recv_handler 当设备收到来自物联网平台的远程配置指令时,调用该回调函数。
- 示例代码:
步骤三:设备主动请求配置消息
如果物联网平台下发远程配置指令时,设备不在线,但设备上线后,可主动请求获取远程配置的指令消息。
说明 如果物联网平台未创建设备任务,调用接口后,返回
revice task get detail reply, task_id:[$next],status:[9]
。- 调用aiot_mqtt_pub,向物联网平台发送请求,以获取远程配置信息。
- 示例代码:
{ char *topic_string = "/sys/a18wP******/LightSwitch/thing/config/get"; char *payload_string = "{\"id\":\"123\",\"params\":{\"configScope\":\"product\",\"getType\":\"file\"}}"; res = aiot_mqtt_pub(mqtt_handle, topic_string, (uint8_t *)payload_string, strlen(payload_string), 0); if (res < STATE_SUCCESS) { printf("aiot_mqtt_pub failed: -0x%04X\r\n", -res); /* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */ goto exit; } }
- 相关参数:
参数 示例值 说明 topic_string /sys/a18wP******5/LightSwitch/thing/config/get 请求获取远程配置信息的Topic。 关于Topic的更多信息,请参见什么是Topic。
示例代码中:
a18wP******
为设备的ProductKey。LightSwitch
为设备的DeviceName。
更多信息,请参见获取设备认证信息。
payload_string {\"id\":\"123\",\"params\":{\"configScope\":\"product\",\"getType\":\"file\"}} 请求消息的内容。 请求消息的内容为JSON格式,是Alink格式数据中params的值。详细说明,请参见设备主动请求配置信息。
- 示例代码:
- 物联网平台接收请求后,向设备返回配置信息。
- 调用aiot_mqtt_recv,接收远程配置指令消息。
- 如果
g_dl_handle
指针会为空, 则只从网络上轮询MQTT报文。 - 如果
g_dl_handle
指针为非空, 则调用aiot_download_recv
函数。
while (1) { aiot_mqtt_process(mqtt_handle); res = aiot_mqtt_recv(mqtt_handle); if (res == STATE_SYS_DEPEND_NWK_CLOSED) { sleep(1); } if (NULL != g_dl_handle) { /* 完成远程配置的接收前, 将mqtt的收包超时调整到100毫秒, 以减少两次远程配置的下载间隔*/ int32_t ret = aiot_download_recv(g_dl_handle); timeout_ms = 100; if (STATE_DOWNLOAD_FINISHED == ret) { aiot_download_deinit(&g_dl_handle); /* 完成远程配置的接收后, 将mqtt的收包超时调整回到默认值5000毫秒 */ timeout_ms = 5000; } aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_TIMEOUT_MS, (void *)&timeout_ms); } }
- 如果
- 设备接收配置指令的消息后,触发回调函数
demo_ota_recv_handler
。您可以参考以下内容,编写回调函数的处理逻辑:- 物联网平台通过Topic
/sys/${ProductKey}/${DeviceName}/thing/config/push
,向设备下发远程配置指令。 - Link SDK解析物联网平台指令,解析后的数据结构类型为aiot_ota_recv_t,Link SDK自动解析收到的配置消息。
- 远程配置的消息类型为AIOT_OTARECV_COTA。
通过回调函数,发起HTTPS协议的请求,下载远程配置文件。回调函数的编写逻辑,请参见步骤五:下载远程配置文件。
- 物联网平台通过Topic
步骤四:设备接收物联网平台推送的远程配置信息
如果设备在线,物联网平台下发远程配置指令后,设备随即接收远程配置指令,触发回调函数。
具体操作,请参见编写回调函数的处理逻辑。
步骤五:下载远程配置文件
重要 接收配置指令后,设备不会自动下载配置文件,您需调用Link SDK提供的API启动下载。
- 初始化下载器。调用aiot_download_init,创建
download
客户端实例,并初始化默认参数。uint32_t res = 0; uint16_t port = 443; uint32_t max_buffer_len = 2048; aiot_sysdep_network_cred_t cred; void *dl_handle = aiot_download_init(); if (NULL == dl_handle || NULL == ota_msg->task_desc) { return; } printf("configId: %s, configSize: %u Bytes\r\n", ota_msg->task_desc->version, ota_msg->task_desc->size_total); memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t)); cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA; cred.max_tls_fragment = 16384; cred.x509_server_cert = ali_ca_cert; cred.x509_server_cert_len = strlen(ali_ca_cert);
- 配置下载参数。调用aiot_download_setopt,配置下载任务的相关参数。
/* 设置下载时为TLS下载 */ if ((STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_CRED, (void *)(&cred))) || /* 设置下载时访问的服务器端口号 */ (STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_PORT, (void *)(&port))) || /* 设置下载的任务信息, 通过输入参数 ota_msg 中的 task_desc 成员得到, 内含下载地址, 远程配置的大小和版本号*/ (STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_TASK_DESC, (void *)(ota_msg->task_desc))) || /* 设置下载内容到达时, SDK将调用的回调函数 */ (STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_RECV_HANDLER, (void *)(demo_download_recv_handler))) || /* 设置单次下载最大的buffer长度, 每当这个长度的内存读满了后会通知用户 */ (STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_BODY_BUFFER_MAX_LEN, (void *)(&max_buffer_len))) || /* 发送http的GET请求给http服务器 */ (STATE_SUCCESS != aiot_download_send_request(dl_handle))) { if (res != STATE_SUCCESS) { aiot_download_deinit(&dl_handle); break; } }
- 发起下载请求后,接收应答报文,触发回调函数
demo_download_recv_handler
,执行下载处理。您可以参考以下内容,编写回调函数的处理逻辑:- 下载消息的数据结构类型为aiot_download_recv_t,是回到函数的入参。
- 下载消息的
packet->type
类型AIOT_DLRECV_HTTPBODY。 - 示例代码仅做打印处理,您需根据业务需要,将配置文件下载至指定位置。
void demo_download_recv_handler(void *handle, const aiot_download_recv_t *packet, void *userdata) { /* 目前只支持 packet->type 为 AIOT_DLRECV_HTTPBODY 的情况 */ if (!packet || AIOT_DLRECV_HTTPBODY != packet->type) { return; } int32_t percent = packet->data.percent; uint8_t *src_buffer = packet->data.buffer; uint32_t src_buffer_len = packet->data.len; /* 如果 percent 为负数, 说明发生了收包异常或者digest校验错误 */ if (percent < 0) { /* 收包异常或者digest校验错误 */ printf("exception happend, percent is %d\r\n", percent); return; } …… …… }
- 调用aiot_download_report_progress,上报下载进度。
void demo_download_recv_handler(void *handle, const aiot_download_recv_t *packet, void *userdata) { /* 目前只支持 packet->type 为 AIOT_DLRECV_HTTPBODY 的情况 */ if (!packet || AIOT_DLRECV_HTTPBODY != packet->type) { return; …… …… aiot_download_report_progress(handle, percent); printf("config len is %d, config content is %.*s\r\n", src_buffer_len, src_buffer_len, (char *)src_buffer); }
- 下载结束后,通过aiot_download_deinit,把句柄置
g_dl_handle
空。while (1) { aiot_mqtt_process(mqtt_handle); res = aiot_mqtt_recv(mqtt_handle); if (res == STATE_SYS_DEPEND_NWK_CLOSED) { sleep(1); } if (NULL != g_dl_handle) { /* 完成远程配置的接收前, 将mqtt的收包超时调整到100ms, 以减少两次远程配置的下载间隔*/ int32_t ret = aiot_download_recv(g_dl_handle); timeout_ms = 100; if (STATE_DOWNLOAD_FINISHED == ret) { aiot_download_deinit(&g_dl_handle); /* 完成远程配置的接收后, 将mqtt的收包超时调整回到默认值5000ms */ timeout_ms = 5000; } aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_TIMEOUT_MS, (void *)&timeout_ms); } }
步骤六:退出程序
调用aiot_ota_deinit,销毁OTA客户端实例,释放资源。
aiot_ota_deinit(&ota_handle);