阿里云物联网平台提供了远程配置功能,让用户可以将产品的配置以文件方式上传到物联网平台,设备上线后将配置文件下载到设备进行解析与处理。
API列表
远程配置包括两部分的内容, 从MQTT通道接收到远程配置的URL的环节, 与从HTTP通道下载远程配置的环节, 是分开的(都在components/ota/aiot_ota_api.h
中声明)。
- 远程配置信息推送
这部分API主要基于MQTT连接工作, 用于完成设备接收到云端下推的远程配置信息, 比如下载地址。
aiot_ota_init 创建ota客户端实例并设置默认参数。 aiot_ota_deinit 销毁ota客户端实例, 回收资源。 aiot_ota_setopt 设置ota连接参数, 详见OTA设置参数说明。 - 远程配置下载
这部分API主要基于HTTP连接工作, 用于完成设备从服务器下载远程配置。
aiot_download_init 创建download客户端实例并设置默认参数。 aiot_download_deinit 销毁download客户端实例, 回收资源。 aiot_download_setopt 设置download连接参数, 详见Download设置参数说明。 aiot_download_report_progress 用MQTT报文向云平台上报下载的进度, 也可以上报升级过程中发生的异常, 例如校验失败等。 aiot_download_send_request 向 aiot_download_setopt()
指定的远程配置服务器, 发起HTTP GET请求, 要求下载远程配置。aiot_download_recv 从网络上收取来自服务器的远程配置内容, 然后进入 AIOT_DLOPT_RECV_HANDLER
选项设置的用户回调, 将下载内容传给用户。
API使用概述
OTA模块可用于配合阿里云平台的远程配置服务, 有关介绍远程配置的控制操作请参见远程配置。
了解设备端配合远程配置的网络交互流程,请参见远程配置。
- 在控制台设置远程配置的内容后, 设备需要主动向控制台发起请求, 或者控制台主动下推, 设备才能收到远程配置的下载信息(包括url等)。
- 收到远程配置的下载信息后, 设备自行用
aiot_download_send_request
向云端发起下载请求, SDK不会自动下载远程配置。 - 设备下载中或者下载完成后, 设备需自行处理内存buffer中的远程配置, SDK不含有处理远程配置的逻辑。
- 远程配置内容大小的上限是64KB。
- 用户在控制台推送远程配置的下载地址等信息给设备时, 是通过MQTT通道, 所以设备获取远程配置的前提是成功建立并保持MQTT长连接通道在线。
- aiot_ota_setopt
对OTA会话进行配置, 常见的配置选项包括。
AIOT_OTAOPT_MQTT_HANDLE
: 把aiot_mqtt_init
返回的MQTT会话句柄跟OTA会话关联起来。AIOT_OTAOPT_RECV_HANDLER
: 设置OTA消息的数据处理回调, 这个用户回调在有OTA消息的时候, 会被aiot_mqtt_recv
调用到。
- aiot_download_recv
用户解析完OTA消息, 知道了远程配置的下载地址之后, 就可以用这个接口以HTTP下载远程配置内容。
被下载到的内容, 会通过回调函数传递给用户, 用户调用
aiot_download_setopt
把自己的数据处理回调函数设置给SDK。 - aiot_download_setopt
设置远程配置下载会话的选项, 常见需要设置的选项包括:
AIOT_DLOPT_RECV_HANDLER
: 用户告诉SDK, 当SDK收到远程配置内容的时候, 调用哪个用户函数来传出远程配置内容缓冲区。AIOT_DLOPT_NETWORK_CRED
: 可以配置是走HTTP还是走HTTPS下载远程配置内容。AIOT_DLOPT_BODY_BUFFER_MAX_LEN
: 这是个缓冲区的长度, SDK下载中, 每当填满这个长度就调用一次用户回调, 所以这里设置的越大, 下载越快, 内存开销也越大。
- aiot_download_report_progress
在设备开始下载远程配置的过程之后, 都可以用这个接口向云端上报进展情况, 包括下载进度或出错信息。
- 如果下载是正常的, 可以整数形式上报, 当前已下载的内容占远程配置整体大小的百分比, percent参数, SDK会自动计算好, 在回调中传给用户。
- 如果下载异常, 也可以用这个接口把异常告诉云端, 设备和云端的协议错误码约定见
aiot_ota_protocol_errcode_t
。
- aiot_download_send_request
设备通过OTA消息回调函数知道了远程配置的下载地址后, 就可以调用这个接口, 下载远程配置。
例程讲解
现对照components/ota/demos/cota_basic_demo.c
例程, 分步骤讲解如何使用API。

这个例程适用于演示了用SDK配置MQTT参数并建立连接, 之后向云端发起获取远程配置的请求. 在收到云端的回复后, 根据回复报文中的远程配置的url, 发起下载请求并开始下载。
需要用户关注或修改的部分, 已用 TODO
在注释中标明。
- 设置设备证书
例程使用的设备证书是公用的, 所以应用于实际业务时, 请替换如下的
TODO
部分, 传入用户自己真实的设备证书。。/* TODO: 替换为自己设备的设备证书 */ char *product_key = "a1dhWXXXXXX"; char *device_name = "cota_basic_demo"; char *device_secret = "wTvkvSgmYiCULwzi5IqsxEoN6iXXXXXX";
- 建立MQTT连接
OTA过程需要从MQTT通道接收远程配置推送消息, 所以程序的主体中, 建立MQTT连接仍是必须的。
int main(int argc, char *argv[]) { ... /* 创建1个MQTT客户端实例并内部初始化默认参数 */ mqtt_handle = aiot_mqtt_init(); /* 配置MQTT服务器地址 */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)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);
- 创建OTA会话实例
与单纯MQTT连云场景不同的是, 还应为OTA会话创建单独的实例, 并将它与MQTT会话实例关联起来, 再发起MQTT连接。
/* 用以下语句, 把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) { printf("aiot_mqtt_connect failed: -0x%04X\r\n", -res); /* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */ goto exit; }
说明 上述代码段同时也给OTA会话设置了数据接收回调, 为demo_ota_recv_handler
函数。 - 设备请求获取远程配置
/* 发送请求, 获取云端的远程配置 */ { char *topic_string = "/sys/a1dhWKuKqX5/cota_basic_demo/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; } }
- 从网络上轮询mqtt报文和远程配置的报文
如果g_dl_handle指针会为空, 则只从网络上轮询mqtt报文。
如果g_dl_handle指针为非空, 会去调用aiot_download_recv函数通过。
https协议去服务器收取远程配置, 结束后会通过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); } }
- 远程配置消息到达, 开始下载
以上程序主体已讲解完毕, 此后, 如果SDK从MQTT通道收到了远程配置的消息, 则会调用aiot_download_send_request向服务器发起下载请求。
把g_dl_handle指针赋一个非空的值, 触发上述while循环从网络上轮询远程配置的报文。
/* 用户通过 aiot_ota_setopt() 注册的OTA消息处理回调, 如果SDK收到了OTA相关的MQTT消息, 会自动识别, 调用这个回调函数 */ void demo_ota_recv_handler(void *ota_handle, aiot_ota_recv_t *ota_msg, void *userdata) { switch (ota_msg->type) { case AIOT_OTARECV_COTA: { 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_crt; cred.x509_server_cert_len = strlen(ali_ca_crt); /* 设置下载时为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 *)(user_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; } } g_dl_handle = dl_handle; break; } default: break; } }
- 处理下载内容
SDK收到的远程配置内容, 会进入用户通过
aiot_download_setopt()
设置的收包回调, 告知用户, 用户应在这里处理下载内容。/* 下载收包回调, 用户调用 aiot_download_recv() 后, SDK收到数据会进入这个函数, 把下载到的数据交给用户 */ /* TODO: 在用户收到远程配置的数据后, 需要自己决定如何处理 */ 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(handle, percent); printf("config len is %d, config content is %.*s\r\n", src_buffer_len, src_buffer_len, (char *)src_buffer); }
- 观察运行日志
以下就是在控制台网页部署完远程配置信息后, 编译运行
output/cota-basic-demo
, 设备向控制台发送远程配置请求从而获得控制台推送的远程配置, 得到的一次完整运行日志。$./output/cota-basic-demo [1582113704.433][LK-0313] MQTT user calls aiot_mqtt_connect api, connect [1582113704.433][LK-0317] cota_basic_demo&a1dhWKuKqX5 [1582113704.433][LK-0318] AD97391F4E5BAA6FFB7F62F9D15EA6FD1ED5AB20E82B73686E21D44110E6BE51 [1582113704.433][LK-0319] a1dhWKuKqX5.cota_basic_demo|timestamp=2524608000000,_ss=1,_v=sdk-c-4.0.0,securemode=2,signmethod=hmacsha256,ext=1,| establish mbedtls connection with server(host='a1dhWKuKqX5.iot-as-mqtt.cn-shanghai.aliyuncs.com', port=[443]) success to establish mbedtls connection, fd = 3(cost 44763 bytes in total, max used 47675 bytes) [1582113704.488][LK-0313] MQTT connect success in 46 ms AIOT_MQTTEVT_CONNECT [1582113704.488][LK-0309] pub: /sys/a1dhWXXXXXX/cota_basic_demo/thing/config/get [LK-030A] > 7B 22 69 64 22 3A 22 31 32 33 22 2C 22 70 61 72 | {"id":"123","par [LK-030A] > 61 6D 73 22 3A 7B 22 63 6F 6E 66 69 67 53 63 6F | ams":{"configSco [LK-030A] > 70 65 22 3A 22 70 72 6F 64 75 63 74 22 2C 22 67 | pe":"product","g [LK-030A] > 65 74 54 79 70 65 22 3A 22 66 69 6C 65 22 7D 7D | etType":"file"}} [1582113704.511][LK-0309] pub: /sys/a1dhWXXXXXX/cota_basic_demo/thing/config/get_reply [LK-030A] < 7B 22 63 6F 64 65 22 3A 32 30 30 2C 22 64 61 74 | {"code":200,"dat [LK-030A] < 61 22 3A 7B 22 63 6F 6E 66 69 67 49 64 22 3A 22 | a":{"configId":" [LK-030A] < 31 31 64 32 38 62 34 31 32 36 62 61 34 66 62 33 | 11d28b4126ba4fb3 [LK-030A] < 61 30 61 63 61 64 31 32 32 63 61 37 31 37 65 61 | a0acad122ca717ea [LK-030A] < 22 2C 22 63 6F 6E 66 69 67 53 69 7A 65 22 3A 32 | ","configSize":2 [LK-030A] < 37 2C 22 67 65 74 54 79 70 65 22 3A 22 66 69 6C | 7,"getType":"fil [LK-030A] < 65 22 2C 22 73 69 67 6E 22 3A 22 38 37 39 65 35 | e","sign":"879e5 [LK-030A] < 39 31 33 34 63 36 61 34 34 39 35 34 37 63 62 33 | 9134c6a449547cb3 [LK-030A] < 63 65 32 35 66 64 32 38 32 63 64 61 65 66 30 34 | ce25fd282cdaef04 [LK-030A] < 36 65 38 64 34 38 34 30 65 32 35 63 30 31 63 64 | 6e8d4840e25c01cd [LK-030A] < 66 39 66 32 38 64 66 64 66 62 64 22 2C 22 73 69 | f9f28dfdfbd","si [LK-030A] < 67 6E 4D 65 74 68 6F 64 22 3A 22 53 68 61 32 35 | gnMethod":"Sha25 [LK-030A] < 36 22 2C 22 75 72 6C 22 3A 22 68 74 74 70 73 3A | 6","url":"https: [LK-030A] < 2F 2F 6F 74 78 2D 64 65 76 69 63 65 63 65 6E 74 | //otx-devicecent [LK-030A] < 65 72 2D 74 68 69 6E 67 2D 63 6F 6E 66 69 67 2D | er-thing-config- [LK-030A] < 63 6E 2D 73 68 61 6E 67 68 61 69 2D 6F 6E 6C 69 | cn-shanghai-onli [LK-030A] < 6E 65 2E 6F 73 73 2D 63 6E 2D 73 68 61 6E 67 68 | ne.oss-cn-shangh [LK-030A] < 61 69 2E 61 6C 69 79 75 6E 63 73 2E 63 6F 6D 2F | ai.aliyuncs.com/ [LK-030A] < 61 31 64 68 57 4B 75 4B 71 58 35 2F 31 35 38 30 | a1dhWKuKqX5/1580 [LK-030A] < 30 35 33 36 34 33 30 36 31 5F 51 4A 6A 4F 66 43 | 053643061_QJjOfC [LK-030A] < 78 43 3F 45 78 70 69 72 65 73 3D 31 35 38 32 31 | xC?Expires=15821 [LK-030A] < 31 35 35 30 34 26 4F 53 53 41 63 63 65 73 73 4B | 15504&OSSAccessK [LK-030A] < 65 79 49 64 3D 4C 54 41 49 52 59 33 72 78 35 64 | eyId=LTAIRY3rx5d [LK-030A] < 67 32 4A 42 6D 26 53 69 67 6E 61 74 75 72 65 3D | g2JBm&Signature= [LK-030A] < 36 69 50 63 6A 6E 51 30 65 6F 75 50 63 44 56 25 | 6iPcjnQ0eouPcDV% [LK-030A] < 32 42 78 77 5A 4B 65 78 30 31 78 51 63 25 33 44 | 2BxwZKex01xQc%3D [LK-030A] < 22 7D 2C 22 69 64 22 3A 22 31 32 33 22 2C 22 6D | "},"id":"123","m [LK-030A] < 65 74 68 6F 64 22 3A 22 74 68 69 6E 67 2E 63 6F | ethod":"thing.co [LK-030A] < 6E 66 69 67 2E 67 65 74 22 2C 22 76 65 72 73 69 | nfig.get","versi [LK-030A] < 6F 6E 22 3A 22 31 2E 30 22 7D | on":"1.0"} configId: 11d28b4126ba4fb3a0acad122ca717ea, configSize: 27 Bytes establish mbedtls connection with server(host='otx-devicecenter-thing-config-cn-shanghai-online.oss-cn-shanghai.aliyuncs.com', port=[443]) success to establish mbedtls connection, fd = 4(cost 54383 bytes in total, max used 57119 bytes) [1582113704.566][LK-040B] > GET /a1dhWKuKqX5/1580053643061_QJjOfCxC?Expires=1582115504&OSSAccessKeyId=LTAIRY3rx5dg2JBm&Signature=6iPcjnQ0eouPcDV%2BxwZKex01xQc%3 [1582113704.566][LK-040B] > Host: otx-devicecenter-thing-config-cn-shanghai-online.oss-cn-shanghai.aliyuncs.com [1582113704.566][LK-040B] > Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8 [1582113704.566][LK-040B] > Range: bytes=0- [1582113704.566][LK-040B] > Content-Length: 0 [1582113704.566][LK-040B] > [1582113704.566][LK-0309] pub: /ota/device/progress/a1dhWKuKqX5/cota_basic_demo [LK-030A] > 7B 22 69 64 22 3A 30 2C 20 22 70 61 72 61 6D 73 | {"id":0, "params [LK-030A] > 22 3A 7B 22 73 74 65 70 22 3A 22 30 22 2C 22 64 | ":{"step":"0","d [LK-030A] > 65 73 63 22 3A 22 22 7D 7D | esc":""}} [1582113704.588][LK-040D] < HTTP/1.1 206 Partial Content [1582113704.588][LK-040D] < Server: AliyunOSS [1582113704.588][LK-040D] < Date: Wed, 19 Feb 2020 12:01:44 GMT [1582113704.588][LK-040D] < Content-Type: application/octet-stream [1582113704.588][LK-040D] < Content-Length: 27 [1582113704.588][LK-040D] < Connection: keep-alive [1582113704.588][LK-040D] < x-oss-request-id: 5E4D23A841C1B03839D1C7A9 [1582113704.588][LK-040D] < Content-Range: bytes 0-26/27 [1582113704.588][LK-040D] < Accept-Ranges: bytes [1582113704.588][LK-040D] < ETag: "5BBE66A8F10381667E587A8F3CB1877C" [1582113704.588][LK-040D] < Last-Modified: Sun, 26 Jan 2020 15:47:23 GMT [1582113704.588][LK-040D] < x-oss-object-type: Normal [1582113704.588][LK-040D] < x-oss-hash-crc64ecma: 14994493172188797654 [1582113704.588][LK-040D] < x-oss-storage-class: Standard [1582113704.588][LK-040D] < Content-MD5: W75mqPEDgWZ+WHqPPLGHfA== [1582113704.588][LK-040D] < x-oss-server-time: 22 [1582113704.588][LK-040D] < [1582113704.588][LK-0901] digest matched [1582113704.588][LK-0309] pub: /ota/device/progress/a1dhWKuKqX5/cota_basic_demo [LK-030A] > 7B 22 69 64 22 3A 31 2C 20 22 70 61 72 61 6D 73 | {"id":1, "params [LK-030A] > 22 3A 7B 22 73 74 65 70 22 3A 22 31 30 30 22 2C | ":{"step":"100", [LK-030A] > 22 64 65 73 63 22 3A 22 22 7D 7D | "desc":""}} config len is 27, config content is {"remote_config_demo":1234} heartbeat response
远程配置的关键日志举例:
configId: 11d28b4126ba4fb3a0acad122ca717ea, configSize: 27 Bytes
。固件升级的关键日志举例:
OTA target firmware version: 1.1.0, size: 1248306 Bytes
。从上述关键关键日志中, 可以区分当前是远程配置还是固件升级。
在文档使用中是否遇到以下问题
更多建议
匿名提交