优特云
[ { "name": "首页", "url": "http://www.utyun.com/", "target": "0", "child": [] }, { "name": "活动", "url": "https://pre-mobile.utyun.com/page-design/?__UT_TITLE__=%E4%BC%98%E7%89%B9%E4%BA%91%E7%A7%91%E6%8A%80-%E4%BA%91%E6%9C%8D%E5%8A%A1%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88%E5%AE%9A%E5%88%B6%E5%95%86#/8412299005246101317/events", "target": "0", "child": [] }, { "name": "产品", "url": "https://pre-mobile.utyun.com/page-design/?__UT_TITLE__=%E4%BC%98%E7%89%B9%E4%BA%91%E7%A7%91%E6%8A%80-%E4%BA%91%E6%9C%8D%E5%8A%A1%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88%E5%AE%9A%E5%88%B6%E5%95%86#/8412299005246101317/products", "target": "0", "child": [] }, { "name": "关于优特云", "url": "https://pre-mobile.utyun.com/page-design/?__UT_TITLE__=%E4%BC%98%E7%89%B9%E4%BA%91%E7%A7%91%E6%8A%80-%E4%BA%91%E6%9C%8D%E5%8A%A1%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88%E5%AE%9A%E5%88%B6%E5%95%86#/8412299005246101317/about", "target": "0", "child": [] } ]
硬件云(公网版)
硬件云(公网版)API文档

伟 最后一次编辑

4 年多前 304

云端API列表(共56个)

序号接口名称接口描述版本
设备管理(开发者)
1创建设备 调用该接口在指定产品下注册设备。此接口需要权限:设备开发者  V1.00
2批量创建设备(随机名称)调用该接口在指定产品下批量创建多个设备(随机生成设备名)。此接口需要权限:设备开发者V1.00
3批量更新设备调用该接口批量修改设备。此接口需要权限:设备拥有者V1.00
4删除设备调用该接口删除指定设备。此接口需要权限:设备拥有者V1.00
5解绑设备调用该接口解绑设备。此接口需要权限:设备开发者。注:此接口清谨慎使用V1.00
6禁用设备调用该接口禁用设备。此接口需要权限:设备拥有者V1.00
7解禁设备调用该接口解禁设备。此接口需要权限:设备拥有者V1.00
8查询设备详情调用该接口查询指定设备的详细信息。此接口需要权限:设备拥有者V1.00
9批量查询设备调用该接口批量查询设备。此接口需要权限:设备开发者V1.00
10批量查询设备状态调用该接口查看指定设备的运行状态。此接口需要权限:设备拥有者V1.00
11查询子设备(分页)调用该接口查询网关子设备列表(分页)。此接口需要权限:设备开发者V1.00
12查询批量申请批次列表调用该接口查询批量申请设备列表。此接口需要权限:设备开发者V1.00
13查询批量申请批次详情调用该接口查询批量申请设备详情。此接口需要权限:设备开发者V1.00
14查询批量申请批次详情(全量)调用该接口查询批量申请设备详情(全量)。此接口需要权限:设备开发者V1.00
15调用设备服务同步调用服务,最大超时时间为5秒。若5秒内服务器未收到回复,则返回超时错误。此接口需要权限:设备拥有者V1.00
16批量调用设备服务只支持异步调用该接口。此接口需要权限:设备拥有者V1.00
17查询设备属性值调用该接口查询指定设备的属性。此接口需要权限:设备拥有者V1.00
18查询设备期望属性值调用该接口为指定设备批量设置期望属性值。限制说明:只读属性不支持设置期望属性值。一次调用最多可设置10个期望属性值。设备创建后,期望属性值的版本(Version)为0。首次设置期望属性值时,如果指定Version参数,则需指定Version值为0。此接口需要权限:设备拥有者V1.00
19设置设备属性值  因为云端下发属性设置命令和设备收到并执行该命令是异步的,所以调用该接口时,返回的成功结果只表示云端下发属性设置的请求成功,不能保证设备端收到并执行了该请求。需设备端SDK成功响应云端设置设备属性值的请求,设备属性值才能真正设置成功。 此接口需要权限:设备拥有者  V1.00
20批量设置设备属性值调用该接口批量设置设备属性值。此接口需要权限:设备拥有者V1.00
21批量查询设备属性历史数据  调用该接口批量查询指定设备的属性上报数据。注: 一次调用最多可查询10个属性的历史数据。 每个属性最多返回100条数据。 仅能查询最近30天内的属性数据 此接口需要权限:设备拥有者  V1.00
22批量设置设备期望属性值调用该接口为指定设备批量设置期望属性值。限制说明:只读属性不支持设置期望属性值。一次调用最多可设置10个期望属性值。设备创建后,期望属性值的版本(Version)为0。首次设置期望属性值时,如果指定Version参数,则需指定Version值为0。此接口需要权限:设备拥有者V1.00
23查询设备属性历史数据调用该接口查询指定设备的属性记录。仅能查询最近30天内的属性数据。此接口需要权限:设备拥有者V1.00
24查询设备服务历史数据调用该接口查询指定设备的服务记录。仅能查询最近30天内的属性数据。此接口需要权限:设备拥有者V1.00
25查询设备事件历史数据调用该接口查询指定设备的事件记录。此接口需要权限:设备拥有者V1.00
26查询设备标签列表调用该接口查询指定设备的标签列表。此接口需要权限:设备开发者V1.00
27全量更新设备标签调用该接口为指定设备全量更新标签,设备最大标签数为10个。此接口需要权限:设备开发者V1.00
28增量更新设备标签调用该接口为指定设备增量更新标签,产品最大标签数为10个。此接口需要以下功能权限:设备开发者V1.00
设备管理(用户)
29批量更新设备调用该接口批量修改设备。此接口需要权限:设备拥有者V1.00
30绑定设备调用该接口绑定设备。此接口需要以下功能权限:设备用户V1.00
31解绑设备调用该接口解绑设备。此接口需要以下功能权限:设备用户V1.00
32批量解绑设备调用该接口批量解绑设备。此接口需要以下功能权限:设备用户V1.00
33批量更新设备备注名称调用该接口批量修改设备备注名称。此接口需要权限:设备拥有者  V1.00
34查询设备详情调用该接口查询指定设备的详细信息。此接口需要以下功能权限:设备用户V1.00
35批量查询设备调用该接口批量批量查询设备。此接口需要以下功能权限:设备用户V1.00
36批量查询设备详情调用该接口批量查询指定设备的详细信息。此接口需要以下功能权限:设备用户V1.00
37批量查询设备状态调用该接口查看指定设备的运行状态。此接口需要权限:设备用户V1.00
38查询子设备(分页)  调用该接口查询网关子设备列表(分页)。此接口需要权限:设备用户V1.00
39全量更新设备标签调用该接口为指定设备全量更新标签,设备最大标签数为10个。此接口需要权限:设备用户V1.00
40增量更新设备标签 调用该接口为指定设备增量更新标签,产品最大标签数为10个。此接口需要以下功能权限:设备用户  V1.00
41查询设备标签列表 调用该接口查询指定设备的标签列表。此接口需要权限:设备用户  V1.00
42查询设备属性值调用该接口查询指定设备的属性。此接口需要权限:设备用户V1.00
43设置设备属性值  因为云端下发属性设置命令和设备收到并执行该命令是异步的,所以调用该接口时,返回的成功结果只表示云端下发属性设置的请求成功,不能保证设备端收到并执行了该请求。需设备端SDK成功响应云端设置设备属性值的请求,设备属性值才能真正设置成功 此接口需要权限:设备用户  V1.00
44批量设置设备属性值调用该接口批量设置设备属性值。此接口需要权限:设备用户V1.00
45批量设置设备期望属性值调用该接口为指定设备批量设置期望属性值。限制说明:只读属性不支持设置期望属性值。一次调用最多可设置10个期望属性值。设备创建后,期望属性值的版本(Version)为0。首次设置期望属性值时,如果指定Version参数,则需指定Version值为0。此接口需要权限:设备用户V1.00
46查询设备期望属性值调用该接口为指定设备批量设置期望属性值。限制说明:只读属性不支持设置期望属性值。一次调用最多可设置10个期望属性值。设备创建后,期望属性值的版本(Version)为0。首次设置期望属性值时,如果指定Version参数,则需指定Version值为0。此接口需要权限:设备用户V1.00
47调用设备服务 同步调用服务,最大超时时间为5秒。若5秒内服务器未收到回复,则返回超时错误。此接口需要权限:设备用户  V1.00
48批量调用设备服务只支持异步调用该接口。此接口需要权限:设备用户V1.00
49全量更新设备标签调用该接口为指定设备全量更新标签,设备最大标签数为10个。此接口需要权限:设备用户V1.00
50查询设备标签列表调用该接口查询指定设备的标签列表。此接口需要权限:设备用户V1.00
产品管理(开发者)
51查询产品(单个)根据产品key查询产品详细信息。此接口需要以下功能权限:产品拥有者V1.00
52分页查询产品信息  返回产品信息分页数据。此接口需要以下功能权限:产品拥有者  V1.00
53查询产品标签根据产品key返回产品标签列表V1.00
54全量更新多个产品标签根据产品key全量更新产品标签信息,产品最大标签数为10个。此接口需要以下功能权限:产品拥有者V1.00
55增量更新多个产品标签根据产品key增量更新产品标签信息,标签值为null即删除标签,产品最大标签数为10个。此接口需要以下功能权限:产品拥有者V1.00
产品管理(用户)
56查询产品信息(分页)返回产品信息分页数据。V1.00

3.x版本设备端接口(共54个)

序号接口名称接口描述版本
基础HAL V3.2
1HAL_Free释放参数ptr指向的一块堆内存, 当传入的参数为NULL时不执行任何操作
2HAL_GetChipID  获取唯一的芯片ID字符串, 字符串长度不能超过HAL_CID_LEN定义的数值
注:该HAL只需要芯片商进行适配,如果用户不是芯片商,该HAL返回空字符串即可  
3HAL_GetDeviceID获取设备的DeviceID, 用于标识设备单品的ID
4HAL_GetDeviceName获取设备的DeviceName, 用于唯一标识单个设备的名字, 三元组之一, 在云端控制台注册得到并烧写到设备中
5HAL_GetDeviceSecret获取设备的DeviceSecret, 用于标识单个设备的密钥, 三元组之一, 在云端控制台注册得到并烧写到设备中
6HAL_GetFirmwareVersion获取设备的固件版本字符串, 此固件版本号将会用于OTA升级的版本上报。如果设备不准备支持OTA,该函数返回空串即可。
7HAL_GetModuleID获取设备的Module ID, 仅用于紧密合作伙伴。该函数用于模组商上报模组型号,其它角色的用户返回空串即可。
8HAL_GetPartnerID获取设备的Partner ID, 仅用于紧密合作伙伴。
9HAL_GetProductKey获取设备的ProductKey, 用于标识设备的品类, 三元组之一, 在云端控制台注册得到并烧写到设备中
10HAL_GetProductSecret获取设备的ProductSecret, 用于标识品类的密钥, 在云端控制台注册得到并烧写到设备中, 在一型一密的场景下将会使用到此字符串
11HAL_GetTimeStr获取当前时间字符串
12HAL_Kv_Del删除指定KV数据, 删除key对应的KV对数据, 可以通过擦除Flash或修改文件数据的方式实现持久化数据的删除
13HAL_Kv_Erase_All擦除所有的KV数据, 可以通过擦除Flash或修改文件数据的方式实现持久化数据的删除
14HAL_Kv_Get获取KV数据, 获取key对应的KV对数据, 可以通过读取Flash或读取文件的方式实现持久化数据的读取
15HAL_Kv_Set设置KV数据接口, 可通过写flash或写文件的方式实现数据持久化
16HAL_Malloc申请一块堆内存
17HAL_Printf打印函数, 用于向串口或其它标准输出打印日志或调试信息, 可参考C99的printf()函数实现
18HAL_Random随机数函数, 接受一个无符号数作为范围, 返回0到region范围内的一个随机数
19HAL_Reboot设备重启, 调用该接口能实现复位功能
20HAL_SetDeviceName设置设备的DeviceName, 用于标识设备单品的名字, 三元组之一
21HAL_SetDeviceSecret设置设备的DeviceSecret, 用于标识设备单品的密钥, 三元组之一
22HAL_SetProductKey设置设备的ProductKey, 用于标识设备的品类, 三元组之一
23HAL_SetProductSecret设置设备的ProductSecret, 用于标识品类的密钥, 在一型一密场景中会使用到此字符串
24HAL_SleepMs睡眠函数, 使当前执行线程睡眠指定的毫秒数
25HAL_Snprintf 打印函数, 向内存缓冲区格式化构建一个字符串, 参考C99标准库函数
26HAL_Srandom随机数播种函数, 使 HAL_Random 的返回值每个随机序列各不相同, 类似C标准库中的srand
27HAL_Sys_reboot系统立即重启
28HAL_Timer_Create创建指定名称的定时器, 同时注册用户回调函数和用户数据
29HAL_Timer_Delete删除由HAL_Timer_Create()创建的定时器, 释放资源
30HAL_Timer_Start启动定时器
31HAL_Timer_Start关闭定时器
32HAL_UptimeMs获取设备从上电到当前时刻所经过的毫秒数
33HAL_UTC_Get获取UTC时间, 数值为从Epoch(1970年1月1日00:00:00 UTC)开始所经过的秒数单位
34HAL_UTC_Set设置UTC时间, 设置参数为从Epoch(1970年1月1日00:00:00 UTC)开始所经过的秒数单位
35HAL_Vsnprintf格式化输出字符串到指定buffer中, 可参考C标准库的vsnprintf()实现
MQTT连云HAL
36HAL_SSL_Destroy销毁由参数handle指定的TLS连接
37HAL_SSL_Establish根据指定的服务器网络地址, 服务器端口号和证书文件建立TLS连接, 返回对应的连接句柄
38HAL_SSL_Read从指定的TLS连接中读取数据, 此接口为同步接口, 如果在超时时间内读取到参数len指定长度的数据则立即返回, 否则在超时时间到时才解除阻塞返回
39HAL_SSL_Write从指定的TLS连接中写入数据, 此接口为同步接口, 如果在超时时间内写入了参数len指定长度的数据则立即返回, 否则在超时时间到时才解除阻塞返回
40HAL_TCP_Destroy销毁由参数fd指定的TCP连接, 释放资源
41HAL_TCP_Establish根据指定的服务器网络地址和端口号建立TCP连接, 并返回对应连接句柄
42HAL_TCP_Read从指定的TCP连接中读取数据, 此接口为同步接口, 如果在超时时间内读取到参数len指定长度的数据则立即返回, 否则在超时时间到时才解除阻塞返回
43HAL_TCP_Write从指定的TCP连接中写入数据, 此接口为同步接口, 如果在超时时间内写入了参数len指定长度的数据则立即返回, 否则在超时时间到时才解除阻塞返回
线程HAL
44HAL_MutexCreate创建一个互斥量对象, 返回指向所创建互斥量的指针, 用于同步访问, 对于仅支持单线程应用, 可实现为空函数
45HAL_MutexDestroy销毁一个互斥量对象, 释放资源
46HAL_MutexLock锁住一个互斥量
47HAL_MutexUnlock解锁一个互斥量
48HAL_SemaphoreCreate创建一个计数信号量, 此接口实现必须为原子操作, 对于仅支持单线程应用, 可实现为空函数
49HAL_SemaphoreDestroy销毁一个由参数sem指定的信号量, 此接口实现必须为原子操作, 此函数无返回值
50HAL_SemaphorePost在指定的计数信号量上做自增操作, 解除其它线程的等待, 此接口实现必须为原子操作, 对于仅支持单线程应用, 可实现为空函数
51HAL_SemaphoreWait在指定的计数信号量上等待并做自减操作, 对于仅支持单线程应用, 此接口实现必须为原子操作, 可实现为空函数
52HAL_ThreadCreate按照指定入参创建一个线程
53HAL_ThreadDelete删除指定的线程
54HAL_ThreadDelete将指定线程设置为分离状态

4.x版本设备端接口(共54个)

序号接口名称接口描述版本
基础HAL接口v4.x
1core_sysdep_malloc释放内存
2core_sysdep_free释放内存 
3core_sysdep_time获取当前时间戳,SDK用于计算差值
4core_sysdep_sleep睡眠指定的毫秒数
5core_sysdep_network_init创建一个网络会话
6core_sysdep_network_setopt配置一个网络会话的连接参数
7core_sysdep_network_establish建立一个网络会话,作为MQTT/HTTP等协议的底层承载
8core_sysdep_network_send在指定的网络会话上发送
9core_sysdep_network_deinit销毁一个网络会话
10core_sysdep_rand随机数的生成方法
11core_sysdep_mutex_init创建互斥锁
12core_sysdep_mutex_lock申请互斥锁
13core_sysdep_mutex_unlock释放互斥锁
14core_sysdep_mutex_deinitcore_sysdep_mutex_deinit
MQTT连云接口
15aiot_mqtt_init创建mqtt客户端实例并设置默认参数
16aiot_mqtt_setopt设置mqtt连接参数
17aiot_mqtt_deinit释放mqtt客户端实例的资源
18aiot_mqtt_connect与mqtt服务器建立连接, 同步接口
19aiot_mqtt_disconnect与mqtt服务器断开连接但保留客户端实例的资源, 同步接口 
20aiot_mqtt_heartbeat向mqtt服务器发送PINGREQ报文, 维持连接, 仅在特殊场景使用 
21aiot_mqtt_process包含定时心跳发送和QoS1消息的重传, 同步接口, 需要用户周期性调用
22aiot_mqtt_pub发送一条PUBLISH消息到mqtt服务器, 异步接口, 用于设备上报, 消息QoS为0 
23aiot_mqtt_sub发送一条SUBSCRIBE消息到mqtt服务器, 异步接口, 用于启动对云端消息的接收 
24aiot_mqtt_unsub发送一条UNSUBSCRIBE消息到mqtt服务器, 异步接口, 用于停止对云端消息的接收 
25aiot_mqtt_recv尝试收取mqtt报文并分发给用户的订阅回调函数, 同步接口 
物模型接口
26aiot_dm_init初始化data-model会话实例 
27aiot_dm_setopt设置data-model会话参数
28aiot_dm_send发送一条data-model消息到物联网平台, 消息类型和消息内容由msg参数决定 
29aiot_dm_deinit销毁data-model实例, 释放资源 
时间同步接口
30aiot_ntp_init初始化ntp实例并设置默认参数 
31aiot_ntp_setopt配置ntp实例 
32aiot_ntp_deinit释放ntp实例句柄的资源 
33aiot_ntp_send_request向MQTT服务器发送ntp查询请求 
OTA接口
34aiot_ota_init创建ota客户端实例并设置默认参数 
35aiot_ota_deinit销毁ota客户端实例, 回收资源 
36aiot_ota_report_version用MQTT报文向云平台上报自身设备的固件版本号, 这是云端判断升级是否启动和成功的依据
37aiot_ota_report_version_ext用MQTT报文向云平台上报指定设备的固件版本号, 用于网关代理子设备上报的场景 
38aiot_ota_setopt设置ota连接参数
39aiot_download_init创建download客户端实例并设置默认参数 
40aiot_download_deinit销毁download客户端实例, 回收资源 
41aiot_download_setopt设置download连接参数
42aiot_download_report_progress用MQTT报文向云平台上报下载的进度, 也可以上报升级过程中发生的异常, 例如烧写失败, 网络断开等
43aiot_download_send_request向aiot_download_setopt()指定的固件存储服务器, 发起HTTP GET请求, 要求下载一段固件内容 
44aiot_download_recv从网络上收取来自服务器的固件内容, 然后进入AIOT_DLOPT_RECV_HANDLER选项设置的用户回调, 将下载内容传给用户 
子设备接口
45aiot_subdev_init初始化subdev实例并设置默认参数。
46aiot_subdev_setopt配置subdev实例
47aiot_subdev_deinit 释放subdev实例句柄的资源。
48aiot_subdev_send_topo_add向云端发送添加子设备与网关的拓扑关系的请求。
49aiot_subdev_send_topo_delete向云端发送删除子设备与网关的拓扑关系的请求。
50aiot_subdev_send_topo_get向云端发送获取子设备与网关的拓扑关系的请求。
51aiot_subdev_send_batch_login向云端发送子设备批量登录的请求。
52aiot_subdev_send_batch_logout向云端发送子设备批量登出的请求。
53aiot_subdev_send_sub_register向云端发送子设备动态注册的请求。
54aiot_subdev_send_product_register向云端发送子设备动态注册的请求(可从其他网关抢绑)。

云端接口

写在前面

API规范说明

  1. 查询数据使用GET方式,新增、修改、删除数据使用POST方式
  2. 返回数据统一使用JSON格式,请参考每个API具体说明
  3. 访问API出现异常需返回统一错误信息,返回JSON格式和响应码
{ "utCode":"parameter invalid", //错误码 "utMsg":"参数非法", //错误说明 "utService":"iot-cloud-app-service" //API服务提供方 }
  • 错误码说明一览
  注:更详细的接口规范请参考:点我查看

设备管理(开发者)

创建设备

基本信息
Path: /device/create
Method: POST
接口描述: 调用该接口在指定产品下注册设备。此接口需要权限:设备开发者
请求参数
Headers
参数名称参数值是否必须 示例备注
Content-Typeapplication/json
Body
名称类型是否必须默认值备注其他信息
deviceNamestring非必须设备名称,长度为4-32个字符,可以包含英文字母、数字和特殊字符:连字符(-)、下划线(_)、at符号(@)、点号(.)、和英文冒号(:)。最大长度: 32最小长度: 4
indexinteger非必须自定义下标,通过维护此字段实现自定义排序format: int64
nicknamestring非必须备注名称,长度为4-64个字符,可包含中文汉字、英文字母、数字和下划线(_)。一个中文汉字算2字符。最大长度: 64最小长度: 4
productKeystring必须隶属的产品key
返回数据
名称类型是否必须默认值备注其他信息
deviceNamestring非必须设备名称
deviceSecretstring非必须设备密钥

批量创建设备(随机名称)

基本信息
Path: /device/batchCreate
Method: POST
接口描述: 调用该接口在指定产品下批量创建多个设备(随机生成设备名)。此接口需要权限:设备开发者
请求参数
Headers
参数名称参数值是否必须示例备注
Content-Type application/json  
Body
名称类型是否必须默认值备注其他信息
countinteger必须要创建的设备数量,取值不能大于1,000format: int32
indexinteger非必须自定义下标,若传入则批量创建设备的自定义下标将根据此值依次递增format: int64
productKeystring必须产品Key
返回数据
名称类型是否必须默认值备注其他信息
applyIdinteger非必须申请批次IDformat: int64

批量更新设备

基本信息
Path: /device/batchUpdate
Method: POST
接口描述: 调用该接口批量修改设备。此接口需要权限:设备拥有者
请求参数
Headers
参数名称参数值是否必须示例备注
Content-Typeapplication/json
Body
名称类型是否必须默认值备注其他信息
updateDeviceInfosobject []必须item 类型: object
├─ deviceNamestring必须设备名称
├─ indexinteger必须自定义下标format: int64
├─ productKeystring必须产品Key
返回数据
ok

删除设备

基本信息
Path: /device/delete
Method: POST
接口描述: 调用该接口删除指定设备。此接口需要权限:设备拥有者
请求参数
Headers
参数名称参数值是否必须示例备注
Content-Typeapplication/json
Body
名称类型是否必须默认值备注其他信息
deviceNamestring必须设备名称
productKeystring必须产品Key
返回数据
ok

解绑设备

基本信息
Path: /device/unbind
Method: POST
接口描述: 调用该接口解绑设备。此接口需要权限:设备开发者。注:此接口清谨慎使用
请求参数
Headers
参数名称参数值是否必须  示例备注
Content-Typeapplication/json
Body
名称类型是否必须默认值备注其他信息
deviceNamestring必须设备名称
productKeystring必须产品Key
返回数据
ok

禁用设备

基本信息
Path: /device/disableThing
Method: POST
接口描述: 调用该接口禁用设备。此接口需要权限:设备拥有者
请求参数
Headers
参数名称参数值是否必须示例备注
Content-Typeapplication/json
Body
名称类型是否必须默认值备注其他信息
deviceNamestring必须设备名称
productKeystring必须产品Key
返回数据
ok

解禁设备

基本信息
Path: /device/enableThing
Method: POST
接口描述: 调用该接口解禁设备。此接口需要权限:设备拥有者
请求参数
Headers
参数名称参数值是否必须示例备注
Content-Typeapplication/json
Body
名称类型是否必须默认值备注其他信息
deviceNamestring必须设备名称  
productKeystring必须产品Key
返回数据
ok

查询设备详情

基本信息
Path: /device/get
Method: GET
接口描述: 调用该接口查询指定设备的详细信息。此接口需要权限:设备拥有者
请求参数
Query
参数名称是否必须示例备注
deviceName设备名称
productKey产品Key
返回数据
名称类型是否必须默认值备注其他信息
deviceNamestring非必须 设备名称
deviceSecretstring非必须设备密钥
gmtActivestring非必须设备的激活时间
gmtCreatestring非必须创建时间
gmtOnlinestring非必须设备最近一次上线的时间
imageKeystring非必须产品图片Key。通过该图片Key可以从文件服务中获取图片内容
indexinteger非必须自定义下标,通过维护此字段实现自定义排序format: int64
instanceIdstring非必须实例ID
ipAddressstring非必须设备IP地址
nicknamestring非必须备注名称
parentDeviceNamestring非必须父设备名称
parentProductKeystring非必须父产品Key
productKeystring非必须产品key
productNamestring非必须产品名称
productNodeTypestring非必须节点类型,取值:0:普通设备,1:普通网关,2:边缘网关,4:虚拟子设备
statusstring非必须设备状态。取值:ONLINE:设备在线。OFFLINE:设备离线。UNACTIVE:设备未激活。DISABLE:设备已禁用。
subDeviceNamestring非必须子设备名称
subProductKeystring非必须子产品Key
tagsobject非必须返回的设备标签信息JSON对象。key为标签键,value为标签值,例:{"tagKey":"tagValue"}
utcActivestring非必须设备的激活时间(UTC)
utcCreatestring非必须设备的创建时间(UTC)
utcOnlinestring非必须设备最近一次上线的时间(UTC)

批量查询设备

基本信息
Path: /device/list
Method: GET
接口描述: 调用该接口批量查询设备。此接口需要权限:设备开发者
请求参数
Query
参数名称是否必须示例备注
deviceName设备名称(模糊查询)
networkType联网方式。取值:‘WIFI’:WiFi、‘ETHERNET’:以太网、‘BLUETOOTH’:蓝牙
nickname备注名称
ownerId设备用户Id
pageNumber查询的页码(0..N)  
pageSize页大小
productGroup产品分组
productKey产品Key
productName产品名称
productNodeType节点类型,取值:0:普通设备,2:边缘网关,4:虚拟子产品
productNodeTypes查询节点类型集合,取值:0:普通设备,2:边缘网关,4:虚拟子产品
productType产品类型
sort排序条件,格式: 排序字段(,asc/desc). 默认按升序排序.支持多个排序条件.
sub是否子设备
tagList[0].tagKey标签键
tagList[0].tagValue标签值
tagsOr多个过滤标签之间是否OR操作。true:使用OR操作,结果为并集。false:使用AND操作,结果为交集。默认为false。
返回数据
名称类型是否必须默认值备注其他信息
contentobject []非必须item 类型: object
├─ deviceNamestring非必须设备名称
├─ imageKeystring非必须产品图片Key。通过该图片Key可以从文件服务中获取图片内容
├─ indexinteger非必须自定义下标,通过维护此字段实现自定义排序format: int64
├─ lastOnlineTimestring非必须设备最近一次上线的时间
├─ networkTypestring非必须联网方式。取值:'WIFI':WiFi、'ETHERNET':以太网、'BLUETOOTH':蓝牙
├─ nicknamestring非必须设备备注名称
├─ ownerIdstring非必须设备用户Id
├─ productGroupstring非必须产品分组
├─ productKeystring非必须产品Key
├─ productNamestring非必须产品名称
├─ productNodeTypestring非必须节点类型,取值:0:普通设备,1:普通网关,2:边缘网关,4: 虚拟子设备
├─ productTypestring非必须产品类型
├─ tagsobject非必须返回的设备标签信息JSON对象。key为标签键,value为标签值,例:{"tagKey":"tagValue"}
├─ statusstring非必须设备状态。取值:ONLINE:设备在线。OFFLINE:设备离线。UNACTIVE:设备未激活。DISABLE:设备已禁用。
├─ subboolean非必须是否子设备
├─ userIdstring非必须开发者用户Id
emptyboolean非必须
firstboolean非必须
lastboolean非必须
numberinteger非必须format: int32
numberOfElementsinteger非必须format: int32
pageableobject非必须
├─ pageNumberinteger非必须查询的页码(0..N)format: int32
├─ pageSizeinteger非必须页大小format: int32
├─ sortstring []非必须排序条件,格式: 排序字段(,asc/desc). 默认按升序排序.支持多个排序条件.item 类型: string
├─非必须format: int32
sizeinteger非必须
sortobject非必须
├─ emptyboolean非必须
├─ sortedboolean非必须
├─ unsortedboolean非必须
totalElementsinteger非必须format: int64
totalPagesinteger非必须format: int32

批量查询设备状态

基本信息
Path: /device/batchGetStatus
Method: GET
接口描述: 调用该接口查看指定设备的运行状态。此接口需要权限:设备拥有者
请求参数
Query
参数名称是否必须示例备注
deviceName设备名称列表,单次查询最多50个设备,数据类型:list
productKey产品Key
返回数据
名称类型是否必须默认值备注其他信息
deviceStatusListobject []非必须设备状态信息列表item 类型: object
├─ deviceNamestring非必须设备名称
├─ lastOnlineTimestring非必须最近一次上线时间
├─ statusstring非必须设备状态

查询子设备(分页)

基本信息
Path: /device/querySubDevice
Method: GET
接口描述: 调用该接口查询网关子设备列表(分页)。此接口需要权限:设备开发者
请求参数
Query
参数名称是否必须示例备注
deviceName设备名称
pageNumber查询的页码(0..N)
pageSize页大小
productKey产品key
sort排序条件,格式: 排序字段(,asc/desc). 默认按升序排序.支持多个排序条件.
subDeviceName子设备名称
返回数据
名称类型是否必须默认值备注其他信息
contentobject []非必须item 类型: object
├─ deviceName string非必须设备名称
├─ lastOnlineTimestring非必须设备最近一次上线的时间
├─ productKeystring非必须产品Key
├─ productNamestring非必须产品名称
├─ statusstring非必须设备状态。取值:  ONLINE:设备在线。OFFLINE:设备离线。UNACTIVE:设备未激活。DISABLE:设备已禁用。
emptyboolean非必须
firstboolean非必须
lastboolean非必须
numberinteger非必须format: int32  
numberOfElementsinteger非必须format: int32 
pageableobject非必须
├─ pageNumberinteger非必须查询的页码(0..N)format: int32
├─ pageSizeinteger非必须页大小format: int32
├─ sortstring []非必须排序条件,格式: 排序字段(,asc/desc). 默认按升序排序.支持多个排序条件.item 类型: string
├─非必须非必须
sizeinteger非必须format: int32
sortobject非必须
├─ emptyboolean非必须
├─ sortedboolean非必须
├─ unsortedboolean非必须
totalElementsinteger非必须format: int64
totalPagesinteger非必须format: int32

查询批量申请批次列表

基本信息
Path: /device/queryBatchList
Method: GET
接口描述: 调用该接口查询批量申请设备列表。此接口需要权限:设备开发者
请求参数
Query
参数名称是否必须示例备注
pageNumber查询的页码(0..N)  
pageSize页大小
productKey产品Key,不传则统计所有设备
sort 排序条件,格式: 排序字段(,asc/desc). 默认按升序排序.支持多个排序条件.  
返回数据
名称类型是否必须默认值备注其他信息
contentobject []非必须item 类型: object
├─ applyIdinteger非必须申请批次IDformat: int64
├─ count integer非必须申请总数format: int32
├─ createTimestring非必须设备的创建时间
├─ productKeystring非必须产品Key
├─ productNamestring非必须产品名称
├─ statusstring非必须申请单的处理状态和结果,取值:CHECK:校验。CHECK_SUCCESS:校验成功。CHECK_FAILED:校验失败。CREATE:创建。CREATE_SUCCESS:创建成功。CREATE_FAILED:创建失败。
├─ userIdstring非必须申请人ID
emptyboolean非必须
firstboolean非必须
lastboolean非必须
numberinteger非必须format: int32  
numberOfElementsinteger非必须format: int32 
pageableobject非必须
├─ pageNumberinteger非必须查询的页码(0..N)format: int32
├─ pageSizeinteger非必须页大小format: int32
├─ sortstring []非必须排序条件,格式: 排序字段(,asc/desc). 默认按升序排序.支持多个排序条件.item 类型: string
├─非必须非必须
sizeinteger非必须format: int32
sortobject非必须
├─ emptyboolean非必须
├─ sortedboolean非必须
├─ unsortedboolean非必须
totalElementsinteger非必须format: int64
totalPagesinteger非必须format: int32

查询批量申请批次详情

基本信息
Path: /device/queryBatchInfo
Method: GET
接口描述: 调用该接口查询批量申请设备详情。此接口需要权限:设备开发者
请求参数
Query
参数名称是否必须示例备注
applyId申请批次ID
pageNumber查询的页码(0..N)  
pageSize页大小
productKey产品Key
sort 排序条件,格式: 排序字段(,asc/desc). 默认按升序排序.支持多个排序条件.  
返回数据
名称类型是否必须默认值备注其他信息
contentobject []非必须item 类型: object
├─ deviceNamestring非必须设备名称
├─ deviceSecretstring非必须设备密钥
├─ productKeystring非必须产品Key
├─ statusstring非必须设备状态。取值:ONLINE:设备在线。OFFLINE:设备离线。UNACTIVE:设备未激活。DISABLE:设备已禁用。
├─ utcActivestring非必须设备的激活时间(UTC)
emptyboolean非必须
firstboolean非必须
lastboolean非必须
numberinteger非必须format: int32  
numberOfElementsinteger非必须format: int32 
pageableobject非必须
├─ pageNumberinteger非必须查询的页码(0..N)format: int32
├─ pageSizeinteger非必须页大小format: int32
├─ sortstring []非必须排序条件,格式: 排序字段(,asc/desc). 默认按升序排序.支持多个排序条件.item 类型: string
├─非必须非必须
sizeinteger非必须format: int32
sortobject非必须
├─ emptyboolean非必须
├─ sortedboolean非必须
├─ unsortedboolean非必须
totalElementsinteger非必须format: int64
totalPagesinteger非必须format: int32

查询批量申请批次详情(全量)

基本信息
Path: /device/queryBatchInfoList
Method: GET
接口描述: 调用该接口查询批量申请设备详情(全量)。此接口需要权限:设备开发者
请求参数
Query
参数名称是否必须示例备注
applyId申请批次ID
返回数据
名称类型是否必须默认值备注其他信息
deviceInfosobject []非必须 设备信息列表item 类型: object
├─ deviceNamestring非必须设备名称
├─ deviceSecretstring非必须设备密钥
├─ productKeystring非必须产品Key

调用设备服务

基本信息
Path: /device/invokeThingService
Method: POST
接口描述: 同步调用服务,最大超时时间为5秒。若5秒内服务器未收到回复,则返回超时错误。此接口需要权限:设备拥有者
请求参数
Headers
参数名称参数值是否必须示例备注
Content-Type application/json
Body
名称类型是否必须默认值备注其他信息
argsstring必须 要启用服务的入参信息,数据格式为JSON String,如, Args={"param1":1}。若此参数为空时,需传入 Args={} 。
deviceNamestring必须设备名称
identifierstring必须服务的标识符。
productKeystring必须产品Key
返回数据
名称类型是否必须默认值备注其他信息
resultstring非必须 同步调用服务,返回的调用结果。异步调用服务,则此参数为空。

批量调用设备服务

基本信息
Path: /device/invokeThingsService
Method: POST
接口描述: 只支持异步调用该接口。此接口需要权限:设备拥有者
请求参数
Headers
参数名称参数值是否必须示例备注
Content-Type application/json
Body
名称类型是否必须默认值备注其他信息
argsstring必须 要启用服务的入参信息,数据格式为JSON String,如, Args={"param1":1}。若此参数为空时,需传入 Args={} 。
deviceNamestring []必须要设置属性值的设备名称列表,最多支持100个设备item 类型: string
├─非必须
identifierstring必须服务的标识符。
productKeystring必须产品Key
返回数据
ok

查询设备属性值

基本信息
Path: /device/queryDeviceProperty
Method: GET
接口描述: 调用该接口查询指定设备的属性。此接口需要权限:设备拥有者
请求参数
Query
参数名称是否必须示例备注
deviceName 设备名称
productKey产品Key
返回数据
名称类型是否必须默认值备注其他信息
propertyDataListobject []非必须 设备属性信息列表item 类型: object
├─ dataTypestring非必须属性格式类型,取值: int:整型。float:单精度浮点型。double:双精度浮点型。enum:枚举型。bool:布尔型。text:字符型。date:时间型(String类型的UTC时间戳,单位是毫秒)。array:数组型。struct:结构体类型。
├─ identifierstring非必须属性标识符
├─ namestring非必须属性名称
├─ timestring非必须属性修改的时间,单位是毫秒。
├─ unitstring非必须属性单位
├─ valuestring非必须属性值

查询设备期望属性值

基本信息
Path: /device/queryDesiredProperty
Method: GET
接口描述: 调用该接口为指定设备批量设置期望属性值。限制说明:只读属性不支持设置期望属性值。一次调用最多可设置10个期望属性值。设备创建后,期望属性值的版本(Version)为0。首次设置期望属性值时,如果指定Version参数,则需指定Version值为0。此接口需要权限:设备拥有者
请求参数
Query
参数名称是否必须示例备注
deviceName 设备名称
identifiers要查询期望值的属性的标识符 (identifier) 列表。单次调用,最多能传入10个identifie,如不传入则将返回该设备所有属性的期望值
productKey产品Key
返回数据
名称类型是否必须默认值备注其他信息
propertyDataListobject []非必须 设备属性信息列表item 类型: object
├─ dataTypestring非必须属性格式类型,取值: int:整型。float:单精度浮点型。double:双精度浮点型。enum:枚举型。bool:布尔型。text:字符型。date:时间型(String类型的UTC时间戳,单位是毫秒)。array:数组型。struct:结构体类型。
├─ identifierstring非必须属性标识符
├─ namestring非必须属性名称
├─ timestring非必须属性修改的时间,单位是毫秒。
├─ unitstring非必须属性单位
├─ valuestring非必须属性值
├─ versioninteger非必须当前期望属性值的版本号format: int64

设置设备属性值

基本信息
Path: /device/setDeviceProperty
Method: POST
接口描述: 因为云端下发属性设置命令和设备收到并执行该命令是异步的,所以调用该接口时,返回的成功结果只表示云端下发属性设置的请求成功,不能保证设备端收到并执行了该请求。需设备端SDK成功响应云端设置设备属性值的请求,设备属性值才能真正设置成功。 此接口需要权限:设备拥有者
请求参数
Headers
参数名称参数值 是否必须示例备注
Content-Type application/json
Body
参数名称类型是否必须示例备注其他信息
deviceName string必须设备名称
identifiersstring必须要设置的属性信息,组成为key:value,数据格式为 JSON String。其中key:要设置的属性的标识符(identifier),value:属性值,取值需和您定义的属性的数据类型和取值范围保持一致
productKeystring必须产品Key
返回数据
ok

批量设置设备属性值

基本信息
Path: /device/setDevicesProperty
Method: POST
接口描述: 调用该接口批量设置设备属性值。此接口需要权限:设备拥有者
请求参数
Headers
参数名称参数值 是否必须示例备注
Content-Type application/json
Body
参数名称类型是否必须默认值备注其他信息
deviceName strig [] 必须要设置属性值的设备名称列表,最多支持100个设备item 类型: string
├─非必须
itemsstring必须要设置的属性信息,组成为key:value,数据格式为 JSON String。其中key:   要设置的属性的标识符(identifier),value:属性值,         取值需和您定义的属性的数据类型和取值范围保持一致
productKeystring必须产品Key
返回数据
ok

批量查询设备属性历史数据

基本信息
Path: /device/queryPropertiesData
Method: GET
接口描述: 调用该接口批量查询指定设备的属性上报数据。注: 一次调用最多可查询10个属性的历史数据。 每个属性最多返回100条数据。 仅能查询最近30天内的属性数据 此接口需要权限:设备拥有者
请求参数
Query
参数名称是否必须示例备注
asc返回结果中,属性记录按时间排序的方式。取值:     0:倒序。倒序查询时,StartTime必须大于EndTime。    1:正序。正序查询时,StartTime必须小于EndTime。
deviceName设备名称
endTime属性记录的结束时间。取值为13位毫秒值时间戳。
identifier属性的标识符列表。最大只能同时传入10个属性,数据类型List
pageSize单个属性可返回的数据记录数量。最大值为100。任意一个属性返回的数据记录数量不超过该值。  
productKey产品Key
startTime属性记录的开始时间。取值为13位毫秒值时间戳。
返回数据
名称类型是否必须默认值备注其他信息
nextTimeinteger非必须下一页属性记录的起始时间。可以将NextTime的值作为下次查询的StartTime,继续查询本次查询不显示的数据。format: int64
nextValidboolean非必须是否有下一页属性记录。true表示有,false表示没有。返回NextValid为true时,可以将NextTime的值作为下次查询的StartTime,继续查询本次查询不显示的数据。
propertyDataInfosobject []  非必须属性信息列表item 类型: object
├─ identifierstring非必须属性标识符
├─ propertyInfoListobject []非必须属性数据列表item 类型: object
├─ timestring非必须属性上报时间format: int64
├─ valuestring非必须属性值

批量设置设备期望属性值

基本信息
Path: /device/setDesiredProperty
Method: POST
接口描述: 调用该接口为指定设备批量设置期望属性值。限制说明:只读属性不支持设置期望属性值。一次调用最多可设置10个期望属性值。设备创建后,期望属性值的版本(Version)为0。首次设置期望属性值时,如果指定Version参数,则需指定Version值为0。此接口需要权限:设备拥有者
请求参数
Headers
参数名称参数值 是否必须示例备注
Content-Type application/json
Body
参数名称类型是否必须默认值备注其他信息
deviceName strig 必须要设置属性值的设备名称列表,最多支持100个设备item 类型: string
├─非必须
itemsstring必须要设置的属性信息,组成为key:value,数据格式为 JSON String。其中key:   要设置的属性的标识符(identifier),value:属性值,         取值需和您定义的属性的数据类型和取值范围保持一致
productKeystring必须产品Key
返回数据
名称类型是否必须备注其他信息
version string 非必须本次设置期望属性值后,期望属性值的当前版本号。

查询设备属性历史数据

基本信息
Path: /device/queryPropertyData
Method: GET
接口描述: 调用该接口查询指定设备的属性记录。仅能查询最近30天内的属性数据。此接口需要权限:设备拥有者
请求参数
Query
参数名称是否必须示例备注
asc返回结果中,属性记录按时间排序的方式。取值:     0:倒序。倒序查询时,StartTime必须大于EndTime。    1:正序。正序查询时,StartTime必须小于EndTime。
deviceName设备名称
endTime属性记录的结束时间。取值为13位毫秒值时间戳。
identifier要查询的属性标识符。
pageSize单个属性可返回的数据记录数量。最大值为100。任意一个属性返回的数据记录数量不超过该值。  
productKey产品Key
startTime属性记录的开始时间。取值为13位毫秒值时间戳。
返回数据
名称类型是否必须默认值备注其他信息
nextTimeinteger非必须下一页属性记录的起始时间。可以将NextTime的值作为下次查询的StartTime,继续查询本次查询不显示的数据。format: int64
nextValidboolean非必须是否有下一页属性记录。true表示有,false表示没有。返回NextValid为true时,可以将NextTime的值作为下次查询的StartTime,继续查询本次查询不显示的数据。
queryPropertyDataInfoListobject []非必须属性集合item 类型: object
├─ timestring非必须属性上报时间format: int64
├─ valuestring非必须属性值

查询设备服务历史数据。

基本信息
Path: /device/queryServiceData
Method: GET
接口描述: 调用该接口查询指定设备的服务记录。仅能查询最近30天内的属性数据。此接口需要权限:设备拥有者
请求参数
Query
参数名称是否必须示例备注
asc返回结果中,属性记录按时间排序的方式。取值:     0:倒序。1:正序。
deviceName设备名称
endTime属性记录的结束时间。取值为13位毫秒值时间戳。
identifier要查询的属性标识符。
pageSize单个属性可返回的数据记录数量。最大值为100。任意一个属性返回的数据记录数量不超过该值。  
productKey产品Key
startTime属性记录的开始时间。取值为13位毫秒值时间戳。
返回数据
名称类型是否必须默认值备注其他信息
nextTimeinteger非必须下一页属性记录的起始时间。可以将NextTime的值作为下次查询的StartTime,继续查询本次查询不显示的数据。format: int64
nextValidboolean非必须是否有下一页属性记录。true表示有,false表示没有。返回NextValid为true时,可以将NextTime的值作为下次查询的StartTime,继续查询本次查询不显示的数据。
serviceInfoListobject []非必须服务记录集合。item 类型: object
├─ identifierstring非必须服务标识符。
├─ inputDatastring非必须服务的输入参数,map格式的字符串,结构为 key:value。
├─ name string非必须服务名称。
├─ outputData string非必须服务的输出参数,map格式的字符串,结构为 key:value。  
├─ time string非必须服务执行的时间。

查询设备事件历史数据

基本信息
Path: /device/queryEventData
Method: GET
接口描述: 调用该接口查询指定设备的事件记录。此接口需要权限:设备拥有者
请求参数
Query
参数名称是否必须示例备注
asc返回结果中,属性记录按时间排序的方式。取值:     0:倒序。1:正序。
deviceName设备名称
endTime属性记录的结束时间。取值为13位毫秒值时间戳。
eventType要查询的事件类型。取值:  info:信息。  alert:告警。error:故障。
identifier要查询的属性标识符。
pageSize单个属性可返回的数据记录数量。最大值为100。任意一个属性返回的数据记录数量不超过该值。  
productKey产品Key
startTime属性记录的开始时间。取值为13位毫秒值时间戳。
返回数据
名称类型是否必须默认值备注其他信息
nextTimeinteger非必须下一页属性记录的起始时间。可以将NextTime的值作为下次查询的StartTime,继续查询本次查询不显示的数据。format: int64
nextValidboolean非必须是否有下一页属性记录。true表示有,false表示没有。返回NextValid为true时,可以将NextTime的值作为下次查询的StartTime,继续查询本次查询不显示的数据。
eventInfoListobject []非必须 事件集合。 item 类型: object
├─ identifierstring非必须事件标识符。
├─ inputDatastring非必须服务的输入参数,map格式的字符串,结构为 key:value。
├─ name string非必须服务名称。
├─ outputData string非必须服务的输出参数,map格式的字符串,结构为 key:value。  
├─ time string非必须服务执行的时间。

查询设备标签列表

基本信息
Path: /device/listTags
Method: GET
接口描述: 调用该接口查询指定设备的标签列表。此接口需要权限:设备开发者
请求参数
Query
参数名称是否必须示例备注
deviceName设备名称
productKey产品Key
返回数据
名称类型是否必须默认值备注其他信息
tagMapobject 非必须 返回的设备标签信息JSON对象。key为标签键,value为标签值,例:{"tagKey":"tagValue"}

全量更新设备标签

基本信息
Path: /device/fullUpdateTags
Method: POST
接口描述: 调用该接口为指定设备全量更新标签,设备最大标签数为10个。此接口需要权限:设备开发者
请求参数
Headers
参数名称参数值 是否必须示例备注
Content-Type application/json
Body
名称类型是否必须默认值备注其他信息
deviceNamestring必须设备名称
productKeystring必须产品Key
tagMapobject 必须 返回的设备标签信息JSON对象。key为标签键,value为标签值,例:{"tagKey":"tagValue"}
返回数据
ok

增量更新设备标签

基本信息
Path: /device/updateTags
Method: POST
接口描述: 调用该接口为指定设备增量更新标签,产品最大标签数为10个。此接口需要以下功能权限:设备开发者
请求参数
Headers
参数名称参数值 是否必须示例备注
Content-Type application/json
Body
名称类型是否必须默认值备注其他信息
deviceNamestring必须设备名称
productKeystring必须产品Key
tagMapobject 必须 返回的设备标签信息JSON对象。key为标签键,value为标签值,例:{"tagKey":"tagValue"}
返回数据
ok

设备管理(用户)

批量更新设备

基本信息
Path: /deviceOwner/batchUpdate
Method: POST
接口描述: 调用该接口批量修改设备。此接口需要权限:设备拥有者
请求参数
Headers
参数名称参数值 是否必须示例备注
Content-Type application/json
Body
名称类型 是否必须默认值备注其他信息
updateDeviceInfos object [] 必须 item 类型: object
├─ deviceName string必须 设备名称
├─ index integer必须 自定义下标 format: int64
├─ productKey string必须 产品Key
返回数据
ok

绑定设备

基本信息
Path: /deviceOwner/bind
Method: POST
接口描述: 调用该接口绑定设备。此接口需要以下功能权限:设备用户
请求参数
Headers
参数名称参数值 是否必须示例备注
Content-Type application/json
Body
名称类型 是否必须默认值备注其他信息
deviceName string必须 设备名称
deviceSecret string必须 设备密钥
productKey string必须 产品Key
返回数据
ok

解绑设备

基本信息
Path: /deviceOwner/unbind
Method: POST
接口描述: 调用该接口解绑设备。此接口需要以下功能权限:设备用户
请求参数
Headers
参数名称参数值 是否必须示例备注
Content-Type application/json
Body
名称类型 是否必须默认值备注其他信息
deviceName string必须 设备名称
productKey string必须 产品Key
返回数据
ok

批量解绑设备

基本信息
Path: /deviceOwner/batchUnbind
Method: POST
接口描述: 调用该接口批量解绑设备。此接口需要以下功能权限:设备用户
请求参数
Headers
参数名称参数值 是否必须示例备注
Content-Type application/json
Body
名称类型 是否必须默认值备注其他信息
deviceKeys object [] 必须 解绑设备标识列表,单次最大解绑设备数:50 item 类型: object
├─ deviceName string必须 设备名称
├─ productKey string必须 产品Key
返回数据
ok

批量更新设备备注名称

基本信息
Path: /deviceOwner/batchUpdateNickname
Method: POST
接口描述: 调用该接口批量修改设备备注名称。此接口需要权限:设备拥有者
请求参数
Headers
参数名称参数值 是否必须示例备注
Content-Type application/json
Body
名称类型 是否必须默认值备注其他信息
updateDeviceNicknameInfos object [] 必须 item 类型: object
├─ deviceName string必须 设备名称
├─ nickname string必须新的备注名称,长度为4-64个字符,可包含中文汉字、英文字母、数字和下划线(_)。一个中文汉字算2字符。最大长度: 64     最小长度: 4
├─ productKey string必须 产品Key
返回数据
ok

查询设备详情

基本信息
Path: /deviceOwner/get
Method: GET
接口描述: 调用该接口查询指定设备的详细信息。此接口需要以下功能权限:设备用户
请求参数
Query
名称是否必须示例备注
deviceName 必须 设备名称
productKey 必须 产品Key
返回数据
名称类型 是否必须默认值备注其他信息
deviceNamestring非必须设备名称
deviceSecretstring非必须设备密钥
gmtActivestring非必须设备的激活时间
gmtCreatestring非必须创建时间
gmtOnlinestring非必须设备最近一次上线的时间
imageKeystring非必须产品图片Key。通过该图片Key可以从文件服务中获取图片内容
indexinteger非必须自定义下标,通过维护此字段实现自定义排序format: int64
instanceIdstring非必须实例ID
ipAddressstring非必须设备IP地址
nicknamestring非必须备注名称
parentDeviceNamestring非必须父设备名称
parentProductKeystring非必须父产品Key
productKeystring非必须产品key
productNamestring非必须产品名称
productNodeTypestring非必须节点类型,取值:0:普通设备,1:普通网关,2:边缘网关,4:虚拟子设备
statusstring非必须设备状态。取值:   ONLINE:设备在线。OFFLINE:设备离线。UNACTIVE:设备未激活。DISABLE:设备已禁用。
subDeviceNamestring非必须子设备名称
subProductKeystring非必须子产品Key
utcActivestring非必须设备的激活时间(UTC)
utcCreatestring非必须设备的创建时间(UTC) 
utcOnlinestring非必须设备最近一次上线的时间(UTC)

批量查询设备

基本信息
Path: /deviceOwner/list
Method: GET
接口描述: 调用该接口批量批量查询设备。此接口需要以下功能权限:设备用户
请求参数
Query
名称是否必须示例备注
deviceName 设备名称(模糊查询)
networkType联网方式。取值:   ‘WIFI’:WiFi    ‘ETHERNET’:以太网     ‘BLUETOOTH’:蓝牙
nickname备注名称
ownerId设备用户Id
pageNum查询的页码(0..N)
pageSize页大小
productGroup 产品分组
productKey产品Key
productName产品名称
productNodeType 节点类型,取值:0:普通设备,2:边缘网关,4:虚拟子产品
productNodeTypes 查询节点类型集合,取值:0:普通设备,2:边缘网关,4:虚拟子产品
productType产品类型
sort排序条件,格式: 排序字段(,asc/desc). 默认按升序排序.支持多个排序条件.
sub是否子设备
tagList[0].tagKey 标签键
tagList[0].tagValue 标签值
tagsOr多个过滤标签之间是否OR操作。true:使用OR操作,结果为并集。 false:使用AND操作,结果为交集 默认为false。  
返回数据
名称类型是否必须 默认值 备注 其他信息
contentobject [] 非必须 item 类型: object
├─ deviceName string非必须 设备名称
├─ imageKey string非必须 产品图片Key。通过该图片Key可以从文件服务中获取图片内容
├─ index integer非必须 自定义下标,通过维护此字段实现自定义排序 format: int64
├─ lastOnlineTime string非必须 设备最近一次上线的时间
├─ networkType string非必须 联网方式。取值:   'WIFI':WiFi    'ETHERNET':以太网   'BLUETOOTH':蓝牙
├─ nickname string非必须 设备备注名称
├─ ownerId string非必须 设备用户Id
├─ productGroup string非必须 产品分组
├─ productKey string非必须 产品Key
├─ productName string非必须 产品名称
├─ productNodeType string非必须 节点类型,取值:0:普通设备,1:普通网关,2:边缘网关,4: 虚拟子设备
├─ productType string非必须 产品类型
├─ status string非必须 设备状态。取值:    ONLINE:设备在线。  OFFLINE:设备离线。  UNACTIVE:设备未激活。 DISABLE:设备已禁用。
├─ sub boolean非必须 是否子设备
├─ userId string非必须 开发者用户Id
empty boolean非必须
first boolean非必须
lastboolean非必须
number integer非必须 format: int32
numberOfElementsinteger非必须 format: int32
pageableobject非必须
├─ pageNum integer非必须 查询的页码(0..N) format: int32
├─ pageSize integer非必须 页大小 format: int32
├─ sort string [] 非必须 排序条件,格式: 排序字段(,asc/desc). 默认按升序排序.支持多个排序条件.item 类型:string
├─ 非必须
sizeinteger非必须 format: int32
sortobject非必须
├─ empty boolean非必须
├─ sorted boolean非必须
├─ unsorted boolean非必须
totalElementsinteger非必须 format: int64
totalPages integer非必须 format: int32

批量查询设备详情

基本信息
Path: /deviceOwner/batchGet
Method: GET
接口描述: 调用该接口批量查询指定设备的详细信息。此接口需要以下功能权限:设备用户
请求参数
Query
名称是否必须示例备注
deviceName 设备名称列表,单次查询最多100个设备,数据类型:list
productKey 产品Key
返回数据
名称类型是否必须 默认值 备注 其他信息
object [] 非必须 item 类型: object
├─ deviceName string非必须 设备名称
├─ deviceSecret string非必须设备密钥
├─ gmtActive string非必须设备的激活时间
├─ gmtCreate string非必须创建时间
├─ gmtOnline string非必须设备最近一次上线的时间
├─ instanceId string非必须实例ID
├─ ipAddress string非必须设备IP地址
├─ nickname string非必须备注名称
├─ parentDeviceName string非必须父设备名称
├─ parentProductKey string非必须父产品Key
├─ productKey string非必须产品key
├─ productName string非必须产品名称
├─ productNodeType string非必须 节点类型,取值:0:普通设备,1:普通网关,2:边缘网关,4: 虚拟子设备
├─ status string非必须 设备状态。取值:    ONLINE:设备在线。  OFFLINE:设备离线。  UNACTIVE:设备未激活。 DISABLE:设备已禁用。
├─ subDeviceName string非必须 子设备名称
├─ subProductKey string非必须 子产品Key
├─ utcActive string非必须 设备的激活时间(UTC)
├─ utcCreate string非必须 设备的创建时间(UTC)
├─ utcOnline string非必须 设备最近一次上线的时间(UTC)

批量查询设备状态

基本信息
Path: /deviceOwner/batchGetStatus
Method: GET
接口描述: 调用该接口查看指定设备的运行状态。此接口需要权限:设备用户
请求参数
Query
名称是否必须示例备注
deviceName 设备名称列表,单次查询最多50个设备,数据类型:list
productKey 产品Key
返回数据
名称类型是否必须 默认值 备注 其他信息
deviceStatusList object [] 非必须 设备状态信息列表 item 类型: object
├─ deviceName string非必须 设备名称
├─ lastOnlineTime string非必须 最近一次上线时间
├─ status string非必须 设备状态

查询子设备(分页)

基本信息
Path: /deviceOwner/querySubDevice
Method: GET
接口描述: 调用该接口查询网关子设备列表(分页)。此接口需要权限:设备用户
请求参数
Query
名称是否必须示例备注
deviceName 设备名称
pageNum查询的页码(0..N)
pageSize页大小
productKey 产品Key
sort排序条件,格式: 排序字段(,asc/desc). 默认按升序排序.支持多个排序条件.
subDeviceName 子设备名称
返回数据
名称类型是否必须 默认值 备注 其他信息
contentobject [] 非必须 item 类型: object
├─ deviceName string非必须 设备名称
├─ lastOnlineTime string非必须设备最近一次上线的时间
├─ productKey string非必须产品key
├─ productName string非必须产品名称
├─ status string非必须 设备状态。取值:    ONLINE:设备在线。  OFFLINE:设备离线。  UNACTIVE:设备未激活。 DISABLE:设备已禁用。
emptyboolean非必须
firstboolean非必须
lastboolean非必须
numberinteger非必须 format: int32
numberOfElements integer非必须 format: int32
pageableobject非必须
├─ pageNum integer非必须 查询的页码(0..N) format: int32
├─ pageSize integer非必须 页大小 format: int32
├─ sort string [] 非必须 排序条件,格式: 排序字段(,asc/desc). 默认按升序排序.支持多个排序条件. item 类型: string
├─ 非必须
sizeinteger非必须 format: int32
sortobject非必须
├─ empty boolean非必须
├─ sorted boolean非必须
├─ unsorted boolean非必须
totalElementsinteger非必须 format: int64
totalPages integer非必须 format: int32

全量更新设备标签

基本信息
Path: /deviceOwner/fullUpdateTags
Method: POST
接口描述: 调用该接口为指定设备全量更新标签,设备最大标签数为10个。此接口需要权限:设备用户
请求参数
Headers
名称参数值 是否必须示例备注
Content-Type application/json
Body
名称类型是否必须默认值备注其他信息
deviceName string 必须设备名称
productKey string 必须产品Key
tagMapobject 必须覆盖更新的标签JSON对象。key为标签键,value为标签值,例:{"tagKey":"tagValue"},
返回数据
ok

增量更新设备标签

基本信息
Path: /deviceOwner/updateTags
Method: POST
接口描述: 调用该接口为指定设备增量更新标签,产品最大标签数为10个。此接口需要以下功能权限:设备用户
请求参数
Headers
名称参数值 是否必须示例备注
Content-Type application/json
Body
名称类型是否必须默认值备注其他信息
deviceName string 必须设备名称
productKey string 必须产品Key
tagMapobject 必须覆盖更新的标签JSON对象。key为标签键,value为标签值,例:{"tagKey":"tagValue"},
返回数据
ok

查询设备标签列表

基本信息
Path: /deviceOwner/listTags
Method: GET
接口描述: 调用该接口查询指定设备的标签列表。此接口需要权限:设备用户
请求参数
Query
名称是否必须示例备注
deviceName 设备名称
productKey 产品Key
返回数据
名称类型是否必须默认值备注其他信息
tagMapobject 必须覆盖更新的标签JSON对象。key为标签键,value为标签值,例:{"tagKey":"tagValue"},

查询设备属性值

基本信息
Path: /deviceOwner/queryDeviceProperty
Method: GET
接口描述: 调用该接口查询指定设备的属性。此接口需要权限:设备用户
请求参数
Query
名称是否必须示例备注
deviceName 设备名称
productKey 产品Key
返回数据
名称类型是否必须默认值备注其他信息
propertyDataListobject [] 非必须 设备属性信息列表 item 类型: object
├─ dataType string 非必须 属性格式类型,取值:   int:整型。  float:单精度浮点型。  double:双精度浮点型。  enum:枚举型。 bool:布尔型。  text:字符型。  date:时间型(String类型的UTC时间戳,单位是毫秒)。  array:数组型。 struct:结构体类型。 
├─ identifier string 非必须 属性标识符
├─ name string 非必须 属性名称
├─ time string 非必须 属性修改的时间,单位是毫秒。
├─ unit string 非必须 属性单位
├─ value string 非必须 属性值

设置设备属性值

基本信息
Path: /deviceOwner/setDeviceProperty
Method: POST
接口描述: 因为云端下发属性设置命令和设备收到并执行该命令是异步的,所以调用该接口时,返回的成功结果只表示云端下发属性设置的请求成功,不能保证设备端收到并执行了该请求。需设备端SDK成功响应云端设置设备属性值的请求,设备属性值才能真正设置成功。 此接口需要权限:设备用户
请求参数
Headers
名称参数值 是否必须示例备注
Content-Type application/json
Body
名称类型是否必须默认值备注其他信息
deviceNamestring必须设备名称
items string必须要设置的属性信息,组成为key:value,数据格式为 JSON String。其中key:要设置的属性的标识符(identifier),value:属性值,取值需和您定义的属性的数据类型和取值范围保持一致
productKey string必须产品Key
返回数据
ok

批量设置设备属性值

基本信息
Path: /deviceOwner/setDevicesProperty
Method: POST
接口描述: 调用该接口批量设置设备属性值。此接口需要权限:设备用户
请求参数
Headers
名称参数值 是否必须示例备注
Content-Type application/json
Body
名称类型是否必须默认值备注其他信息
deviceNamestring [] 必须要设置属性值的设备名称列表,最多支持100个设备 item 类型: string
├─ 非必须
items string必须要设置的属性信息,组成为key:value,数据格式为 JSON String。其中key:要设置的属性的标识符(identifier),value:属性值,取值需和您定义的属性的数据类型和取值范围保持一致
productKey string必须产品Key
返回数据
ok

查询指定设备期望属性值

基本信息
Path: /deviceOwner/queryDesiredProperty
Method: GET
接口描述:
调用该接口为指定设备批量设置期望属性值。限制说明:只读属性不支持设置期望属性值。一次调用最多可设置10个期望属性值。设备创建后,期望属性值的版本(Version)为0。首次设置期望属性值时,如果指定Version参数,则需指定Version值为0。此接口需要权限:设备用户
请求参数
Query
参数名称是否必须  示例备注
deviceName设备名称
identifiers要查询期望值的属性的标识符 (identifier) 列表。单次调用,最多能传入10个identifie,如不传入则将返回该设备所有属性的期望值
productKey产品Key
返回数据
名称类型  是否必须默认值备注其他信息
desiredPropertyDataListobject []非必须期望值属性信息列表item 类型: object
├─ dataTypestring  非必须属性格式类型,取值: int:整型。 float:单精度浮点型。 double:双精度浮点型。 enum:枚举型。 bool:布尔型。 text:字符型。 date:时间型(String类型的UTC时间戳,单位是毫秒)。 array:数组型。 struct:结构体类型。  
├─ identifierstring  非必须属性标识符
├─ namestring  非必须属性名称  
├─ timestring  非必须属性修改的时间,单位是毫秒。  
├─ unitstring  非必须属性单位
├─ valuestring  非必须属性值
├─ versioninteger非必须当前期望属性值的版本号format: int64

批量设置设备期望属性值

基本信息
Path: /deviceOwner/setDesiredProperty
Method: POST
接口描述:
调用该接口为指定设备批量设置期望属性值。限制说明:只读属性不支持设置期望属性值。一次调用最多可设置10个期望属性值。设备创建后,期望属性值的版本(Version)为0。首次设置期望属性值时,如果指定Version参数,则需指定Version值为0。此接口需要权限:设备用户
请求参数
Headers
参数名称参数值是否必须示例备注
Content-Typeapplication/json
Body
名称类型是否必须默认值备注其他信息
deviceNamestring必须设备名称
itemsstring必须要设置的期望属性值,组成为key:value,key取值为属性的标识符(identifier),value取值为要设置的期望属性值,数据格式为 JSON String,如{"Power":1}。
productKeystring必须产品Key
versionsstring必须当前期望属性值版本,组成为key:value,key取值为属性的标识符(identifier),value取值为当前期望属性值的版本号。数据格式为 JSON String,如{"Power":2}。若您不确定当前期望值的版本号,可以不传入版本号,但仍需传入{}
返回数据
名称类型是否必须默认值备注其他信息
versionstring非必须本次设置期望属性值后,期望属性值的当前版本号。

调用设备服务

基本信息
Path: /deviceOwner/invokeThingService
Method: POST
接口描述:
同步调用服务,最大超时时间为5秒。若5秒内服务器未收到回复,则返回超时错误。此接口需要权限:设备用户
请求参数
Headers
参数名称参数值是否必须示例备注
Content-Typeapplication/json
Body
名称类型是否必须默认值备注其他信息
argsstring必须要启用服务的入参信息,数据格式为JSON String,如, Args={"param1":1}。若此参数为空时,需传入 Args={} 。
deviceNamestring必须设备名称  
identifierstring必须服务的标识符。
productKeystring必须产品Key
返回数据
名称类型是否必须默认值备注其他信息
resultstring非必须同步调用服务,返回的调用结果。 异步调用服务,则此参数为空。

批量调用设备服务

基本信息
Path: /deviceOwner/invokeThingsService
Method: POST
接口描述:
只支持异步调用该接口。此接口需要权限:设备用户
请求参数
Headers
参数名称参数值是否必须示例备注
Content-Typeapplication/json
Body
名称类型是否必须默认值备注  其他信息
argsstring必须要启用服务的入参信息,数据格式为JSON String,如, Args={"param1":1}。若此参数为空时,需传入 Args={} 。
deviceNamesstring []必须要设置属性值的设备名称列表,最多支持100个设备item 类型: string
├─非必须
identifierstring必须服务标识符。  
productKeystring必须产品key  
返回数据
ok

全量更新设备标签

基本信息
Path: /deviceOwner/fullUpdateTags
Method: POST
接口描述:
调用该接口为指定设备全量更新标签,设备最大标签数为10个。此接口需要权限:设备用户
请求参数
Headers
参数名称参数值是否必须示例备注
Content-Typeapplication/json
Body
名称类型是否必须默认值备注其他信息
deviceNamestring必须设备名称
productKeystring必须产品Key
tagMapobject必须 覆盖更新的标签JSON对象。key为标签键,value为标签值,例:{"tagKey":"tagValue"},  
Path: /deviceOwner/updateTags
Method: POST
接口描述:
调用该接口为指定设备增量更新标签,产品最大标签数为10个。此接口需要以下功能权限:设备用户
请求参数
Headers
参数名称参数值是否必须示例备注
Content-Typeapplication/json
Body
名称类型是否必须默认值备注其他信息
deviceNamestring必须设备名称
productKeystring必须产品Key  
tagMapobject必须增量更新的标签JSON对象。key为标签键,value为标签值,需要删除标签,请将键值设为空值,例:{"tagKey":"tagValue"},
返回数据
ok

查询设备标签列表

基本信息
Path: /deviceOwner/listTags
Method: GET
接口描述:
调用该接口查询指定设备的标签列表。此接口需要权限:设备用户
请求参数
Query
参数名称是否必须示例备注
deviceName设备名称
productKey产品Key
返回数据
名称类型是否必须默认值备注其他信息
tagMapobject非必须返回的设备标签信息JSON对象。key为标签键,value为标签值,例:{"tagKey":"tagValue"}
> 返回数据 ```javascript OK ``` ## 增量更新设备标签 > 基本信息

产品管理(开发者)

查询产品(单个)

基本信息
Path: /product/get
Method: GET
接口描述:
根据产品key查询产品详细信息。此接口需要以下功能权限:产品拥有者
请求参数
Query
参数名称是否必须示例  备注
productKey产品Key
返回数据
名称类型是否必须默认值备注其他信息
createTimeinteger非必须产品创建时间format: int64
dataFormatinteger非必须数据格式。取值: 0:透传/自定义格式 1:ICA 标准数据格式 (Alink JSON)  format: int32
descriptionstring非必须产品描述
deviceCountinteger非必须该产品下的设备数量format: int64
imageKeystring非必须产品图片Key。通过该图片Key可以从文件服务中获取图片内容
networkTypestring非必须联网方式。取值: 'WIFI':WiFi 'ETHERNET':以太网 'BLUETOOTH':蓝牙  
parentProductKeystring非必须父产品Key
productGroupstring非必须产品分组
productKeystring非必须产品KEY
productNamestring非必须产品名称
productNodeTypestring非必须节点类型,取值:0:普通设备,1:普通网关,2:边缘网关,4: 虚拟子设备
productStatusstring非必须产品状态,枚举值:开发中:DEVELOPMENT_STATUS,已发布:RELEASE_STATUS
productTypestring非必须产品类型
subboolean非必须是否子设备  
subProductKeystring非必须子产品KEY
tagListobject []非必须标签列表item 类型: object
├─ tagKeystring非必须标签键
├─ tagValuestring非必须标签值
userIdstring非必须产品所属开发者账号id

分页查询产品信息

基本信息
Path: /product/list
Method: GET
接口描述:
返回产品信息分页数据。此接口需要以下功能权限:产品拥有者
请求参数
Query
参数名称是否必须示例备注
dataFormat数据格式。取值: 0:透传/自定义格式 1:ICA 标准数据格式 (Alink JSON)  
description产品描述
networkType  联网方式。取值: ‘WIFI’:WiFi ‘ETHERNET’:以太网 ‘BLUETOOTH’:蓝牙  
pageNumber查询的页码(0..N)
pageSize页大小
productGroup产品分组
productName产品名称(模糊查询)
productNodeType节点类型,取值:0:普通设备,1:普通网关,2:边缘网关
productType产品类型
sort排序条件,格式: 排序字段(,asc/desc). 默认按升序排序.支持多个排序条件.
sub是否子设备
tagList[0].tagKey标签键
tagList[0].tagValue标签值
tagsOr  多个过滤标签之间是否OR操作。 true:使用OR操作,结果为并集 false:使用AND操作,结果为交集 默认为false  
返回数据
名称类型是否必须默认值  备注其他信息
contentobject []非必须item 类型: object
├─ createTimeinteger非必须产品创建时间format: int64
├─ dataFormatinteger非必须数据格式。取值: 0:透传/自定义格式 1:ICA 标准数据格式 (Alink JSON)  format: int32
├─ descriptionstring非必须产品描述
├─ imageKeystring非必须产品图片Key。通过该图片Key可以从文件服务中获取图片内容
├─ networkTypestring非必须联网方式。取值: 'WIFI':WiFi 'ETHERNET':以太网 'BLUETOOTH':蓝牙  
├─ productGroupstring非必须产品分组
├─ productKeystring非必须产品KEY  
├─ productNamestring非必须产品名称
├─ productNodeTypestring非必须节点类型,取值:0:普通设备,1:普通网关,2:边缘网关,4: 虚拟子设备
├─ productTypestring非必须产品类型
├─ subboolean非必须是否子设备
├─ tagListobject []非必须标签列表item 类型: object
    ├─ tagKeystring非必须标签键
    ├─ tagValuestring非必须标签值
emptyboolean非必须
firstboolean非必须
lastboolean非必须
numberinteger非必须format: int32
numberOfElementsinteger非必须format: int32
pageableobject非必须
├─ pageNumberinteger非必须查询的页码(0..N)format: int32
├─ pageSizeinteger非必须页大小format: int32
├─ sortstring []非必须排序条件,格式: 排序字段(,asc/desc). 默认按升序排序.支持多个排序条件.item 类型: string
    ├─非必须
sizeinteger非必须format: int32
sortobject非必须
├─ emptyboolean非必须
├─ sortedboolean非必须
├─ unsortedboolean非必须
totalElementsinteger非必须format: int64
totalPagesinteger非必须format: int32

查询产品标签

基本信息
Path: /product/listTags
Method: GET
接口描述:
根据产品key返回产品标签列表
请求参数
Query
参数名称是否必须示例备注
productKey产品Key
返回数据
名称类型是否必须默认值备注其他信息
object []非必须  item 类型: object
├─ tagKeystring非必须标签键
├─ tagValuestring非必须 标签值  

全量更新多个产品标签

基本信息
Path:/product/fullUpdateTags
Method: POST
接口描述:
根据产品key全量更新产品标签信息,产品最大标签数为10个。此接口需要以下功能权限:产品拥有者
请求参数
Headers
参数名称参数值  是否必须示例备注
Content-Typeapplication/json  
body
名称类型  是否必须默认值备注其他信息
productKeystring  必须产品KEY
tagListobject [] 必须增量更新产品标签列表,最多10个item 类型: object
├─ tagKeystring  非必须  标签键
├─ tagValuestring  非必须  标签值
返回数据
ok

增量更新多个产品标签

基本信息
Path:/product/incrementalUpdateTags
Method: POST
接口描述:
根据产品key增量更新产品标签信息,标签值为null即删除标签,产品最大标签数为10个。此接口需要以下功能权限:产品拥有者
请求参数
Headers
参数名称参数值是否必须  示例备注
Content-Typeapplication/json
Body
名称类型是否必须默认值备注其他信息
productKeystring必须  产品KEY
tagListobject []必须  增量更新产品标签列表,最多10个item 类型: object
├─ tagKeystring非必须标签键
├─ tagKeystring非必须标签值  
返回数据
ok

更新产品

基本信息
Path:/product/update
Method: POST
接口描述:
根据产品key更新产品信息
请求参数
Headers
参数名称参数值是否必须示例备注
Content-Typeapplication/json 是  
Body
名称类型是否必须默认值  备注其他信息
productKeystring必须产品KEY
productNamestring非必须产品名称最大长度: 30;最小长度: 4
productTypestring非必须产品类型
productGroupstring非必须产品分组
descriptionstring非必须产品描述.长度必须在0-100之间  最大长度: 100;最小长度: 0
imageKeystring非必须 产品图片Key  
返回数据
ok

产品管理(用户)

历史变更记录

日期修改描述作者
2019-09-26创建文档姚海明

查询产品信息(分页)

基本信息
Path:/productUser/list
Method: GET
接口描述:
请求参数
Query
参数名称是否必须示例备注
dataFormat数据格式。取值: 0:透传/自定义格式 1:ICA 标准数据格式 (Alink JSON)  
description产品描述
networkType联网方式。取值: ‘WIFI’:WiFi ‘ETHERNET’:以太网 ‘BLUETOOTH’:蓝牙  
pageNum查询的页码(0..N)
pageSize页大小
productGroup产品分组  
productName产品名称(模糊查询)
productNodeType节点类型,取值:0:普通设备,1:普通网关,2:边缘网关
productType产品类型
sort排序条件,格式: 排序字段(,asc/desc). 默认按升序排序.支持多个排序条件.  
sub是否子设备
tagList[0].tagKey标签键
tagList[0].tagValue 标签值  
tagsOr多个过滤标签之间是否OR操作。 true:使用OR操作,结果为并集 false:使用AND操作,结果为交集 默认为false  
返回数据
名称类型是否必须 默认值 备注 其他信息
contentobject [] 非必须 item 类型: object
├─ createTime integer 非必须产品创建时间 format: int64
├─ dataFormat integer 非必须数据格式。取值: 0:透传/自定义格式 。1:ICA 标准数据格式 (Alink JSON) format: int32
├─ description string 非必须产品描述
├─ imageKey string 非必须产品图片Key。通过该图片Key可以从文件服务中获取图片内容
├─ networkType string非必须 联网方式。取值:   'WIFI':WiFi    'ETHERNET':以太网   'BLUETOOTH':蓝牙
├─ productGroup string非必须 产品分组
├─ productKey string非必须 产品Key
├─ productName string非必须 产品名称
├─ productNodeType string非必须 节点类型,取值:0:普通设备,1:普通网关,2:边缘网关,4: 虚拟子设备
├─ productType string非必须 产品类型
├─ sub boolean非必须 是否子设备
├─ tagList object []非必须标签列表
├─ tagKey string 非必须标签键
├─ tagValue string 非必须标签值 item 类型: object
empty boolean非必须
first boolean非必须
lastboolean非必须
number integer非必须 format: int32
numberOfElementsinteger非必须 format: int32
pageableobject非必须
├─ pageNum integer非必须 查询的页码(0..N) format: int32
├─ pageSize integer非必须 页大小 format: int32
├─ sort string [] 非必须 排序条件,格式: 排序字段(,asc/desc). 默认按升序排序.支持多个排序条件.item 类型:string
├─ 非必须
sizeinteger非必须 format: int32
sortobject非必须
├─ empty boolean非必须
├─ sorted boolean非必须
├─ unsorted boolean非必须
totalElementsinteger非必须 format: int64
totalPages integer非必须 format: int32

设备端 SDK 功能说明

3.2版本的HAL列表

基础HAL

HAL_Free

  • 原型
void HAL_Free(_IN_ void *ptr);
  • 接口说明  释放参数ptr指向的一块堆内存, 当传入的参数为NULL时不执行任何操作  
  • 参数说明
参数数据类型 方向 说明
ptrvoid * 输入 指向将要释放的堆内存的指针
  • 返回值说明 void

HAL_GetChipID

(在3.0.1及以后版本中不需要实现)
  • 原型
char *HAL_GetChipID(_OU_ char cid_str[HAL_CID_LEN]);
  • 接口说明  获取唯一的芯片ID字符串, 字符串长度不能超过HAL_CID_LEN定义的数值。 注:该HAL只需要芯片商进行适配,如果用户不是芯片商,该HAL返回空字符串即可 
  • 参数说明
参数数据类型 方向 说明
cid_str char【】输出存放芯片ID的字符串缓冲区
  • 返回值说明指向字符串缓冲区的指针

HAL_GetDeviceID

(在2.3.1及以后版本中不需要实现)
  • 原型
int HAL_GetDeviceID(_OU_ char device_id[DEVICE_ID_LEN]);
  • 接口说明 获取设备的DeviceID, 用于标识设备单品的ID
参数数据类型方向说明
device_idchar[]输出  存放DeviceID的字符串缓冲区  
  • 返回值说明  实际获取到的DeviceID字符串长度  

HAL_GetDeviceName

  • 原型
int HAL_GetDeviceName(_OU_ char device_name[DEVICE_NAME_LEN]);
  • 接口说明 获取设备的DeviceName, 用于唯一标识单个设备的名字, 三元组之一, 在云端控制台注册得到并烧写到设备中  
  • 参数说明
参数数据类型 方向 说明
device_name char【】输出  存放DeviceName的字符串缓冲区  
  • 返回值说明  实际获取到的DeviceName字符串长度  

HAL_GetDeviceSecret

  • 原型
int HAL_GetDeviceSecret(_OU_ char device_secret[DEVICE_SECRET_LEN]);
  • 接口说明  获取设备的DeviceSecret, 用于标识单个设备的密钥, 三元组之一, 在云端控制台注册得到并烧写到设备中  
参数数据类型方向说明
device_secretchar[]输出  存放DeviceSecret的字符串缓冲区  
  • 返回值说明 实际获取到的DeviceSecret字符串长度  

HAL_GetFirmwareVersion

  • 原型
int HAL_GetFirmwareVersion(_OU_ char version[FIRMWARE_VERSION_MAXLEN]);
  • 接口说明 获取设备的固件版本字符串, 此固件版本号将会用于OTA升级的版本上报。如果设备不准备支持OTA,该函数返回空串即可。
  • 参数说明
参数数据类型 方向 说明
version char[]输出  存放FirmwareVersion的字符串缓冲区  
  • 返回值说明  实际获取到的FirmwareVersion字符串长度  

HAL_GetModuleID

(在3.0.1+sp1及以后版本中不需要实现)
  • 原型
int HAL_GetModuleID(_OU_ char mid_str[MID_STR_MAXLEN]);
  • 接口说明  获取设备的Module ID, 仅用于紧密合作伙伴。该函数用于模组商上报模组型号,其它角色的用户返回空串即可  
  • 参数说明
参数数据类型 方向 说明
mid_str char[]输出  存放Module ID的字符串缓冲区  
  • 返回值说明  实际获取到的Module ID字符串长度  

HAL_GetPartnerID

(在3.0.1+sp1及以后版本中不需要实现)
  • 原型
int HAL_GetPartnerID(_OU_ char pid_str[PID_STR_MAXLEN]);
  • 接口说明  获取设备的Partner ID, 仅用于紧密合作伙伴。  
  • 参数说明
参数数据类型 方向 说明
pid_strchar[]输出  存放Partner ID的字符串缓冲区  
  • 返回值说明  实际获取到的Partner ID字符串长度  

HAL_GetProductKey

  • 原型
int HAL_GetProductKey(_OU_ char product_key[PRODUCT_KEY_LEN]);
  • 接口说明  获取设备的ProductKey, 用于标识设备的品类, 三元组之一, 在云端控制台注册得到并烧写到设备中  
  • 参数说明
参数数据类型 方向 说明
product_key char[]输出  存放ProductKey的字符串缓冲区  
  • 返回值说明  实际获取到的ProductKey字符串长度  

HAL_GetProductSecret

  • 原型
int HAL_GetProductSecret(_OU_ char product_secret[DEVICE_SECRET_LEN]);
  • 接口说明  获取设备的ProductSecret, 用于标识品类的密钥, 在云端控制台注册得到并烧写到设备中, 在一型一密的场景下将会使用到此字符串  
  • 参数说明
参数数据类型 方向 说明
product_secret char[]输出  存放ProductSecret的字符串缓冲区  
  • 返回值说明  实际获取到的ProductSecret字符串长度  

HAL_GetTimeStr

(在2.3.1及以后版本中不需要实现)
  • 原型
char *HAL_GetTimeStr(_OU_ char *buf, _IN_ int len);
  • 接口说明 获取当前时间字符串
  • 参数说明
参数数据类型 方向 说明
output uint8_t *输出 指向时间字符串缓冲区的指针
output_lenuint32_t输入字符串缓冲区的字节长度
  • 返回值说明 指向时间字符串缓冲区的指针

HAL_Kv_Del

  • 原型
int HAL_Kv_Del(const char *key);
  • 接口说明 删除指定KV数据, 删除key对应的KV对数据, 可以通过擦除Flash或修改文件数据的方式实现持久化数据的删除
  • 参数说明
参数数据类型 方向 说明
key const char * 输入指向key字符串的指针
buffervoid * 输出指向存放获取数据的指针
buffer_lenint * 输出指向存放获取
  • 返回值说明
说明
0删除成功
1删除失败

HAL_Kv_Erase_All

  • 原型
int HAL_Kv_Erase_All(void);
  • 接口说明 擦除所有的KV数据, 可以通过擦除Flash或修改文件数据的方式实现持久化数据的删除
  • 参数说明 void
  • 返回值说明
说明
0删除成功
1删除失败

HAL_Kv_Get

  • 原型
int HAL_Kv_Get(const char *key, void *buffer, int *buffer_len);
  • 接口说明 获取KV数据, 获取key对应的KV对数据, 可以通过读取Flash或读取文件的方式实现持久化数据的读取
  • 参数说明
参数数据类型 方向 说明
key const char * 输入指向key字符串的指针
buffervoid * 输出指向存放获取数据的指针
buffer_lenint * 输出指向存放获取
  • 返回值说明
说明
0删除成功
1删除失败

HAL_Kv_Set

  • 原型
int HAL_Kv_Set(const char *key, const void *val, int len, int sync);
  • 接口说明 设置KV数据接口, 可通过写flash或写文件的方式实现数据持久化
  • 参数说明
参数数据类型方向说明
keyconst char *输入指向key字符串的指针  
valconst void *输入指向待设置数据的指针
lenint输入待设置数据的字节长度
syncint输入 0: 异步接口. 1: 同步接口  
  • 返回值说明
说明
0设置成功  
-1设置失败

HAL_Malloc

  • 原型
void *HAL_Malloc(_IN_ uint32_t size);
  • 接口说明 申请一块堆内存
  • 参数说明
参数数据类型方向  说明
sizeuint32_t输入申请的堆内存大小
  • 返回值说明
说明
NULL内存申请失败
!NULL指向堆内存首地址的指针

HAL_Printf

  • 原型
void HAL_Printf(_IN_ const char *fmt, ...);
  • 接口说明 打印函数, 用于向串口或其它标准输出打印日志或调试信息, 可参考C99的printf()函数实现
  • 参数说明
参数数据类型方向说明
fmtconst char *输入格式化字符串  
可变类型  输入可变参数列表
  • 返回值说明 
          void   

HAL_Random

  • 原型
uint32_t HAL_Random(_IN_ uint32_t region);
  • 接口说明 随机数函数, 接受一个无符号数作为范围, 返回0到region范围内的一个随机数
  • 参数说明
参数数据类型方向说明
regionuint32_t输入用于指定随机数范围的无符号数
  • 返回值说明
          在指定范围的随机数

HAL_Reboot

  • 原型
void HAL_Reboot(void);
  • 接口说明   
          设备重启, 调用该接口能实现复位功能
  • 参数说明   void
  • 返回值说明   void

HAL_SetDeviceName

  • 原型
int HAL_SetDeviceName(_IN_ char *device_name);
  • 接口说明 设置设备的DeviceName, 用于标识设备单品的名字, 三元组之一
  • 参数说明
参数数据类型方向说明
device_namechar *输出指向待传入DeviceName字符串的指针
  • 返回值说明 
          待设置DeviceName字符串的长度

HAL_SetDeviceSecret

  • 原型
int HAL_SetDeviceSecret(_IN_ char *device_secret);
  • 接口说明 设置设备的DeviceSecret, 用于标识设备单品的密钥, 三元组之一
  • 参数说明
参数数据类型方向说明
device_secretchar *  输出指向待传入DeviceSecret字符串的指针
  • 返回值说明 
          待设置DeviceSecret字符串的长度

HAL_SetProductKey

  • 原型
int HAL_SetProductKey(_IN_ char *product_key);
  • 接口说明 
          设置设备的ProductKey, 用于标识设备的品类, 三元组之一
  • 参数说明
参数数据类型方向说明
product_keychar *输出指向待设置ProductKey字符串的指针  
  1. 返回值说明 
          待设置ProductKey字符串的长度

HAL_SetProductSecret

  • 原型
int HAL_SetProductSecret(_IN_ char *product_secret);
  • 接口说明 
          设备的ProductSecret, 用于标识品类的密钥, 在一型一密场景中会使用到此字符串
  • 参数说明
参数数据类型方向说明
product_secretchar *输出指向待传入ProductSecret字符串的指针
  • 返回值说明 
          待设置ProductSecret字符串的长度

HAL_SleepMs

  • 原型
void HAL_SleepMs(_IN_ uint32_t ms);
  • 接口说明 
          睡眠函数, 使当前执行线程睡眠指定的毫秒数
  • 参数说明
参数数据类型方向说明
msuint32_t输出线程挂起的时间, 单位ms
  • 返回值说明 
          void

HAL_Snprintf

  • 原型
int HAL_Snprintf(_OU_ char *str, _IN_ const int len, _IN_ const char *fmt, ...);
  • 接口说明 
          打印函数, 向内存缓冲区格式化构建一个字符串, 参考C99标准库函数
  • 参数说明
参数数据类型方向说明
strchar *输入指向字符缓冲区的指针
lenint输入缓冲区的字符长度
fmtconst char *输入格式化字符串
输入可变参数列表
  • 返回值说明 
          实际写入缓冲区的字符串长度

HAL_Srandom

  • 原型
void HAL_Srandom(_IN_ uint32_t seed);
  • 接口说明 
          随机数播种函数, 使 HAL_Random 的返回值每个随机序列各不相同, 类似C标准库中的srand
  • 参数说明
参数数据类型方向说明
seeduint32_t输入用于产生新随机序列的种子
  • 返回值说明 
          void

HAL_Sys_reboot

  • 原型
void HAL_Sys_reboot(void);
  • 接口说明 
          系统立即重启
  • 参数说明
          void
  • 返回值说明 
          void

HAL_Timer_Create

  • 原型
void *HAL_Timer_Create(const char *name, void (*func)(void *), void *user_data);
  • 接口说明 
          创建指定名称的定时器, 同时注册用户回调函数和用户数据
  • 参数说明 
参数数据类型方向说明
namechar *输入 定时器名称字符串
funcvoid (func)(void )输入 用户回调函数
user_datavoid *输入 指向用户数据的指针
  • 返回值说明
参数数据类型
NULL创建失败
!NULL创建成功, 返回定时器句柄

HAL_Timer_Delete

  • 原型
int HAL_Timer_Delete(void *timer);
  • 接口说明 
          删除由HAL_Timer_Create()创建的定时器, 释放资源
  • 参数说明
参数数据类型方向说明
timervoid *输入定时器句柄, 此句柄由调用HAL_Timer_Create()时返回
  • 返回值说明
参数数据类型
0操作成功
-1操作失败

HAL_Timer_Start

  • 原型
int HAL_Timer_Start(void *t, int ms);
  • 接口说明 
          启动定时器
  • 参数说明
参数数据类型方向说明
timervoid *输入定时器句柄, 此句柄由调用HAL_Timer_Create()时返回
msint输入定时器定时时间, 单位ms

HAL_Timer_Start

  • 原型
int HAL_Timer_Stop(void *t);
  • 接口说明 
          关闭定时器
  • 参数说明
参数数据类型  方向说明
timervoid *输入定时器句柄, 此句柄由调用HAL_Timer_Create()时返回
  • 返回值说明
参数数据类型
0操作成功  
-1操作失败

HAL_UptimeMs

  • 原型
uint64_t HAL_UptimeMs(void);
  • 接口说明 
          获取设备从上电到当前时刻所经过的毫秒数
  • 参数说明 
          void
  • 返回值说明 
          设备从上电到当前时刻所经过的毫秒数

HAL_UTC_Get

  • 原型
long long HAL_UTC_Get(void);
  • 接口说明 
          获取设备从上电到当前时刻所经过的毫秒数
  • 参数说明 
          void
  • 返回值说明 单位为ms的UTC时间
          单位为ms的UTC时间

HAL_UTC_Set

  • 原型
void HAL_UTC_Set(long long ms);
  • 接口说明 
          设置UTC时间, 设置参数为从Epoch(1970年1月1日00:00:00 UTC)开始所经过的秒数单位
  • 参数说明 
参数数据类型方向说明
mslong long输入单位为ms的UTC时间
  • 返回值说明 
          void

HAL_Vsnprintf

  • 原型
int HAL_Vsnprintf(_OU_ char *str, _IN_ const int len, _IN_ const char *fmt, _IN_ va_list ap);
  • 接口说明 
          格式化输出字符串到指定buffer中, 可参考C标准库的vsnprintf()实现
  • 参数说明 
参数数据类型方向说明
strchar *输出  用于存放写入字符串的buffer
lenconst int输入允许写入的最大字符串长度
fmtconst char输入格式化字符串
apva_list输入可变参数列表
  • 返回值说明 
           成功写入的字符串长

MQTT连云HAL

HAL_SSL_Destroy

  • 原型
int32_t HAL_SSL_Destroy(_IN_ uintptr_t handle);
  • 接口说明
          销毁由参数handle指定的TLS连接
  • 参数说明
参数数据类型方向说明
handleuintptr_t输入TLS连接句柄
  • 返回值说明
参数数据类型
< 0操作失败  
= 0操作成功

HAL_SSL_Establish

  • 原型
uintptr_t HAL_SSL_Establish( _IN_ const char *host, _IN_ uint16_t port, _IN_ const char *ca_crt, _IN_ size_t ca_crt_len);
  • 接口说明 
          根据指定的服务器网络地址, 服务器端口号和证书文件建立TLS连接, 返回对应的连接句柄
  1. 参数说明
参数数据类型方向说明
hostconst char输入指定的TLS服务器网络地址
portuint16_t输入指定的TLS服务器端口
ca_crtconst char输入指向PEM编码的X.509证书的指针
ca_crt_lensize_t输入证书字节长度
  • 返回值说明
参数数据类型
NULL创建失败
!NULL创建成功, 返回TLS连接句柄

HAL_SSL_Read

  • 原型
int32_t HAL_SSL_Read(_IN_ uintptr_t handle, _OU_ char *buf, _OU_ int len, _IN_ int timeout_ms);
  • 接口说明 
          从指定的TLS连接中读取数据, 此接口为同步接口, 如果在超时时间内读取到参数len指定长度的数据则立即返回, 否则在超时时间到时才解除阻塞返回
  • 参数说明
参数数据类型方向说明
handleuintptr_t  输入TLS连接句柄
bufchar *  输出指向数据接收缓冲区的指针
lenint输入数据接收缓冲区的字节大小
timeout_msint输入超时时间
  • 返回值说明
参数数据类型
-2TLS连接发生错误
-1TLS连接被远程设备关闭
0TLS读超时, 且没有接收到任何数据
> 0TLS读取到的字节数, TLS读取成功

HAL_SSL_Write

  • 原型
int32_t HAL_SSL_Write(_IN_ uintptr_t handle, _IN_ const char *buf, _IN_ int len, _IN_ int timeout_ms);
  • 接口说明 
          从指定的TLS连接中写入数据, 此接口为同步接口, 如果在超时时间内写入了参数len指定长度的数据则立即返回, 否则在超时时间到时才解除阻塞返回
  • 参数说明
参数数据类型方向说明
handleuintptr_t输入TLS连接句柄
bufchar *输出指向数据接收缓冲区的指针
lenint输入数据接收缓冲区的字节大小
timeout_msint输入超时时间
  • 返回值说明
参数数据类型
< 0TLS连接发生错误
0TLS写超时, 且没有写入任何数据
> 0TLS写入的字节数, TLS写入成功

HAL_TCP_Destroy

  • 原型
int32_t HAL_TCP_Destroy(_IN_ uintptr_t fd);
  • 接口说明 
          销毁由参数fd指定的TCP连接, 释放资源
  • 参数说明
参数数据类型方向说明
fduintptr_t输入TLS连接句柄
  • 返回值说明
参数数据类型
< 0操作失败
= 0操作成功  

HAL_TCP_Establish

  • 原型
uintptr_t HAL_TCP_Establish(_IN_ const char *host, _IN_ uint16_t port);
  • 接口说明 
          根据指定的服务器网络地址和端口号建立TCP连接, 并返回对应连接句柄
  • 参数说明 
参数数据类型方向说明
hostconst char *输入指定TCP服务器的网络地址
portuint16_t输入指定TCP服务器的端口号
  • 返回值说明
参数数据类型
NULLTCP连接建立失败
!NULLTCP连接建立成功, 返回对应的连接句柄

HAL_TCP_Read

  • 原型
int32_t HAL_TCP_Read(_IN_ uintptr_t fd, _OU_ char *buf, _IN_ uint32_t len, _IN_ uint32_t timeout_ms);
  • 接口说明 
从指定的TCP连接中读取数据, 此接口为同步接口, 如果在超时时间内读取到参数len指定长度的数据则立即返回, 否则在超时时间到时才解除阻塞返回
  • 参数说明
参数数据类型方向说明
fduintptr_t  输入TCP连接句柄  
bufchar *输出指向数据接收缓冲区的指针
lenint输入数据接收缓冲区的字节大小
timeout_msint输入超时时间
  • 返回值说明
参数数据类型
-2TLS连接发生错误
-1TLS连接被远程设备关闭
0TLS读超时, 且没有接收到任何数据
> 0TLS读取到的字节数, TLS读取成功

HAL_TCP_Write

  • 原型
int32_t HAL_TCP_Write(_IN_ uintptr_t fd, _IN_ const char *buf, _IN_ uint32_t len, _IN_ uint32_t timeout_ms);
  • 接口说明
从指定的TCP连接中写入数据, 此接口为同步接口, 如果在超时时间内写入了参数len指定长度的数据则立即返回, 否则在超时时间到时才解除阻塞返回
  • 参数说明
参数数据类型  方向说明
fduintptr_t输入TLS连接句柄
bufchar *输出指向数据接收缓冲区的指针
lenint输入数据接收缓冲区的字节大小
timeout_msint输入超时时间
  • 返回值说明
参数数据类型
< 0TLS连接发生错误
0TLS写超时, 且没有写入任何数据
> 0TLS写入的字节数, TLS写入成功

线程HAL

HAL_MutexCreate

  • 原型
void *HAL_MutexCreate(void);
  • 接口说明 
创建一个互斥量对象, 返回指向所创建互斥量的指针, 用于同步访问, 对于仅支持单线程应用, 可实现为空函数
  • 参数说明
          void
  • 返回值说明
          void

HAL_MutexDestroy

  • 原型
void HAL_MutexDestroy(_IN_ void *mutex);
  • 接口说明
          锁住一个互斥量
  • 参数说明
参数数据类型  方向说明
mutexvoid *输入互斥量指针
  • 返回值说明 
           void

HAL_MutexLock

  • 原型
void HAL_MutexLock(_IN_ void *mutex);
  • 接口说明 
           解锁一个互斥量
  • 参数说明
参数数据类型方向说明
mutexvoid *输入互斥量指针
  • 返回值说明 
          void

HAL_MutexUnlock

  • 原型
void HAL_MutexUnlock(_IN_ void *mutex);
  • 接口说明
          解锁一个互斥量
  • 参数说明
参数数据类型方向说明
mutexvoid *输入互斥量指针
  • 返回值说明
          void

HAL_SemaphoreCreate

  • 原型
void *HAL_SemaphoreCreate(void);
  • 接口说明 
创建一个计数信号量, 此接口实现必须为原子操作, 对于仅支持单线程应用, 可实现为空函数
  • 参数说明
          void
  • 返回值说明
参数数据类型
NULL创建失败
!NULL创建成功, 返回信号量句柄

HAL_SemaphoreDestroy

  • 原型
void HAL_SemaphoreDestroy(_IN_ void *sem);
  • 接口说明 
销毁一个由参数sem指定的信号量, 此接口实现必须为原子操作, 此函数无返回值
  • 参数说明
参数数据类型方向说明
semvoid *输入信号量指针
  • 返回值说明 
          void

HAL_SemaphorePost

  • 原型
void HAL_SemaphorePost(_IN_ void *sem);
  • 接口说明 
在指定的计数信号量上做自增操作, 解除其它线程的等待, 此接口实现必须为原子操作, 对于仅支持单线程应用, 可实现为空函数
  • 参数说明
参数数据类型方向说明
semvoid *输入信号量句柄
  • 返回值说明 
          void

HAL_SemaphoreWait

  • 原型
int HAL_SemaphoreWait(_IN_ void *sem, _IN_ uint32_t timeout_ms);
  1. 接口说明 
在指定的计数信号量上等待并做自减操作, 对于仅支持单线程应用, 此接口实现必须为原子操作, 可实现为空函数
  1. 参数说明 
参数数据类型方向说明
semvoid *输入信号量句柄
timeout_msuint32_t输入信号量等待超时时间, 单位ms, 如果参数为PLATFORM_WAIT_INFINITE, 则函数返回只能由获取信号量触发
  • 返回值说明
参数数据类型
0函数返回是由信号量触发  
-1函数返回是由超时触发  

HAL_ThreadCreate

  • 原型
int HAL_ThreadCreate( _OU_ void **thread_handle, _IN_ void *(*work_routine)(void *), _IN_ void *arg, _IN_ hal_os_thread_param_t *hal_os_thread_param, _OU_ int *stack_used);
  • 接口说明 
          按照指定入参创建一个线程
  • 参数说明
参数数据类型方向说明
thread_handlevoid **输出指向线程句柄变量的指针
work_routinevoid (work_routine)(void *)输入指向线程执行函数的函数指针
argvoid *输入传递给运行程序的单个参数
hal_os_thread_paramhal_os_thread_param_t *输入指向线程初始化参数的指针
stack_usedint *输出指示平台是否使用栈缓冲区, 0: 未使用. 1: 使用
线程初始化参数定义:
typedef struct _hal_os_thread { hal_os_thread_priority_t priority; /* initial thread priority */ void *stack_addr; /* thread stack address malloced by caller, use system stack by . */ size_t stack_size; /* stack size requirements in bytes; 0 is default stack size */ int detach_state; /* 0: not detached state; otherwise: detached state. */ char *name; /* thread name. */ } hal_os_thread_param_t;
线程优先级定义:
typedef enum { os_thread_priority_idle = -3, /* priority: idle (lowest) */ os_thread_priority_low = -2, /* priority: low */ os_thread_priority_belowNormal = -1, /* priority: below normal */ os_thread_priority_normal = 0, /* priority: normal (default) */ os_thread_priority_aboveNormal = 1, /* priority: above normal */ os_thread_priority_high = 2, /* priority: high */ os_thread_priority_realtime = 3, /* priority: realtime (highest) */ os_thread_priority_error = 0x84, /* system cannot determine priority or thread has illegal priority */ } hal_os_thread_priority_t;
  • 返回值说明
参数数据类型
-1创建失败
0创建成功  

HAL_ThreadDelete

  • 原型
void HAL_ThreadDelete(_IN_ void *thread_handle);
  • 接口说明 
          删除指定的线程
  • 参数说明
参数数据类型方向说明
thread_handlevoid *输入线程句柄, NULL表示当前线程
  1. 返回值说明 
          void

HAL_ThreadDetach

  • 原型
void HAL_ThreadDetach(_IN_ void *thread_handle);
  • 接口说明 
          将指定线程设置为分离状态
  • 参数说明
参数数据类型方向说明
thread_handlevoid *输入 线程句柄, NULL表示当前线程  
  • 返回值说明 
          void

设备认证

设备的身份认证分为一机一密以及一型一密两种:
  • 一机一密:在设备上烧写设备的ProductKey、DeviceName、DeviceSecret,然后适配相应的HAL并调用SDK提供的连接云端的函数即可,这种方式要求对设备的产线工具进行一定的修改,需要对每个设备烧写不同的DeviceName和DeviceSecret;
  • 一型一密:设备上烧写设备的ProductKey、ProductSecret,每个设备需要具备自己的唯一标识并将该标识预先上传到阿里云IoT物联网平台,然后调用SDK提供的函数连接云端。这种方式每个设备上烧写的信息是固定的ProductKey、ProductSecret

一机一密编程

设备厂商需要实现下面的三个HAL,用于让SDK获取设备三元组:
  • HAL_GetProductKey
  • HAL_GetDeviceName
  • HAL_GetDeviceSecret
代码示例:
int main(int argc, char *argv[]) { void *pclient = NULL; int res = 0; int loop_cnt = 0; iotx_mqtt_param_t mqtt_params; memset(&mqtt_params, 0x0, sizeof(mqtt_params)); mqtt_params.handle_event.h_fp = example_event_handle; pclient = IOT_MQTT_Construct(&mqtt_params); if (NULL == pclient) { EXAMPLE_TRACE("MQTT construct failed"); return -1; } ... }
++注:IOT_MQTT_Construct()将会调用上面提到的HAL_GetProductKey()等三个HAL函数去获取设备的身份信息。++

一型一密编程

使用的流程示意

它工作时, IoT设备端和外界发生网络通信的交互过程是:(简写说明)
  • PK: ProductKey, 设备品类标识字符串
  • PS: ProductSecret, 设备品类密钥
  • DN: DeviceName, 某台设备的标识字符串
  • DS: DeviceSecret, 某台设备的设备密钥

流程简述

1.设备使用PK、PS和设备标识(DN)到阿里云物联网获取该设备对应的DS 2.阿里云物联网的动态注册Server将查找该设备的标识(DN)是否在该PK对应的设备列表,如果该设备在列表中则将该设备的DS返回 3.设备收到DS之后,将使用一机一密的方式计算MQTT连接参数以及签名 4.设备使用计算出来的MQTT连接参数连接阿里云物联网平台
注:
  • 1、用户必须将获取的DeviceSecret持久化到设备, 以备后续使用. 若获取的DeviceSecret丢失可能导致设备无法上线等严重后果, 云端服务器不会接受已激活设备重复的动态注册请求
  • 2、 使用一型一密功能, 用户必须对每个设备进行预注册, 即在阿里云物联网平台的控制台上传每个设备的DeviceName,并且在控制台上打开对应产品的动态注册功能

例子程序讲解

一型一密功能的例子程序在 src/dynamic_register/examples/dynreg_example.c, 以下对其逐段讲解 要使用一型一密功能, 要包含它的头文件 dynreg_api.h
#include <stdio.h> #include <string.h> #include "infra_types.h" #include "infra_defs.h" #include "dynreg_api.h"
准备输入参数 region 和出入参结构体 meta
iotx_http_region_types_t region = IOTX_HTTP_REGION_SHANGHAI; HAL_Printf("dynreg example\n"); memset(&meta,0,sizeof(iotx_dev_meta_info_t)); HAL_GetProductKey(meta.product_key); HAL_GetProductSecret(meta.product_secret); HAL_GetDeviceName(meta.device_name);
以上例子程序用 IOTX_CLOUD_REGION_SHANGHAI 代表的华东二站点作为例子, 演示连接上海服务器时候的情况, 另一个入参 meta 其实就是填入设备的 PK/PS/DN 调用一型一密的API获取 DeviceSecret
res = IOT_Dynamic_Register(region, &meta); if (res < 0) { HAL_Printf("IOT_Dynamic_Register failed\n"); return -1; } HAL_Printf("\nDevice Secret: %s\n\n", meta.device_secret);
这个 IOT_Dynamic_Register() 接口就是一型一密功能点唯一提供的用户接口, 若执行成功, 在参数 meta 中将填上从服务器成功获取到的 DeviceSecret
其它(参考上面的图示)
  • 第1步和第2步对应用户接口: IOT_Dynamic_Register()
  • 第3步对应用户接口: IOT_Sign_MQTT()
  • 第4步对应用户接口: IOT_MQTT_Construct() 注:
  • 1、因为当设备获取到DeviceSecret之后再次调用IOT_Dynamic_Register()将会返回失败,因此用户编程时获取到DeviceSecret之后需要将其保存到Flash中;
  • 2、用户的程序在调用IOT_Dynamic_Register()之前应该先调用HAL_GetDeviceSecret()查看设备是否已经获取到了DeviceSecret,如果已经获取到,则无需再次去调用IOT_Dynamic_Register()

功能API接口

  • 原型
1.int32_t IOT_Dynamic_Register(iotx_http_region_types_t region, iotx_dev_meta_info_t *meta);
  • 接口说明 根据输入参数中指定的站点区域, 以及productKey和productSecret, 去云端为deviceName指定的设备去云端申请deviceSecret
  • 参数说明
参数数据类型方向说明
regioniotx_http_region_types_t输入表示设备将要工作的区域, 例如美西/新加坡/日本/华东2站点等
metaiotx_dev_meta_info_t *输入输出输入的时候带入设备的PK/PS/DN, 输出的时候返回从服务器取到的 DS

设备签名

功能说明

阿里云物联网平台支持使用MQTT协议作为IoT设备和平台之间的通信协议,MQTT Client在连接阿里云物联网平台时MQTT的userName、password、clientID需要遵守阿里云IoT物联网平台的规定,物联网平台将会使用这些参数验证设备的合法性。
功能点 dev_sign 提供的就是该签名字段的计算封装, 这个功能称为”设备签名”
如上图所示:
  • 左边的是该模块的输入参数, 三元组和region分别表达设备身份和想要连接的站点所在区域(华东2/日本/美西/新加坡…)
  • 右边的是该模块的返回参数, hostname/port是连接服务器的域名和端口
  • 另外3个则是IoT设备连接云平台时证明自己合法的身份认证信息, 统称为”设备签名信息” 图示正中间的是该模块提供的唯一一个用户接口API

使用场景说明

如果需要集成SDK的设备已支持MQTT Client,并且用户打算使用MQTT Topic直接与物联网平台进行通信,并且不使用SDK的其它任何功能,可以调用设备签名API获取MQTT协议连接阿里云物联网平台时需要设置的MQTT参数,然后使用用户自己的MQTT Client去连接阿里云物联网平台。 注:用户需要确保MQTT Client的KeepAlive机制正常运行。

例子程序讲解

设备签名的例子程序在 src/dev_sign/examples/dev_sign_example.c, 以下对其进行逐段讲解 要使用 dev_sign 功能, 需包含它的API头文件 dev_sign_api.h
#include "dev_sign_api.h"
从IoT控制台申请到一台新的IoT设备, 记录其三元组
#define EXAMPLE_PRODUCT_KEY "a1X2bEnP82z" #define EXAMPLE_PRODUCT_SECRET "7jluWm1zql7bt8qK" #define EXAMPLE_DEVICE_NAME "example1" #define EXAMPLE_DEVICE_SECRET "ga7XA6KdlEeiPXQPpRbAjOZXwG8ydgSe"
声明需要用户提供的底层接口 功能点 dev_sign 是零依赖的, 不需要用户做任何HAL的适配就可以使用. 这里声明了 HAL_Printf 仅是因为例程可能需要输出字符
/* Implenment this HAL or using "printf" of your own system if you want to print something in example*/ void HAL_Printf(const char *fmt, ...);
准备出入参结构体
int main(int argc, char *argv[]) { iotx_mqtt_region_types_t region = IOTX_CLOUD_REGION_SHANGHAI; iotx_dev_meta_info_t meta; iotx_sign_mqtt_t sign_mqtt; memset(&meta,0,sizeof(iotx_dev_meta_info_t)); memcpy(meta.product_key,EXAMPLE_PRODUCT_KEY,strlen(EXAMPLE_PRODUCT_KEY)); memcpy(meta.product_secret,EXAMPLE_PRODUCT_SECRET,strlen(EXAMPLE_PRODUCT_SECRET)); memcpy(meta.device_name,EXAMPLE_DEVICE_NAME,strlen(EXAMPLE_DEVICE_NAME)); memcpy(meta.device_secret,EXAMPLE_DEVICE_SECRET,strlen(EXAMPLE_DEVICE_SECRET));
准备一个类型为 iotx_dev_meta_info_t 的结构体变量, 把需要计算签名的设备标识信息填入其中即可 然后调用 dev_sign 功能点唯一的一个用户接口 IOT_Sign_MQTT(), 计算签名
if (IOT_Sign_MQTT(region,&meta,&sign_mqtt) < 0) { return -1; }
结果就在输出参数 sign_mqtt 中, 它是一个 iotx_sign_mqtt_t 类型的结构体变量 打印签名结果或者去连MQTT服务器
#if 0 /* Uncomment this if you want to show more information */ HAL_Printf("sign_mqtt.hostname: %s\n",sign_mqtt.hostname); HAL_Printf("sign_mqtt.port : %d\n",sign_mqtt.port); HAL_Printf("sign_mqtt.username: %s\n",sign_mqtt.username); HAL_Printf("sign_mqtt.password: %s\n",sign_mqtt.password); HAL_Printf("sign_mqtt.clientid: %s\n",sign_mqtt.clientid); #endif }
为了减轻用户对接实现 HAL_Printf 的负担, IOT_Sign_MQTT() 在执行的过程中不会做任何打印动作, 如果想观察结果可打开上述注释
输出参数中的各个成员意义如下:
成员名含义
sign_mqtt.hostname可以连接的MQTT服务器域名地址, 根据入参 region 自动计算
sign_mqtt.port可以连接的MQTT服务器端口号, 根据标准MQTT协议一般是 1883 或者 443
sign_mqtt.username连接MQTT服务器时将要使用的用户名
sign_mqtt.password连接MQTT服务器时将要使用的密码, 和用户名一一对应  
sign_mqtt.clientid连接MQTT服务器时标识设备的自定义ID, 服务器对此不做校验, 鉴权通过 username 和 password 进行

功能API接口

  • 原型
int32_t IOT_Sign_MQTT(iotx_mqtt_region_types_t region, iotx_dev_meta_info_t *meta, iotx_sign_mqtt_t *signout);
  • 接口说明 用于计算给定的IoT设备身份认证信息, 这些信息统称为它的”签名”, 将在输出参数 signout 中返回
  • 参数说明
参数数据类型方向说明
regioniotx_mqtt_region_types_t输入  表示设备将要工作的区域, 例如美西/新加坡/日本/华东2站点等
metaiotx_dev_meta_info_t *输入  存放设备的标识字符串, 包括 productKey,deviceName 等  
signoutiotx_sign_mqtt_t *输入  存放计算好的签名信息, 包括MQTT服务器地址/端口, 连接时的用户名/密码等

需要对接的HAL接口

功能点 dev_sign 是零依赖的, 不需要用户做任何开发动作就可以使用, 所以不需要对接HAL接口

自定义MQTT Topic通信

功能说明

用户可以直接通过SDK提供的MQTT API与阿里云物联网平台通信,也即用户可以通过向指定的topic发送消息的方式将数据发送到阿里云物联网平台,也可以通过订阅指定的topic从阿里云物联网平台接收数据,这些topic都是用户自己定义的。 直接使用MQTT TOPIC与物联网平台通信的流程示意图如下:
说明:
  • 通过调用 IOT_MQTT_Construct() 接口调用, 建立设备和云平台之间的长连接这个接口是用来向云平台发起连接请求的, 其参数中需要的”签名信息”可以通过 dev_sign 功能中的IOT_Sign_MQTT() 获得连接成功的话, 会返回一个 handle 参数, 这就是连接的句柄, 可用作之后所有MQTT网络接口的入参
  • 通过 IOT_MQTT_Subscribe() 接口调用, 可以向云平台表达设备将接收哪些Topic上的报文
  • 然后进入业务主循环通过 IOT_MQTT_Publish() 或 IOT_MQTT_Publish_Simple(), 可将消息上报到云端IOT_MQTT_Yield()用于接收云端下发的消息, 并调用用户在 IOT_MQTT_Subscribe() 时指定的回调函数,用于对数据进行处理

例子程序讲解

MQTT上云的例子程序在 src/mqtt/examples/mqtt_example.c, 在”快速体验”一章中已对其进行过逐段的讲解。

功能API接口

函数名说明
IOT_MQTT_CheckStateNormalMQTT连接后, 调用此函数检查长连接是否正常
IOT_MQTT_ConstructMQTT实例的构造函数, 入参为iotx_mqtt_param_t结构体, 连接MQTT服务器, 并返回被创建句柄
IOT_MQTT_DestroyMQTT实例的摧毁函数, 入参为 IOT_MQTT_Construct 创建的句柄
IOT_MQTT_PublishMQTT会话阶段, 组织一个完整的MQTT Publish报文, 向服务端发送消息发布报文
IOT_MQTT_Publish_SimpleMQTT会话阶段, 组织一个完整的MQTT Publish报文, 向服务端发送消息发布报文,参数中不含结构体等复杂数据类型
IOT_MQTT_SubscribeMQTT会话阶段, 组织一个完整的MQTT Subscribe报文, 向服务端发送订阅请求
IOT_MQTT_Subscribe_SyncMQTT会话阶段, 组织一个完整的MQTT Subscribe报文, 向服务端发送订阅请求,并等待应答
IOT_MQTT_UnsubscribehMQTT会话阶段, 组织一个完整的MQTT UnSubscribe报文, 向服务端发送取消订阅请求  
IOT_MQTT_YieldMQTT会话阶段, MQTT主循环函数, 内含了心跳的维持, 服务器下行报文的收取等

需要实现的HAL接口

以下函数为可选实现, 如果希望SDK提供MQTT通道功能, 则需要用户对接
函数名说明
HAL_UptimeMs返回设备加电以来到当前时间点过去的毫秒数  
HAL_SleepMs按照入参指定的毫秒数睡眠相应时间
HAL_SSL_Destroy销毁一个TLS连接, 用于MQTT功能, 加密连接的情况
HAL_SSL_Establish建立一个TLS连接, 用于MQTT功能, 加密连接的情况
HAL_SSL_Read从一个TLS连接中读数据, 用于MQTT功能, 加密连接的情况
HAL_SSL_Write向一个TLS连接中写数据, 用于MQTT功能, 加密连接的情况
HAL_TCP_Destroy销毁一个TLS连接, 用于MQTT功能
HAL_TCP_Establish建立一个TCP连接, 包含了域名解析的动作和TCP连接的建立
HAL_TCP_Read在指定时间内, 从TCP连接读取流数据, 并返回读到的字节数
HAL_TCP_Write在指定时间内, 向TCP连接发送流数据, 并返回发送的字节数
注: SDK提供的用户API接口都列在 src/mqtt/mqtt_api.h, 可能需要对接的HAL函数都列在src/mqtt/mqtt_wrapper.h, 以代码为准

物模型编程

物模型管理功能是指使用 属性/事件/服务 的方式对产品支持的能力进行描述,在设备开发时也需要以物模型的方式进行编程 下面的讲解中使用了示例代码./src/dev_model/examples/linkkit_example_solo.c 注意:
  • 1.在Linux环境下,用户可通过修改wrappers/os/ubuntu/HAL_OS_linux.c文件中的默认三元组来使用自己在云端控制台创建的设备的身份信息
  • 2.我们在src/dev_model/examples目录下提供了名为model_for_example.json的物模型描述文件,用户可以用自己产品的productkey替换掉该文件中的”productKey”值后,将该物模型文件导入到物联网平台上的产品定义中,这样用户可以快速的参照示例代码来体验基于物模型的编程方式。

设备属性

属性上报说明 用户可以调用IOT_Linkkit_Report()函数来上报属性,属性上报时需要按照云端定义的属性格式使用JSON编码后进行上报。示例中函数user_post_property展示了如何使用IOT_Linkkit_Report进行属性上报(对于异常情况的上报,详见example):
void user_post_property(void) { static int cnt = 0; int res = 0; char property_payload[30] = {0}; HAL_Snprintf(property_payload, sizeof(property_payload), "{\"Counter\": %d}", cnt++); res = IOT_Linkkit_Report(EXAMPLE_MASTER_DEVID, ITM_MSG_POST_PROPERTY, (unsigned char *)property_payload, strlen(property_payload)); EXAMPLE_TRACE("Post Property Message ID: %d", res); }
注:property_payload = “{\”Counter\”:1}” 即是将属性编码为JSON对象。
当上报属性或者事件时需要云端应答时, 通过IOT_Ioctl对IOTX_IOCTL_RECV_EVENT_REPLY选项进行设置,属性设置说明: 示例代码在回调函数user_property_set_event_handler中获取云端设置的属性值, 并原样将收到的数据发回给云端, 这样可以更新在云端的设备属性值, 用户可在此处对收到的属性值进行处理。 注:该回调函数是在example初始化时使用IOT_RegisterCallback注册的ITE_SERVICE_REQUST事件对应的回调函数:
static int user_property_set_event_handler(const int devid, const char *request, const int request_len) { int res = 0; user_example_ctx_t *user_example_ctx = user_example_get_ctx(); EXAMPLE_TRACE("Property Set Received, Devid: %d, Request: %s", devid, request); res = IOT_Linkkit_Report(user_example_ctx->master_devid, ITM_MSG_POST_PROPERTY, (unsigned char *)request, request_len); EXAMPLE_TRACE("Post Property Message ID: %d", res); return 0; }

设备服务

在设备端示例程序中, 当收到服务调用请求时, 会进入如下回调函数, 以下代码使用了cJSON来解析服务请求的参数值:
static int user_property_set_event_handler(const int devid, const char *request, const int request_len) { static int user_service_request_event_handler(const int devid, const char *serviceid, const int serviceid_len, const char *request, const int request_len, char **response, int *response_len) { int add_result = 0; cJSON *root = NULL, *item_number_a = NULL, *item_number_b = NULL; const char *response_fmt = "{\"Result\": %d}"; EXAMPLE_TRACE("Service Request Received, Service ID: %.*s, Payload: %s", serviceid_len, serviceid, request); /* Parse Root */ root = cJSON_Parse(request); if (root == NULL || !cJSON_IsObject(root)) { EXAMPLE_TRACE("JSON Parse Error"); return -1; } if (strlen("Operation_Service") == serviceid_len && memcmp("Operation_Service", serviceid, serviceid_len) == 0) { /* Parse NumberA */ item_number_a = cJSON_GetObjectItem(root, "NumberA"); if (item_number_a == NULL || !cJSON_IsNumber(item_number_a)) { cJSON_Delete(root); return -1; } EXAMPLE_TRACE("NumberA = %d", item_number_a->valueint); /* Parse NumberB */ item_number_b = cJSON_GetObjectItem(root, "NumberB"); if (item_number_b == NULL || !cJSON_IsNumber(item_number_b)) { cJSON_Delete(root); return -1; } EXAMPLE_TRACE("NumberB = %d", item_number_b->valueint); add_result = item_number_a->valueint + item_number_b->valueint; /* Send Service Response To Cloud */ *response_len = strlen(response_fmt) + 10 + 1; *response = (char *)HAL_Malloc(*response_len); if (*response == NULL) { EXAMPLE_TRACE("Memory Not Enough"); return -1; } memset(*response, 0, *response_len); HAL_Snprintf(*response, *response_len, response_fmt, add_result); *response_len = strlen(*response); } cJSON_Delete(root); return 0; } }

设备事件

示例中使用 IOT_Linkkit_TriggerEvent 上报事件. 其中展示了如何使用IOT_Linkkit_Report进行事件上报(对于异常情况的上报,详见example:
void user_post_event(void) { int res = 0; char *event_id = "HardwareError"; char *event_payload = "{\"ErrorCode\": 0}"; res = IOT_Linkkit_TriggerEvent(EXAMPLE_MASTER_DEVID, event_id, strlen(event_id), event_payload, strlen(event_payload)); EXAMPLE_TRACE("Post Event Message ID: %d", res); }

关于上报消息的格式说明及示例

上报属性时, 属性ID和值以JSON格式的形式放在IOT_Linkkit_Report()的payload中, 不同数据类型以及多个属性的格式示例如下:
/* 整型数据 */ char *payload = "{\"Brightness\":50}"; /* 浮点型数据上报 */ char *payload = "{\"Temperature\":11.11}"; /* 枚举型数据上报 */ char *payload = "{\"WorkMode\":2}"; /* 布尔型数据上报, 在物模型定义中, 布尔型为整型, 取值为0或1, 与JSON格式的整型不同 */ char *payload = "{\"LightSwitch\":1}"; /* 字符串数据上报 */ char *payload = "{\"Description\":\"Amazing Example\"}"; /* 时间型数据上报, 在物模型定义中, 时间型为字符串 */ char *payload = "{\"Timestamp\":\"1252512000\"}"; /* 复合类型属性上报, 在物模型定义中, 符合类型属性为JSON对象 */ char *payload = "{\"RGBColor:{\"Red\":11,\"Green\":22,\"Blue\":33}\"}"; /* 多属性上报, 如果需要上报以上各种数据类型的所有属性, 将它们放在一个JSON对象中即可 */ char *payload = "{\"Brightness\":50,\"Temperature\":11.11,\"WorkMode\":2,\"LightSwitch\":1,\"Description\":\"Amazing Example\",\"Timestamp\":\"1252512000\",\"RGBColor:{\"Red\":11,\"Green\":22,\"Blue\":33}\"}"; /* 属性payload准备好以后, 就可以使用如下接口进行上报了 */ IOT_Linkkit_Report(devid, ITM_MSG_POST_PROPERTY, payload, strlen(payload));
上报事件时, 与上报属性的区别是, 事件ID需要单独拿出来, 放在IOT_Linkkit_TriggerEvent()的eventid中, 而事件的上报内容, 也就是物模型定义中事件的输出参数, 则使用与上报属性相同的格式进行上报, 示例如下:
/* 事件ID为Error, 其输出参数ID为ErrorCode, 数据类型为枚举型 */ char *eventid = "Error"; char *payload = "{\"ErrorCode\":0}"; /* 事件ID为HeartbeatNotification, 其输出参数有2个. 第一个是布尔型参数ParkingState, 第二个是浮点型参数VoltageValue */ char *eventid = "HeartbeatNotification"; char *payload = "{\"ParkingState\":1,\"VoltageValue\":3.0}"; /* 事件payload准备好以后, 就可以使用如下接口进行上报了 */ IOT_Linkkit_TriggerEvent(devid, event_id, strlen(event_id), payload, strlen(payload)); /* 从上面的示例可以看出, 当事件的输出参数有多个时, payload的格式与多属性上报是相同的 */

基于MQTT Topic进行数据收发

注意: 虽然物模型编程的 API 并未返回 MQTT 编程接口 IOT_MQTT_XXX() 所需要的 pClient 参数, 但基于MQTT Topic进行数据收发仍可和物模型编程混用
  • 所有MQTT数据收发的接口, 第1个参数都可接受参数 0 作为输入, 表示 “使用当前唯一的MQTT通道进行数据收发等操作”, 包括IOT_MQTT_ConstructIOT_MQTT_DestroyIOT_MQTT_YieldIOT_MQTT_CheckStateNormalIOT_MQTT_SubscribeIOT_MQTT_UnsubscribeIOT_MQTT_PublishIOT_MQTT_Subscribe_SyncIOT_MQTT_Publish_Simple
  • 比如要在使用物模型编程API的程序代码段落中表示对某个Topic进行订阅, 可以用
IOT_MQTT_Subscribe(0, topic_request, IOTX_MQTT_QOS0, topic_callback, topic_context);
  • 比如要在使用物模型编程API的程序代码段落中表示在某个Topic进行发布(数据上报), 可以用
IOT_MQTT_Publish_Simple(0, topic, IOTX_MQTT_QOS0, payload, payload_len);

与物模型功能相关的API列表

函数名说明
IOT_Linkkit_Open创建本地资源, 在进行网络报文交互之前, 必须先调用此接口, 得到一个会话的句柄
IOT_Linkkit_Connect对主设备/网关来说, 将会建立设备与云端的通信. 对于子设备来说, 将向云端注册该子设备(若需要), 并添加主子设备拓扑关系
IOT_Linkkit_Yield若SDK占有独立线程, 该函数只将接收到的网络报文分发到用户的回调函数中, 否则表示将CPU交给SDK让其接收网络报文并将消息分发到用户的回调函数中
IOT_Linkkit_Close若入参中的会话句柄为主设备/网关, 则关闭网络连接并释放SDK为该会话所占用的所有资源
IOT_Linkkit_TriggerEvent向云端发送事件报文, 如错误码, 异常告警等
IOT_Linkkit_Report向云端发送没有云端业务数据下发的上行报文, 包括属性值/设备标签/二进制透传数据/子设备管理等各种报文
IOT_Linkkit_Query向云端发送存在云端业务数据下发的查询报文, 包括OTA状态查询/OTA固件下载/子设备拓扑查询/NTP时间查询等各种报文
IOT_RegisterCallback对SDK注册事件回调函数, 如云端连接成功/失败, 有属性设置/服务请求到达, 子设备管理报文答复等
IOT_Ioctl

4.x版本的HAL列表

基础HAL

core_sysdep_malloc

void *core_sysdep_malloc(uint32_t size, char *name)
  • 接口说明 申请内存
  • 参数说明
参数数据类型说明
sizeuint32_t申请内存大小
namechar*别名
  • 返回值说明
          void*

core_sysdep_free

void core_sysdep_free(void *ptr)
  • 接口说明 释放内存
  • 参数说明
参数数据类型说明
ptrvoid*释放内存的地址
  • 返回值说明
          void

core_sysdep_time

uint64_t core_sysdep_time(void)
  • 接口说明 获取当前时间戳
  • 参数说明
参数数据类型说明
voidvoid
  • 返回值说明
          返回时间戳

core_sysdep_sleep

void core_sysdep_sleep(uint64_t time_ms)
  • 接口说明 睡眠毫秒级
  • 参数说明
参数数据类型说明
time_msuint64_t毫秒
  • 返回值说明
          void

core_sysdep_network_init

void *core_sysdep_network_init(void)
  • 接口说明 创建一个网络会话
  • 参数说明
参数数据类型说明
voidvoid
  • 返回值说明
          网络会话句柄

core_sysdep_network_setopt

int32_t core_sysdep_network_setopt(void *handle, core_sysdep_network_option_t option, void *data)
  • 接口说明 配置一个网络会话的连接参数
  • 参数说明
参数数据类型说明
handlevoid*网络会话句柄
optioncore_sysdep_network_option_t 网络连接参数
datavoid*网络连接参数的配置数据
  • 返回值说明
          返回对应错误码,在\core\aiot_state_api.h中定义

core_sysdep_network_establish

int32_t core_sysdep_network_establish(void *handle)
  • 接口说明 建立一个网络会话,作为MQTT/HTTP等协议的底层承载
  • 参数说明
参数数据类型说明
handlevoid*网络会话句柄
  • 返回值说明
          返回对应错误码,在\core\aiot_state_api.h中定义

core_sysdep_network_send

int32_t core_sysdep_network_send(void *handle, uint8_t *buffer, uint32_t len, uint32_t timeout_ms, core_sysdep_addr_t *addr)
  • 接口说明 在指定的网络会话上发送
  • 参数说明
参数数据类型说明
handlevoid*网络会话句柄
bufferuint8_t* 发送的数据
lenuint32_t 发送的数据长度
timeout_msuint32_t 超时时间
addrcore_sysdep_addr_t* 包含ip和port的数据结构
  • 返回值说明
          返回对应错误码,在\core\aiot_state_api.h中定义

core_sysdep_network_recv

int32_t core_sysdep_network_recv(void *handle, uint8_t *buffer, uint32_t len, uint32_t timeout_ms, core_sysdep_addr_t *addr)
  • 接口说明 在指定的网络会话上接收
  • 参数说明
参数数据类型说明
handlevoid*网络会话句柄
bufferuint8_t* 接收的数据
lenuint32_t 接收的数据长度
timeout_msuint32_t 超时时间
addrcore_sysdep_addr_t* 包含ip和port的数据结构
  • 返回值说明
          返回对应错误码,在\core\aiot_state_api.h中定义

core_sysdep_network_deinit

int32_t core_sysdep_network_deinit(void **handle)
  • 接口说明 销毁网络句柄
  • 参数说明
参数数据类型说明
handlevoid**网络会话句柄
  • 返回值说明
          返回对应错误码,在\core\aiot_state_api.h中定义

core_sysdep_rand

void core_sysdep_rand(uint8_t *output, uint32_t output_len)
  • 接口说明 随机数的生成方法
  • 参数说明
参数数据类型说明
outputuint8_t *数据
output_lenuint32_t 数据长度
  • 返回值说明
          void

core_sysdep_mutex_init

void *core_sysdep_mutex_init(void)
  • 接口说明 创建互斥锁
  • 参数说明
参数数据类型说明
voidvoid
  • 返回值说明
          互斥锁的句柄

core_sysdep_mutex_lock

void core_sysdep_mutex_lock(void *mutex)
  • 接口说明 申请互斥
  • 参数说明
参数数据类型说明
mutexvoid*互斥锁句柄
  • 返回值说明
          void

core_sysdep_mutex_unlock

void core_sysdep_mutex_unlock(void *mutex)
  • 接口说明 释放互斥
  • 参数说明
参数数据类型说明
mutexvoid*互斥锁句柄
  • 返回值说明
          void

core_sysdep_mutex_deinit

void core_sysdep_mutex_deinit(void **mutex)
  • 接口说明 销毁互斥锁
  • 参数说明
参数数据类型说明
mutexvoid**互斥锁句柄
  • 返回值说明
注: •具体的实现参考,可以参考linux和N720的csdk在这块的实现过程,路径stm32_n720_at_mqtt_linkkit_demo\Src\V4-SDK\V4-SDK\portfiles下
stm32_n720_at_mqtt_linkkit_demo.zip
21.4 MB

SDK编程指南

一、端云通信

如上图流程所示, 使用MQTT通信时有5个协议标准操作。
  • 建立连接: MQTT是面向连接的通信协议收发数据前, 设备必须先以云平台认可的用户名和密码登录成功, 对应aiot_mqtt_connect
  • 发布消息: MQTT连接建立后, 设备可向云平台上报消息, 消息的去向需要以某个主题(Topic)表达, 对应aiot_mqtt_pub
  • 订阅消息: MQTT连接建立后, 云平台可向设备下推指令, 而设备是否接收来源于某个主题(Topic)的下推, 以订阅动作向云平台表达, 对应aiot_mqtt_sub
  • 维持连接: MQTT是自动保活的长连接, 需要设备周期性的向云平台发送心跳报文, 对应aiot_mqtt_process, 它同时也重发QoS1的未应答报文。
  • 接收消息: MQTT连接建立, 且主题订阅成功后, 设备可在合适时, 读取云平台在已订阅主题上发给设备的报文, 对应aiot_mqtt_recv

API的使用

1、API列表, 以下是完整的MQTT部分API列表及简要说明 (详见core/aiot_mqtt_api.h)。  
接口名说明
aiot_mqtt_init创建MQTT客户端实例并设置默认参数。
aiot_mqtt_setopt  设置mqtt连接参数, 详见MQTT设置参数说明。  
aiot_mqtt_deinit释放mqtt客户端实例的资源。
aiot_mqtt_connect与mqtt服务器建立连接, 同步接口。
aiot_mqtt_disconnect与mqtt服务器断开连接但保留客户端实例的资源, 同步接口。
aiot_mqtt_heartbeat向mqtt服务器发送PINGREQ报文, 维持连接, 仅在特殊场景使用。
aiot_mqtt_process包含定时心跳发送和QoS1消息的重传, 同步接口, 需要用户周期性调用。
aiot_mqtt_pub发送一条PUBLISH消息到mqtt服务器, 异步接口, 用于设备上报, 消息QoS为0。
aiot_mqtt_sub发送一条SUBSCRIBE消息到mqtt服务器, 异步接口, 用于启动对云端消息的接收。
aiot_mqtt_unsub发送一条UNSUBSCRIBE消息到mqtt服务器, 异步接口, 用于停止对云端消息的接收。
aiot_mqtt_recv尝试收取mqtt报文并分发给用户的订阅回调函数, 同步接口。
2、API使用概述
MQTT模块用于建立与云物联网平台的连接, API使用流程如下:
  1. 调用 aiot_mqtt_init 初始化MQTT会话, 获取会话句柄。
  2. 调用 aiot_mqtt_setopt 配置MQTT会话的参数, 常用配置项见 aiot_mqtt_setopt 的说明。
  3. 调用 aiot_mqtt_connect 建立与云物联网平台的连接。
  4. 启动一个线程, 线程中间歇性调用 aiot_mqtt_process 处理心跳和QoS1的消息。
  5. 启动一个线程, 线程中持续调用 aiot_mqtt_recv 接收网络上的MQTT报文。
当接收到一条报文时, 按以下顺序检查当前MQTT会话的参数, 当满足某条的描述时, 会通过对应的回调函数进行通知, 并停止检查。
  1. 检查收到的报文topic是否已经通过 aiot_mqtt_setopt 的 AIOT_MQTTOPT_APPEND_TOPIC_MAP 参数配置回调函数。
  2. 检查收到的报文topic是否已经通过 aiot_mqtt_sub API配置回调函数。
  3. 检查是否通过 aiot_mqtt_setopt 的 AIOT_MQTTOPT_RECV_HANDLER 参数配置默认回调函数。
经过以上步骤后, MQTT连接已建立并能保持与物联网平台的连接, 接下来按自己的场景用 aiot_mqtt_sub 和 aiot_mqtt_pub 等API实现业务逻辑即可。
3、aiot_mqtt_setopt
    下面列出常用的配置选项, 至少需要配置以下选项才可使用MQTT的基本功能。
    其余配置选项均设有默认值, 可按业务需要进行调整。
  • AIOT_MQTTOPT_HOST: 配置连接的云MQTT站点地址。
  • AIOT_MQTTOPT_PORT: 配置连接的云MQTT站点端口号。
  • AIOT_MQTTOPT_PRODUCT_KEY: 配置设备的 productKey。
  • AIOT_MQTTOPT_DEVICE_NAME: 配置设备的 deviceName。
  • AIOT_MQTTOPT_DEVICE_SECRET: 配置设备的 deviceSecret。
  • AIOT_MQTTOPT_NETWORK_CRED: 配置建立MQTT连接时的安全凭据。
  • AIOT_MQTTOPT_RECV_HANDLER: 配置默认的数据接收回调函数。
  • AIOT_MQTTOPT_EVENT_HANDLER: 配置MQTT事件通知回调函数。
4、aiot_mqtt_connect
  使用aiot_mqtt_setopt配置的mqtt连接参数连接mqtt服务器, 使用的建联参数按如下顺序优先选择。
  若配置了以下选项, 直接用配置的连接参数连接 AIOT_MQTTOPT_HOST 选项指定MQTT服务器的host地址。
  • AIOT_MQTTOPT_USERNAME
  • AIOT_MQTTOPT_PASSWORD
  • AIOT_MQTTOPT_CLIENTID
 若配置了以下选项, 则强制以云平台的签名算法计算连接参数作为MQTT的用户名/密码, 连接云平台。
  • AIOT_MQTTOPT_PRODUCT_KEY
  • AIOT_MQTTOPT_DEVICE_NAME
  • AIOT_MQTTOPT_DEVICE_SECRET
5、aiot_mqtt_disconnect
    向MQTT服务器发送MQTT DISCONNECT报文, 然后断开网络连接。
    如果需要再次与MQTT服务器建立连接, 调用aiot_mqtt_connect 即可。
6、aiot_mqtt_heartbeat
   aiot_mqtt_process 包含了定时发送心跳的机制, 如果有特殊需要的话, 可以使用此函数直接发送心跳报文。  
7、aiot_mqtt_process
  • 发送心跳至mqtt broker以维护mqtt连接, 心跳发送间隔由AIOT_MQTTOPT_HEARTBEAT_INTERVAL_MS 配置项控制。
  • 如果一条qos1的mqtt PUBLISH报文在AIOT_MQTTOPT_REPUB_TIMEOUT_MS 时间内没有收到mqtt PUBACK应答报文, 该函数会重发此消息, 直到成功为止。
8、aiot_mqtt_recv
     除了从网络上接收MQTT报文之外, 本函数也包含了重连机制。
状态码数值说明
STATE_MQTT_CONNACK_RCODE_SERVER_UNAVAILABLE-0x0303MQTT服务器拒绝提供连接, 服务当前不可用。
STATE_MQTT_CONNACK_RCODE_BAD_USERNAME_PASSWORD-0x0304MQTT服务器认为连接时的用户名/密码是非法的。
STATE_MQTT_CONNACK_RCODE_NOT_AUTHORIZED-0x0305MQTT服务器进行连接身份验证失败, 登录密码错误, 一般是设备证书错误。
STATE_PORT_NETWORK_DNS_FAILED-0x0F05TCP域名解析失败, 检查域名或ip是否配置正确。
STATE_PORT_NETWORK_CONNECT_FAILED-0x0F0CTCP建联失败。
STATE_PORT_TLS_INVALID_MAX_FRAGMENT-0x0F0BTLS报文最大长度被设置为0, 是不合理的参数。
STATE_PORT_TLS_INVALID_SERVER_CERT-0x0F14TLS服务端证书配置错误, 检查服务端证书是否正确。
STATE_PORT_TLS_INVALID_CLIENT_CERT-0x0F15TLS设备端证书配置错误, 检查客户端证书是否正确。
STATE_PORT_TLS_INVALID_CLIENT_KEY-0x0F16TLS客户端密钥配置错误, 检查客户端密钥是否正确。
STATE_PORT_TLS_DNS_FAILED-0x0F17TLS域名解析失败, 检查域名或ip是否配置正确。
STATE_PORT_TLS_SOCKET_CREATE_FAILED-0x0F18TLS socket 创建失败。
STATE_PORT_TLS_SOCKET_CONNECT_FAILED-0x0F19TLS socket 连接失败。
STATE_PORT_TLS_INVALID_RECORD-0x0F1ASSL收到的数据包出错, 一般是由于tls fragment过小。

连接状态监控

使用函数aiot_mqtt_setopt()注册一个事件监控函数,对事件进行处理,并列出可以监控的事件。具体实现可参见如下:
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */ 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\n"); /* TODO: 处理SDK建连成功, 不可以在这里调用耗时较长的阻塞函数 */ } break; /* SDK因为网络状况被动断连后, 自动发起重连已成功 */ case AIOT_MQTTEVT_RECONNECT: { printf("AIOT_MQTTEVT_RECONNECT\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\n", cause); /* TODO: 处理SDK被动断连, 不可以在这里调用耗时较长的阻塞函数 */ } break; default: { } } } /* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */ 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\n"); /* TODO: 处理服务器对心跳的回应, 一般不处理 */ } break; case AIOT_MQTTRECV_SUB_ACK: { printf("suback, res: -0x%04X, packet id: %d, max qos: %d\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\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic); printf("pub, payload: %.*s\n", packet->data.pub.payload_len, packet->data.pub.payload); /* TODO: 处理服务器下发的业务报文 */ } break; case AIOT_MQTTRECV_PUB_ACK: { printf("puback, packet id: %d\n", packet->data.pub_ack.packet_id); /* TODO: 处理服务器对QoS1上报消息的回应, 一般不处理 */ } break; default: { } } }

重连机制

连接因为网络原因断开时, SDK内含了自动重连的功能, 这些重连的动作是在aiot_mqtt_recv()接口中完成的。
SDK周期性向服务器发送心跳报文保活, 正常情况下服务器会应答这些心跳, 而心跳如果达到一定次数未被应答, SDK将会发起自动重连。
  • 可容忍的心跳丢失阈值由AIOT_MQTTOPT_HEARTBEAT_MAX_LOST配置的次数决定, 配置接口是aiot_mqtt_setopt
  • 自动重连启动后, 会反复重试直到某次成功, 重连的间隔由AIOT_MQTTOPT_RECONN_INTERVAL_MS选项配置, 配置接口是aiot_mqtt_setopt
SDK调用底层依赖函数, 从协议栈读或写的时候发现连接已断开, 也会触发重连。重连操作请参见如下:
/* 执行aiot_mqtt_recv的线程, 包含网络自动重连和从服务器收取MQTT消息 */ void *demo_mqtt_recv_thread(void *args) { int32_t res = STATE_SUCCESS; while (g_mqtt_recv_thread_running) { res = aiot_mqtt_recv(args); if (res < STATE_SUCCESS) { if (res == STATE_USER_INPUT_EXEC_DISABLED) { break; } sleep(1); } } return NULL; }

获取离线推送消息

MQTT协议中, 设备一般是先建立连接, 再向云平台订阅某个Topic, 之后用户服务器可通过云平台, 向这个Topic下发指令, 即可达到设备。
  1. 如果用户服务器以QoS1等级向设备下发指令时, 设备不在线(未连接到云平台), 当次的指令下发就无法到达设备。
  2. 之后设备再上线连接到云平台的时, 若指定了cleanSession连接参数为0, 云平台会重发之前未到达设备的QoS1消息。
  3. 设备如果是使用的C-SDK连接云平台, SDK收到消息后会进入用户以AIOT_MQTTOPT_RECV_HANDLER选项设置的默认回调函数。
另外, 如果用户希望设备上线时能够接收到离线期间的云端QoS1消息, 并且不在默认数据接收回调中处理这些离线消息, 可以这样编写代码。
/* MQTT离线消息处理回调, 可注册这个回调给SDK, 当SDK从服务器收到离线MQTT消息时进入 */ void demo_mqtt_offline_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata) { if (AIOT_MQTTRECV_PUB != packet->type) { return; } /* TODO: 根据packet参数中的topic和payload编写业务逻辑 */ printf("pub, qos: %d, topic: %.*s\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic); printf("pub, payload: %.*s\n", packet->data.pub.payload_len, packet->data.pub.payload); } int main(int argc, char *argv[]) { ... ... /* 配置SDK的底层依赖 */ aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); /* 配置SDK的日志输出 */ aiot_state_set_logcb(demo_state_logcb); /* 创建1个MQTT客户端实例并内部初始化默认参数 */ mqtt_handle = aiot_mqtt_init(); if (mqtt_handle == NULL) { printf("aiot_mqtt_init failed\n"); return -1; } /* 准备一个aiot_mqtt_topic_map_t结构体, 描述topic和回调函数的对应关系 */ /* TODO: 实际使用时, 请替换sub_topic和topic_map.handler, userdata可酌情使用, SDK调用回调时会传回来 */ char * sub_topic = "/sys/a13FN5TplKq/mqtt_basic_demo/user/get"; aiot_mqtt_topic_map_t topic_map = { .topic = sub_topic, .handler = demo_mqtt_offline_recv_handler, .userdata = NULL }; /* 用AIOT_MQTTOPT_APPEND_TOPIC_MAP选项, 可在连接前设置topic和回调函数之间的对应关系 */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_APPEND_TOPIC_MAP, (void *)&topic_map); ... ... /* 设置连接MQTT时, cleanSession参数为0, SDK会向服务器表达接收离线期间的QoS1消息 */ uint8_t clean = 0; aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_CLEAN_SESSION, (void *)&clean); /* 与服务器建立MQTT连接 */ res = aiot_mqtt_connect(mqtt_handle); if (res < STATE_SUCCESS) { /* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */ aiot_mqtt_deinit(&mqtt_handle); printf("aiot_mqtt_connect failed: -0x%04X\n", -res); return -1; } /* 这时建连成功, 在 /sys/a13FN5TplKq/mqtt_basic_demo/user/get 上到达的离线QoS1消息就会触发 demo_mqtt_offline_recv_handler */ ... }

例程讲解

现对照demos/mqtt_basic_demo.c例程, 分步骤讲解如何使用API。
本示例适用于支持POSIX线程的Linux设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建2个线程。
  • 第一个线程用于保活长连接。
  • 第二个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调。
需要用户关注或修改的部分
  • 设置设备的设备证书
例程使用的设备证书是公用的, 所以应用于实际业务时, 请替换如下的TODO部分, 传入用户自己真实的设备证书。
/* TODO: 替换为自己设备的设备证书 */ char *product_key = "xxxxxx"; char *device_name = "xxxxxx"; char *device_secret = "xxxxxx";
  • 进入程序入口, 给SDK配置全局的底层依赖和日志回调
底层依赖描述了硬件平台的资源使用方式, 比如怎样获取时钟, 分配内存等, 日志回调是用户的函数, SDK有log输出的时候会进入这个函数。
int main(int argc, char *argv[]) { ... ... /* 配置SDK的底层依赖 */ aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); /* 配置SDK的日志输出 */ aiot_state_set_logcb(demo_state_logcb);
  • 给MQTT连接配置连接参数
这些连接参数包括如何建立TLS连接, 服务器地址在哪, 连接时的设备证书是什么, 连接后的数据及事件回调函数是什么等等。
/* 创建1个MQTT客户端实例并内部初始化默认参数 */ mqtt_handle = aiot_mqtt_init(); if (mqtt_handle == NULL) { printf("aiot_mqtt_init failed\n"); return -1; } snprintf(host, 100, "%s.%s", product_key, url); /* 配置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);
  • 向服务器发起连接
  使用aiot_mqtt_connect()接口, 以上面设置的连接参数, 向指定的MQTT服务器发起连接请求。  
/* 与服务器建立MQTT连接 */ res = aiot_mqtt_connect(mqtt_handle); if (res < STATE_SUCCESS) { /* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */ aiot_mqtt_deinit(&mqtt_handle); printf("aiot_mqtt_connect failed: -0x%04X\n", -res); return -1; }
  • 建连成功后, 开启专用的保活线程
  保活线程每到心跳周期会在aiot_mqtt_process中向服务器发送心跳报文, 保持长连接不断, 服务器就可以向设备下推消息。
/* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */ res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle); if (res < 0) { printf("pthread_create demo_mqtt_process_thread failed: %d\n", res); return -1; }
  • 建连成功后, 开启专用的接收线程
    接收线程在连接正常时, 读取服务器下推的MQTT消息, 并交给用户的demo_mqtt_event_handler处理, 连接异常时, 则等待aiot_mqtt_recv()自动重连。  
/* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */ res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle); if (res < 0) { printf("pthread_create demo_mqtt_recv_thread failed: %d\n", res); return -1; }
  • 云端下推消息到达时, 用户需要编写处理逻辑
    demo_mqtt_event_handler是用户的函数, 实际使用时, 用户需要修改的部分已在注释中用TODO标明。
  • 主要对应业务消息, 要处理AIOT_MQTTRECV_PUB, 可根据消息的TopicPayload编写处理逻辑
  • 例程中对业务消息, 仅做了打印示意, 用户使用时需自行填写, 但注意处理逻辑不要耗时太久, 否则收包线程会被阻塞。
/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */ void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *const packet, void *userdata) { switch (packet->type) { ... case AIOT_MQTTRECV_PUB: { printf("pub, qos: %d, topic: %.*s\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic); printf("pub, payload: %.*s\n", packet->data.pub.payload_len, packet->data.pub.payload); /* TODO: 处理服务器下发的业务报文 */ } break; ... }
       例程会出现如下打印, 就是在demo_mqtt_default_recv_handler中发生的, 其中不以pub开头的行是日志回调demo_state_logcb中打印。  
[1577603312.688][LK-0309] pub: /sys/a13FN5XXXX/mqtt_basic_demo/thing/service/property/set [LK-030A] < 7B 22 6D 65 74 68 6F 64 22 3A 22 74 68 69 6E 67 | {"method":"thing [LK-030A] < 2E 73 65 72 76 69 63 65 2E 70 72 6F 70 65 72 74 | .service.propert [LK-030A] < 79 2E 73 65 74 22 2C 22 69 64 22 3A 22 31 31 36 | y.set","id":"116 [LK-030A] < 33 39 34 30 30 37 34 22 2C 22 70 61 72 61 6D 73 | 39XXXXX","params [LK-030A] < 22 3A 7B 22 4C 69 67 68 74 53 77 69 74 63 68 22 | ":{"LightSwitch" [LK-030A] < 3A 30 7D 2C 22 76 65 72 73 69 6F 6E 22 3A 22 31 | :0},"version":"1 [LK-030A] < 2E 30 2E 30 22 7D | .0.0"} pub, qos: 0, topic: /sys/a13FN5XXXX/mqtt_basic_demo/thing/service/property/set pub, payload: {"method":"thing.service.property.set","id":"11639XXXXX","params":{"LightSwitch":0},"version":"1.0.0"}
  • 连接状态变化时, 用户可在事件回调中得知
  连接状态的变化包括SDK检测到网络异常, 自动重连已成功, 或已断开连接等, 若要处理, 可在标明处修改代码。
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */ void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *const event, void *userdata) { switch (event->type) { /* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */ case AIOT_MQTTEVT_CONNECT: { printf("AIOT_MQTTEVT_CONNECT\n"); /* TODO: 处理SDK建连成功, 不可以在这里调用耗时较长的阻塞函数 */ } break; /* SDK因为网络状况被动断连后, 自动发起重连已成功 */ case AIOT_MQTTEVT_RECONNECT: { printf("AIOT_MQTTEVT_RECONNECT\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\n", cause); /* TODO: 处理SDK被动断连, 不可以在这里调用耗时较长的阻塞函数 */ } break; default: { } } }
  • 主循环进入休眠
主线程的任务就是配置连接参数, 建立连接成功, 之后的业务循环主要由接收线程工作完成, 因此接收线程运行后, 主线程可进入休眠。
/* 主循环进入休眠 */ while (1) { sleep(1); }

二、物模型编程

本文为您介绍了如何在设备端完成物模型的开发,从而可以使设备端上报属性和事件,云端可以向设备端发送设置属性和调用服务的指令。

背景说明

云物联网平台定义了一套设备功能建模的方法, 使用属性/事件/服务三个维度来管理设备:
概念解释
属性表示设备支持的某个可读、或者可读可写的参数,例如灯的亮度。
服务表示一个设备支持的功能,例如让一个设备重启。
事件是设备主动上报的某种情况发生的信息,例如有人在撬锁时智能锁可以上报一个暴力撬锁的事件到云端进行报警。
     如上图流程说明, 设备从MQTT长连接通道可与云平台进行属性/服务的双向通信, 以及单向的事件上报, 从而让云平台理解和控制设备的运行。

限制说明

     设备调用aiot_dm_send向云端上报消息太过频繁时, 会被云端限流, 云端限流发生时, 设备端不会收到任何来自网络的通知, 只能人工在控制台页面查看。

数据结构

SDK中的data-model功能将属性/服务/事件对应的消息内容都以结构体描述。
  • 所有上行消息的传递, 都以用户调用aiot_dm_send()接口, 从设备发往云平台。
  • 所有上行消息的内容, 都以 aiot_dm_msg_t 结构体存放, 它是aiot_dm_send()接口的入参。
  • 所有下行消息的传递, 都以用户调用aiot_mqtt_recv()接口, 进而触发用户注册的 aiot_dm_recv_handler_t 回调, 到达用户侧。
  • 所有下行消息的内容, 都以 aiot_dm_recv_t 结构体存放, 它是上述回调函数的入参。
SDK中的物模型模块不维护网络会话, 网络连接和数据收发由它所依赖MQTT模块提供。
因此, 用户在创建物模型实例后, 应正确地为它配置MQTT实例句柄, 在进行物模型数据收发前, 也应确保MQTT已经建连成功。

API列表

以下是API列表及简要说明 (详见components/data-model/aiot_dm_api.h)。
接口名说明
aiot_dm_init初始化data-model会话实例。
aiot_dm_setopt 设置data-model会话参数, 点击物模型详细设置参数了解所有参数。  
aiot_dm_send发送一条data-model消息到物联网平台, 消息类型和消息内容由msg参数决定。
aiot_dm_deinit销毁data-model实例, 释放资源。

API使用概述

请按照以下流程使用API,参考demos/data_model_basic_demo.c
  1. 在使用物模型模块前, 用户应首先创建好一个MQTT实例.
  2. 调用aiot_dm_init创建一个物模型实例, 保存实例句柄。
  3. 调用aiot_dm_setopt配置AIOT_DMOPT_MQTT_HANDLE选项以设置MQTT句柄, 此选项为强制配置选项。
  4. 调用aiot_dm_setopt配置AIOT_DMOPT_PRODUCT_KEYAIOT_DMOPT_DEVICE_NAME选项以设置默认productKey和deviceName。
  5. 调用aiot_dm_setopt配置AIOT_DMOPT_RECV_HANDLERAIOT_DMOPT_USER_DATA选项以注册数据接受回调函数和用户上下文数据指针。
  6. 在使用aiot_dm_send发送消息前, 应先完成MQTT实例的建连。
  7. 调用aiot_dm_send发送不同类型的消息到云平台, 在注册的回调函数中处理各种类型的云平台下行消息。
data-model模块通过调用aiot_dm_send发送消息到云平台, 云平台下发给设备的消息通过调用aiot_dm_setopt注册的回调函数接受。
在讲解如何使用API进行数据收发前, 我们先介绍Alink JSON数据格式, 它分为请求和应答两种格式, 定义如下:
Alink请求:
{ "id": "123", /* msgid, 一般为一串数字的字符串 */ "version": "1.0", "params": /* 用户数据, JSON对象 */, "method": "请求方法字符串" }
Alink应答:  
{ "id": "123", /* msgid, 一般为一串数字的字符串 */ "code": 200, "data": /* 用户数据, JSON对象 */ }
其中paramsdata的value均为JSON对象, 这两个JSON对象都是需要用户进行组装或解析的, 用户可以使用cJSON等第三方库完成JSON对象的组装和解析。
以下列举了上报不同数据类型的属性时params的定义。
/* 整型数据 */ char *params = "{\"Brightness\":50}"; /* 浮点型数据上报 */ char *params = "{\"Temperature\":11.11}"; /* 枚举型数据上报 */ char *params = "{\"WorkMode\":2}"; /* 布尔型数据上报, 在物模型定义中, 布尔型为整型, 取值为0或1, 与JSON格式的整型不同 */ char *params = "{\"LightSwitch\":1}"; /* 字符串数据上报 */ char *params = "{\"Description\":\"Amazing Example\"}"; /* 时间型数据上报, 在物模型定义中, 时间型为整数字符串 */ char *params = "{\"Timestamp\":\"1252512000\"}"; /* 复合类型属性上报, 在物模型定义中, 符合类型属性为JSON对象 */ char *params = "{\"RGBColor:{\"Red\":11,\"Green\":22,\"Blue\":33}\"}"; /* 多属性上报, 如果需要上报以上各种数据类型的所有属性, 将它们放在一个JSON对象中即可 */ char *params = "{\"Brightness\":50,\"Temperature\":11.11," "\"WorkMode\":2,\"LightSwitch\":1,\"Description\":\"Amazing Example\"," "\"Timestamp\":\"1252512000\"," "\"RGBColor:{\"Red\":11,\"Green\":22,\"Blue\":33}\"}";
目前data-model模块支持的消息类型主要有以下几种:
  • 属性上报请求和云平台的应答
  • 事件上报请求和云平台的应答
  • 属性设置请求和设备发送应答
  • 异步服务请求和应答
  • 同步服务请求和应答
  • 二进制数据上下行
  • 二进制同步服务上下行
  • 属性上报请求和云平台的应答
   以下代码演示了如何用aiot_dm_send发送属性上报请求, 其中params参数为包含属性键值对的JSON对象字符串, 如例程中的"{\"LightSwitch\": 0}"
int32_t demo_send_property_post(void *dm_handle, char *params) { aiot_dm_msg_t msg; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_PROPERTY_POST; msg.data.property_post.params = params; /* 发送成功将返回消息的msg_id */ return aiot_dm_send(dm_handle, &msg); } /* 演示上报灯开关属性 */ demo_send_property_post(dm_handle, "{\"LightSwitch\": 0}");
     在成功上报属性后, 设备将收到云端下发的应答消息, 消息类型为AIOT_DMRECV_GENERIC_REPLY, 应答消息的msg_id与对应请求的msg_id一致。  
void aiot_dm_recv_handler(void *dm_handle, const aiot_dm_recv_t *recv, void *user_data) { switch (recv->type) { /* 属性上报或事件上报应答 */ case AIOT_DMRECV_GENERIC_REPLY: { printf("msg_id = %d, code = %d\r\n", recv->data.generic_reply.msg_id, recv->data.generic_reply.code); } break; ... } }
  • 事件上报请求和云平台的应答
  以下代码演示了如何使用aiot_dm_send发送事件上报请求, 其中params参数为包含属性键值对的JSON对象字符串, 如例程中的"{\"ErrorCode\": 0}"。  
int32_t demo_send_event_post(void *dm_handle, char *event_id, char *params) { aiot_dm_msg_t msg; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_EVENT_POST; msg.data.event_post.event_id = event_id; msg.data.event_post.params = params; return aiot_dm_send(dm_handle, &msg); } /* 演示上报event_id为"Error"的错误事件上报 */ demo_send_event_post(dm_handle, "Error", "{\"ErrorCode\": 0}");
  在成功上报事件后, 设备将收到云端下发的应答消息, 消息类型为AIOT_DMRECV_GENERIC_REPLY, 应答消息的msg_id与对应请求的msg_id一致。  
void aiot_dm_recv_handler(void *dm_handle, const aiot_dm_recv_t *recv, void *user_data) { switch (recv->type) { /* 属性上报或事件上报应答 */ case AIOT_DMRECV_GENERIC_REPLY: { printf("msg_id = %d, code = %d\r\n", recv->data.generic_reply.msg_id, recv->data.generic_reply.code); } break; ... ... } }
  • 属性设置请求和设备发送应答
      来自云平台的属性设置请求将通过AIOT_DMRECV_PROPERTY_SET类型消息通知给用户, 其中params参数为包含属性键值对的JSON对象字符串。
用户可使用第三方JSON库对其进行解析。
用户可调用aiot_dm_send发送AIOT_DMMSG_PROPERTY_SET_REPLY类型消息进行应答。
/* 用户数据接收处理回调函数 */ void aiot_dm_recv_handler(void *dm_handle, const aiot_dm_recv_t *recv, void *user_data) { printf("aiot_dm_recv_handler, type = %d\r\n", recv->type); switch (recv->type) { /* 属性设置 */ case AIOT_DMRECV_PROPERTY_SET: { printf("msg_id = %ld, params = %.*s\r\n", (unsigned long)recv->data.property_set.msg_id, recv->data.property_set.params_len, recv->data.property_set.params); /* TODO: 以下代码演示如何对来自云平台的属性设置指令进行应答, 用户可取消注释查看演示效果 */ /* { aiot_dm_msg_t msg; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_PROPERTY_SET_REPLY; msg.data.property_set_reply.msg_id = recv->data.property_set.msg_id; msg.data.property_set_reply.code = 200; msg.data.property_set_reply.data = "{}"; int32_t res = aiot_dm_send(dm_handle, &msg); if (res < 0) { printf("aiot_dm_send failed\r\n"); } } */ } break;
  • 异步服务请求和应答
      来自云平台的异步服务调用请求将通过AIOT_DMRECV_ASYNC_SERVICE_INVOKE类型消息通知给用户, 其中params参数为包含服务输入数据键值对的JSON对象字符串。
用户可使用第三方JSON库对其进行解析。
用户可调用aiot_dm_send搭配AIOT_DMMSG_ASYNC_SERVICE_REPLY类型, 发送应答消息给云端。
/* 异步服务调用 */ case AIOT_DMRECV_ASYNC_SERVICE_INVOKE: { printf("msg_id = %ld, service_id = %s, params = %.*s\r\n", (unsigned long)recv->data.async_service_invoke.msg_id, recv->data.async_service_invoke.service_id, recv->data.async_service_invoke.params_len, recv->data.async_service_invoke.params); /* TODO: 以下代码演示如何对来自云平台的异步服务调用进行应答, 用户可取消注释查看演示效果 * * 注意: 如果用户在回调函数外进行应答, 需要自行保存msg_id, 因为回调函数入参在退出回调函数后将被SDK销毁, 不可以再访问到 */ /* { aiot_dm_msg_t msg; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_ASYNC_SERVICE_REPLY; msg.data.async_service_reply.msg_id = recv->data.async_service_invoke.msg_id; msg.data.async_service_reply.code = 200; msg.data.async_service_reply.service_id = "ToggleLightSwitch"; msg.data.async_service_reply.data = "{\"dataA\": 20}"; int32_t res = aiot_dm_send(dm_handle, &msg); if (res < 0) { printf("aiot_dm_send failed\r\n"); } } */ } break;
    如果用户不在回调函数中立即应答, 而是在回调函数外完成业务处理后再发送应答, 则需自行保存服务msg_id等信息, 以保证应答数据在云端能与请求相匹配。
  • 同步服务请求和应答
     来自云平台的同步服务调用请求将通过AIOT_DMRECV_SYNC_SERVICE_INVOKE类型消息通知给用户, 其中params参数为包含服务输入数据键值对的JSON对象字符串。
用户可使用第三方JSON库对其进行解析。
用户可调用aiot_dm_send搭配AIOT_DMMSG_SYNC_SERVICE_REPLY类型, 发送应答消息给云端。
/* 同步服务调用 */ case AIOT_DMRECV_SYNC_SERVICE_INVOKE: { printf("msg_id = %ld, rrpc_id = %s, service_id = %s, params = %.*s\r\n", (unsigned long)recv->data.sync_service_invoke.msg_id, recv->data.sync_service_invoke.rrpc_id, recv->data.sync_service_invoke.service_id, recv->data.sync_service_invoke.params_len, recv->data.sync_service_invoke.params); /* TODO: 以下代码演示如何对来自云平台的同步服务调用进行应答, 用户可取消注释查看演示效果 * * 注意: 如果用户在回调函数外进行应答, 需要自行保存msg_id和rrpc_id字符串, 因为回调函数入参在退出回调函数后将被SDK销毁, 不可以再访问到 */ /* { aiot_dm_msg_t msg; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_SYNC_SERVICE_REPLY; msg.data.sync_service_reply.rrpc_id = recv->data.sync_service_invoke.rrpc_id; msg.data.sync_service_reply.msg_id = recv->data.sync_service_invoke.msg_id; msg.data.sync_service_reply.code = 200; msg.data.sync_service_reply.service_id = "SetLightSwitchTimer"; msg.data.sync_service_reply.data = "{}"; int32_t res = aiot_dm_send(dm_handle, &msg); if (res < 0) { printf("aiot_dm_send failed\r\n"); } } */ } break;
说明:
  • 同步服务调用, 在云端的超时时间为7秒, 如果设备不在超时时间内完成应答, 云端API接口调用将返回超时失败。
  • 如果用户不在回调函数中立即应答, 而是在回调函数外完成业务处理后再应答, 则需要自行保存服务rrpc_id(用字符串拷贝), msg_id等信息。需要保证应答中含有与请求相同的rrpc_idmsg_id, 使云端能匹配应答与请求。
  • 二进制数据上下行
      来自云平台的下行二进制数据将通过AIOT_DMRECV_RAW_DATA类型消息通知给用户。 用户可调用aiot_dm_send发送AIOT_DMMSG_RAW_DATA类型消息进行应答。
在使用二进制数据进行通信前, 用户应确保设备所属产品使用的数据格式为透传/自定义格式, 并已经正确的在云端部署了数据解析脚本。
/* 下行二进制数据 */ case AIOT_DMRECV_RAW_DATA: { printf("raw data len = %d\r\n", recv->data.raw_data.data_len); /* TODO: 以下代码演示如何发送二进制格式数据, 若使用需要有相应的数据透传脚本部署在云端 */ /* { aiot_dm_msg_t msg; uint8_t raw_data[] = {0x01, 0x02}; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_RAW_DATA; msg.data.raw_data.data = raw_data; msg.data.raw_data.data_len = sizeof(raw_data); aiot_dm_send(dm_handle, &msg); } */ } break;
  • 二进制同步服务上下行
      来自云平台的二进制格式的同步服务调用将通过AIOT_DMRECV_RAW_SYNC_SERVICE_INVOKE类型消息通知给用户。
用户可调用aiot_dm_send搭配AIOT_DMMSG_RAW_SERVICE_REPLY类型, 发送应答消息给云端。
在使用二进制数据进行通信前, 用户应确保设备所属产品使用的数据格式为透传/自定义格式, 并已经正确部署了数据解析脚本。
/* 二进制格式的同步服务调用, 比单纯的二进制数据消息多了个rrpc_id */ case AIOT_DMRECV_RAW_SYNC_SERVICE_INVOKE: { printf("raw sync service rrpc_id = %s, data_len = %d\r\n", recv->data.raw_service_invoke.rrpc_id, recv->data.raw_service_invoke.data_len); } break;
     二进制同步服务调用, 比普通的AIOT_DMRECV_RAW_DATA类型数据多了rrpc_id字符串, 用户的应答响应数据必要带上相同的rrpc_id字符串。  

例程讲解

现对照demos/data_model_basic_demo.c例程, 分步骤讲解如何使用API,细节信息请参见下图:
这个例程适用于Linux这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建2个线程。
  • 一个线程用于保活长连接。
  • 一个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调。
接着演示了在MQTT连接上进行属性上报, 事件上报, 以及处理收到的属性设置, 服务调用, 取消这些代码段落的注释即可观察运行效果。
需要用户关注或修改的部分, 已经用 TODO 在注释中标明。
1、设置设备的设备证书
例程使用的设备证书是公用的, 所以应用于实际业务时, 请替换如下的TODO部分, 传入用户自己真实的设备证书。
/* TODO: 替换为自己设备的设备证书 */ char *product_key = "a18wPXXXXXX"; char *device_name = "data_model_basic_demo"; char *device_secret = "uwMTmVAMnGGHaAkqmeDY6cHxxBXXXXXX";
2、进入程序入口, 给SDK配置全局的底层依赖和日志回调
    底层依赖描述了硬件平台的资源使用方式, 比如怎样获取时钟, 分配内存等, 日志回调是用户的函数, SDK有log输出的时候会进入这个函数。
int main(int argc, char *argv[]) { ... ... /* 配置SDK的底层依赖 */ aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); /* 配置SDK的日志输出 */ aiot_state_set_logcb(demo_state_logcb);
3、给MQTT会话配置参数
   这些连接参数包括如何建立TLS连接, 服务器地址在哪, 连接后的数据及事件回调函数是什么等等, 然后用 aiot_mqtt_connect() 建立连接。
/* 创建1个MQTT客户端实例并内部初始化默认参数 */ mqtt_handle = aiot_mqtt_init(); if (mqtt_handle == NULL) { printf("aiot_mqtt_init failed\n"); return -1; } snprintf(host, 100, "%s.%s", product_key, url); /* 配置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_EVENT_HANDLER, (void *)demo_mqtt_event_handler);
4、创建物模型会话实例并建立MQTT连接
   物模型会话创建后, 可通过其会话句柄组装和解析标准的物模型报文, 这些报文依赖MQTT模块发送和接收, 因此要关联物模型的会话和MQTT的会话, 并建立MQTT连接。
/* 创建DATA-MODEL实例 */ void *dm_handle = aiot_dm_init(); if (dm_handle == NULL) { printf("aiot_dm_init failed"); return -1; } /* 配置MQTT实例句柄 */ aiot_dm_setopt(dm_handle, AIOT_DMOPT_MQTT_HANDLE, mqtt_handle); /* 配置设备productKey */ aiot_dm_setopt(dm_handle, AIOT_DMOPT_PRODUCT_KEY, (void *)product_key); /* 配置设备deviceName */ aiot_dm_setopt(dm_handle, AIOT_DMOPT_DEVICE_NAME, (void *)device_name); /* 配置消息接收处理回调函数 */ aiot_dm_setopt(dm_handle, AIOT_DMOPT_RECV_HANDLER, (void *)aiot_dm_recv_handler); /* 与服务器建立MQTT连接 */ res = aiot_mqtt_connect(mqtt_handle);
5、数据收发
请查看API使用概述部分。
6、其他说明
例程使用的物模型如下所示, 用户可按照以下json定义产品物模型以完成demo的演示。
{ "properties": [ { "identifier": "LightSwitch", "dataType": { "type": "bool" } } ], "events": [ { "outputData": [ { "identifier": "ErrorCode", "dataType": { "type": "enum" } } ], "identifier": "Error", "type": "info" } ], "services": [ { "identifier": "SetLightSwitchTimer", "inputData": [ { "identifier": "Timer", "dataType": { "type": "double" } }, { "identifier": "LightSwitch", "dataType": { "type": "bool" } } ] }, { "outputData": [ { "identifier": "dataA", "dataType": { "type": "int" } } ], "identifier": "ToggleLightSwitch", "inputData": [ { "identifier": "paramA", "dataType": { "type": "int" } } ] } ] }
7、观察日志
以上各种和云端的交互代码都被注释掉, 用户取消注释后, 重新编译运行例程, 即可看到运行过程, 以属性上报和事件上报为例。
[1581401572.800][LK-0313] MQTT user calls aiot_mqtt_connect api, connect [1581401572.800][LK-0317] data_model_basic_demo&a18wPXXXXXX [1581401572.800][LK-0318] DCDDD8568269B74AB67BDD3BC9B854E4F45FD94649750362134F744979C59684 [1581401572.800][LK-0319] a18wPXXXXXX.data_model_basic_demo|timestamp=2524608000000,_ss=1,_v=sdk-c-4.0.0,securemode=2,signmethod=hmacsha256,ext=1,| establish mbedtls connection with server(host='a18wPXXXXXX.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) [1581401572.877][LK-0313] MQTT connect success in 75 ms AIOT_MQTTEVT_CONNECT [1581401572.877][LK-0309] pub: /sys/a18wPXXXXXX/data_model_basic_demo/thing/event/property/post [LK-030A] > 7B 22 69 64 22 3A 22 30 22 2C 22 76 65 72 73 69 | {"id":"0","versi [LK-030A] > 6F 6E 22 3A 22 31 2E 30 22 2C 22 70 61 72 61 6D | on":"1.0","param [LK-030A] > 73 22 3A 7B 22 4C 69 67 68 74 53 77 69 74 63 68 | s":{"LightSwitch [LK-030A] > 22 3A 20 30 7D 7D | ": 0}} [1581401572.877][LK-0309] pub: /sys/a18wPXXXXXX/data_model_basic_demo/thing/event/Error/post [LK-030A] > 7B 22 69 64 22 3A 22 31 22 2C 22 76 65 72 73 69 | {"id":"1","versi [LK-030A] > 6F 6E 22 3A 22 31 2E 30 22 2C 22 70 61 72 61 6D | on":"1.0","param [LK-030A] > 73 22 3A 7B 22 45 72 72 6F 72 43 6F 64 65 22 3A | s":{"ErrorCode": [LK-030A] > 20 30 7D 7D | 0}} [1581401572.911][LK-0309] pub: /sys/a18wPXXXXXX/data_model_basic_demo/thing/event/Error/post_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 7D 2C 22 69 64 22 3A 22 31 22 2C 22 | a":{},"id":"1"," [LK-030A] < 6D 65 73 73 61 67 65 22 3A 22 73 75 63 63 65 73 | message":"succes [LK-030A] < 73 22 2C 22 6D 65 74 68 6F 64 22 3A 22 74 68 69 | s","method":"thi [LK-030A] < 6E 67 2E 65 76 65 6E 74 2E 45 72 72 6F 72 2E 70 | ng.event.Error.p [LK-030A] < 6F 73 74 22 2C 22 76 65 72 73 69 6F 6E 22 3A 22 | ost","version":" [LK-030A] < 31 2E 30 22 7D | 1.0"} [1581401572.911][LK-0A08] DM recv generic reply aiot_dm_recv_handler, type = 0 msg_id = 1, code = 200 [1581401572.911][LK-0309] pub: /sys/a18wPXXXXXX/data_model_basic_demo/thing/event/property/post_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 7D 2C 22 69 64 22 3A 22 30 22 2C 22 | a":{},"id":"0"," [LK-030A] < 6D 65 73 73 61 67 65 22 3A 22 73 75 63 63 65 73 | message":"succes [LK-030A] < 73 22 2C 22 6D 65 74 68 6F 64 22 3A 22 74 68 69 | s","method":"thi [LK-030A] < 6E 67 2E 65 76 65 6E 74 2E 70 72 6F 70 65 72 74 | ng.event.propert [LK-030A] < 79 2E 70 6F 73 74 22 2C 22 76 65 72 73 69 6F 6E | y.post","version [LK-030A] < 22 3A 22 31 2E 30 22 7D | ":"1.0"} [1581401572.911][LK-0A08] DM recv generic reply aiot_dm_recv_handler, type = 0 msg_id = 0, code = 200

三、设备OTA

     本文介绍设备如何调用SDK完成对上传至物联网平台固件的下载,进而完成设备厂商对设备的固件升级计划。

背景说明

     物联网平台提供固件升级与管理服务。若设备集成了C-SDK提供的OTA能力,用户可以在物联网平台的控制台上传新的固件,并通过SDK将固件下载到设备上,然后由设备厂商完成设备的固件升级。SDK提供API下载固件,如何将固件写入设备的Flash完成固件升级需要厂商实现。
下面是使用SDK进行OTA的示意图:

API列表

    OTA升级包括两部分的内容, 通过MQTT通道接收到固件URL, 及通过HTTP通道下载固件内容的两部分操作,声明部分请参见components/ota/aiot_ota_api.h。  
  • 固件信息推送
     这部分API主要基于MQTT连接工作,用于完成设备接收云端下推的固件描述信息,比如下载地址。
接口名说明
aiot_ota_init创建ota客户端实例并设置默认参数
aiot_ota_deinit销毁ota客户端实例, 回收资源。
aiot_ota_report_version用MQTT报文向云平台上报自身设备的固件版本号, 这是云端判断升级是否启动和成功的依据。
aiot_ota_report_version_ext用MQTT报文向云平台上报指定设备的固件版本号, 用于网关代理子设备上报的场景。
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选项设置的用户回调, 将下载内容传给用户。  
注意 SDK收到云端推送的固件URL后并不会自动进行固件下载,用户需要调用SDK提供的API启动固件下载。

API使用概述

OTA模块可用于配合云平台的固件升级服务。
固件升级流程如下:
  1. 设备要使用OTA服务, 必须先以aiot_ota_report_version 向云端上报当前的固件版本号,否则无法接收到固件信息推送。
  2. 设备下载固件时, 设备自行用aiot_download_send_request 从云端获取,SDK不会自动下载固件。
  3. 设备下载中或者下载完成后, 设备需自行将内存buffer中的固件内容写到Flash上,SDK不含有固件烧录的逻辑。
  4. 设备升级完成后, 需以aiot_ota_report_version 接口, 向云端上报升级后的新固件版本号,否则云端不会认为升级完成。
注意 用户在控制台推送固件的下载地址等信息给设备时, 是通过MQTT通道, 所以设备OTA升级的前提是成功建立并保持MQTT长连接通道在线。
下面介绍如何API的使用。
  • aiot_ota_report_version
     如果云端不知道某台设备当前的固件版本号, 就不会为其提供OTA服务,而如果不知道设备的新版本号, 也不会认为它升级成功。
所以OTA要正常工作,可以当设备连接到物联网平台之后调用这个接口,将当前设备上运行的固件版本号字符串上报给云端,如果用户在物联网平台上传了新的固件,平台将会告诉设备端新固件的下载URL以及固件的相关信息。
参数的handle通过aiot_ota_init 得到,比如,上报当前版本号为"1.0.0"的代码写作。
handle = aiot_ota_init(); ... aiot_ota_report_version(handle, "1.0.0");
  • aiot_ota_setopt
    对OTA会话进行配置,常见的配置选项包括。
     AIOT_OTAOPT_MQTT_HANDLE: 把 aiot_mqtt_init 返回的MQTT会话句柄跟OTA会话关联起来。       
     AIOT_OTAOPT_RECV_HANDLER: 设置OTA消息的数据处理回调, 这个用户回调在有OTA消息的时候, 会被 aiot_mqtt_recv 调用到。
  • 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_report_progress 上报的内容都会影响控制台的显示,比如显示OTA升级进度,显示OTA升级失败等。
  • aiot_download_send_request
      设备通过OTA消息回调函数知道了固件下载地址后,就可以调用这个接口,下载一段固件。
      这段长度可由AIOT_DLOPT_RANGE_START和AIOT_DLOPT_RANGE_END选项设置。设置后,下载的范围是       [AIOT_DLOPT_RANGE_START,AIOT_DLOPT_RANGE_END],包括这个区间的起始和终止字节.
     如果不做设置, 则默认SDK会整段去请求固件内容,但每到填满1次用户buffer就会通知用户1次,buffer长度用AIOT_DLOPT_BODY_BUFFER_MAX_LEN选项配置。

常见问题

  • buffer开太小,导致下载速度太慢在下载速度慢的情况下,应首先检查通过aiot_download_setoptAIOT_DLOPT_BODY_BUFFER_MAX_LEN选项配置的值的大小。AIOT_DLOPT_BODY_BUFFER_MAX_LEN选项指定了下载过程中用于接收固件报文的buffer的大小。这个buffer越大,就意味着下载固件和填满它通知用户的次数越少,总体耗时越短。过小的buffer, 比如100 Byte左右,会导致整体耗时偏长,受网速波动影响的可能性也越大。同时,在多线程的系统中,如果下载任务执行慢, 会一直占用CPU,影响其他业务的正常运行。
  • 设备升级超时时间设置太短会导致控制台显示升级失败控制台在部署OTA任务的时候, 有设备升级超时时间(分钟)这个选项。该值需要根据下载的实际情况来填写, 并充分考虑网络异常带来的波动, 保留一定的余量。如果该值设置得太小, 比如设置为2分钟, 那么设备如果没有在2分钟内完成升级并且上报新的版本号的话, 控制台就会提示失败, 此时设备端表现为下载通道直接断开。
  • 控制台在选择定向升级的时候无法找到目标设备控制台在设置升级范围为定向升级时,如果设备范围一栏里面没法找到目标设备,需要确认该目标设备是否上报过版本号。如果上报过版本号,还需确认它上报过的版本号跟当前的待升级版本号是否一致。如果没有上报过版本号,或者上报了但是跟当前的待升级版本号不符合,那么都是无法触发升级任务的。
  • 下载完成,但是没有上报新的版本号,导致云端认为没有升级成功控制台是以设备上报的版本号来判断是否升级成功,而非根据上报的下载百分比。因此, 在设备下载完固件并成功升级后,务必要确保设备上报新的版本号,否则在控制台不会看到设备升级成功。
  • 分段下载的时候, 由于分段之间有重合,或者没有按顺序下载,导致最终校核失败分段下载的时候, 需要保证分段是按照从头到尾这样的顺序来分段的, 同时各个段之间还不能有重叠区域。比如:[0,1024][1024, 2048],这样就会出错,会导致第1024个字节重复下载了2次,从而导致最终校验和失败。需要改成[0, 1024], [1025, 2048]
  • 在控制台看到进度100%不代表升级完成由于升级进度是用户以aiot_download_report_progress上报的,上报了100%的进度后,仅表示这个固件下载完成了100%, 不代表升级完成。云端只根据设备是否上报了OTA推送的目标固件版本号来判断是否升级成功,这在设备端通过调用SDK的aiot_ota_report_version接口并传入升级后版本号来完成。
  • 下载过程中网络断开导致下载失败下载过程中, 如果出现了网络断开的情况, 会导致设备收取不到报文。常见的错误提示如下:
download failed, error code is -3864, try to send renewal request
这时请检查网络连接。在我们的demo代码中,网络恢复后SDK会自动的用断点续传的方式继续下载。

例程讲解

现对照demos/fota_posix_demo.c例程,分步骤讲解如何使用API。
这个例程适用于Linux这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建3个线程
  • 一个线程用于保活长连接。
  • 一个线程用于接收消息,并在有消息到达时进入默认的数据回调,在连接状态变化时进入事件回调。
  • 一个线程用于从网络上HTTP下载待升级的固件,这个线程由接收消息线程得到OTA升级的MQTT消息后启动。
  需要用户关注或修改的部分, 已用 TODO 在注释中标明。  
1、设置设备证书
例程中使用的设备证书是公用的, 所以应用于实际业务时, 请替换如下的TODO部分, 传入用户自己真实的设备证书。
/* TODO: 替换为自己设备的设备证书 */ char *product_key = "a13FNXXXXXX"; char *device_name = "ota_demo"; char *device_secret = "bIP6G5epJtHuOYvuVcFaTN53wJXXXXXX";
2、建立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);
3、创建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) { /* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */ aiot_mqtt_deinit(&mqtt_handle); aiot_ota_deinit(&ota_handle); printf("aiot_mqtt_connect failed: -0x%04X\n", -res); return -3; }
  需要注意的是, 以上代码段同时也给OTA会话设置了数据接收回调,为 demo_ota_recv_handler 函数。
4、上报当前固件版本号
为了让云平台的控制台正常工作,设备建立MQTT连接后,应准确的、及时的、上报当前所运行固件的版本号,参见代码注释说明。
/* TODO: 非常重要!!! * * cur_version 要根据用户实际情况, 改成从设备的配置区获取, 要反映真实的版本号, 而不能像示例这样写为固定值 * * 1. 如果设备从未上报过版本号, 在控制台网页将无法部署升级任务 * 2. 如果设备升级完成后, 上报的不是新的版本号, 在控制台网页将会显示升级失败 * */ /* 演示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\n", -res); }
5、建立MQTT保活线程和MQTT收包线程
这些都是使用MQTT连云时候的正常操作,与OTA无关。
/* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */ res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle); if (res < 0) { printf("pthread_create demo_mqtt_process_thread failed: %d\n", res); return -1; } /* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */ res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle); if (res < 0) { printf("pthread_create demo_mqtt_recv_thread failed: %d\n", res); return -1; }
6、OTA升级消息到达时, 启动下载线程
以上程序主体已讲解完毕,此后,如果SDK从MQTT通道收到了升级的消息,则会开始下载。
/* 用户通过 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_FOTA: { uint32_t last_percent = 0; 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) { return; } 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下载 */ aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_CRED, (void *)(&cred)); /* 设置下载时访问的服务器端口号 */ aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_PORT, (void *)(&port)); /* 设置下载的任务信息, 通过输入参数 ota_msg 中的 task_desc 成员得到, 内含下载地址, 固件大小, 固件签名等 */ aiot_download_setopt(dl_handle, AIOT_DLOPT_TASK_DESC, (void *)(ota_msg->task_desc)); /* 设置下载内容到达时, SDK将调用的回调函数 */ aiot_download_setopt(dl_handle, AIOT_DLOPT_RECV_HANDLER, (void *)(demo_download_recv_handler)); /* 设置单次下载最大的buffer长度, 每当这个长度的内存读满了后会通知用户 */ aiot_download_setopt(dl_handle, AIOT_DLOPT_BODY_BUFFER_MAX_LEN, (void *)(&max_buffer_len)); /* 设置 AIOT_DLOPT_RECV_HANDLER 的不同次调用之间共享的数据, 比如例程把进度存在这里 */ aiot_download_setopt(dl_handle, AIOT_DLOPT_USER_DATA, (void *)&last_percent); /* 启动专用的下载线程, 去完成固件内容的下载 */ res = pthread_create(&g_download_thread, NULL, demo_ota_download_thread, dl_handle); if (res < 0) { printf("pthread_create demo_ota_download_thread failed: %d\n", res); aiot_download_deinit(&dl_handle); } else { /* 下载线程被设置为 detach 类型, 固件内容获取完毕后可自主退出 */ pthread_detach(g_download_thread); } break; } default: break; } }
7、专用下载线程获取固件内容
以上代码启动了一个 demo_ota_download_thread 线程,它的逻辑很简单,就是去服务器下载固件的内容。
/* 执行aiot_download_recv的线程, 实现固件内容的请求和接收 */ void *demo_ota_download_thread(void *dl_handle) { int32_t ret = 0; printf("starting download thread in 2 seconds ......\n"); sleep(2); /* 向固件服务器请求下载 */ /* * TODO: 下面这样的写法, 就是以1个请求, 获取全部的固件内容 * 设备资源比较少, 或者网络较差时, 也可以分段下载, 需要组合 * * aiot_download_setopt(dl_handle, AIOT_DLOPT_RANGE_START, ...); * aiot_download_setopt(dl_handle, AIOT_DLOPT_RANGE_END, ...); * aiot_download_send_request(dl_handle); * * 实现, 这种情况下, 需要把以上组合语句放置到循环中, 多次 send_request 和 recv * */ aiot_download_send_request(dl_handle); while (1) { /* 从网络收取服务器回应的固件内容 */ ret = aiot_download_recv(dl_handle); /* 固件全部下载完时, aiot_download_recv() 的返回值会等于 STATE_DOWNLOAD_FINISHED, 否则是当次获取的字节数 */ if (STATE_DOWNLOAD_FINISHED == ret) { printf("download completed\n"); break; } } /* 下载所有固件内容完成, 销毁下载会话, 线程自行退出 */ aiot_download_deinit(&dl_handle); printf("download thread exit\n"); return NULL; }
8、处理下载内容, 写入Flash
SDK收到的固件内容,会进入用户通过 aiot_download_setopt() 设置的收包回调,告知用户,用户应在这里保存下载内容。
/* 下载收包回调, 用户调用 aiot_download_recv() 后, SDK收到数据会进入这个函数, 把下载到的数据交给用户 */ /* TODO: 一般来说, 设备升级时, 会在这个回调中, 把下载到的数据写到Flash上 */ void demo_download_recv_handler(void *handle, const aiot_download_recv_t *packet, void *userdata) { uint32_t data_buffer_len = 0; uint32_t last_percent = 0; int32_t percent = 0; /* 目前只支持 packet->type 为 AIOT_DLRECV_HTTPBODY 的情况 */ if (!packet || AIOT_DLRECV_HTTPBODY != packet->type) { return; } percent = packet->data.percent; if (userdata) { last_percent = *((uint32_t *)(userdata)); } data_buffer_len = packet->data.len; /* 如果 percent 为负数, 说明发生了收包异常或者digest校验错误 */ if (percent < 0) { printf("exception: percent = %d\n", percent); aiot_download_report_progress(handle, -2); return; } /* * TODO: 下载一段固件成功, 这个时候, 用户应该将 * 起始地址为 packet->data.buffer, 长度为 packet->data.len 的内存, 保存到flash上 * * 如果烧写flash失败, 还应该调用 aiot_download_report_progress(handle, -4) 将失败上报给云平台 */ /* percent 入参的值为 100 时, 说明SDK已经下载固件内容全部完成 */ if (percent == 100) { /* * TODO: 这个时候, 一般用户就应该完成所有的固件烧录, 保存当前工作, 重启设备, 切换到新的固件上启动了 并且, 新的固件必须要以 aiot_ota_report_version(ota_handle, new_version); 这样的操作, 将升级后的新版本号(比如1.0.0升到1.1.0, 则new_version的值是"1.1.0")上报给云平台 云平台收到了新的版本号上报后, 才会判定升级成功, 否则会认为本次升级失败了 */ } /* 简化输出, 只有距离上次的下载进度增加5%以上时, 才会打印进度, 并把进度上报给云端 */ if (percent - last_percent >= 5 || percent == 100) { printf("download %03d%% done, +%d bytes\n", percent, data_buffer_len); aiot_download_report_progress(handle, percent); if (userdata) { *((uint32_t *)(userdata)) = percent; } } }
9、观察运行日志
以下就是编译运行 output/fota-posix-demo 后,从控制台网页操作推送固件1.1.0,得到的一次完整运行日志。
$ ./output/fota-posix-demo [1579511737.377][LK-0313] MQTT user calls aiot_mqtt_connect api, connect [1579511737.377][LK-0317] ota_demo&a13FNXXXXXX [1579511737.377][LK-0318] 779ACA0181ED1C0BD4B1BA7A347C9B172E27EAAB4AA18FC1F9FAD4CED1047B51 [1579511737.377][LK-0319] a13FNXXXXXX.ota_demo|timestamp=2524608000000,_ss=1,_v=sdk-c-4.0.0,securemode=2,signmethod=hmacsha256,ext=1,| establish mbedtls connection with server(host='a13FNXXXXXX.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) [1579511737.522][LK-0313] MQTT connect success in 152 ms AIOT_MQTTEVT_CONNECT [1579511737.522][LK-0309] pub: /ota/device/inform/a13FNXXXXXX/ota_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 76 65 72 73 69 6F 6E 22 3A 22 31 2E | ":{"version":"1. [LK-030A] > 30 2E 30 22 7D 7D | 0.0"}} heartbeat response heartbeat response heartbeat response heartbeat response [1579511826.577][LK-0309] pub: /ota/device/upgrade/a13FNXXXXXX/ota_demo [LK-030A] < 7B 22 63 6F 64 65 22 3A 22 31 30 30 30 22 2C 22 | {"code":"1000"," [LK-030A] < 64 61 74 61 22 3A 7B 22 73 69 7A 65 22 3A 31 32 | data":{"size":12 [LK-030A] < 34 38 33 30 36 2C 22 73 69 67 6E 22 3A 22 34 62 | 48306,"sign":"4b [LK-030A] < 66 61 63 65 66 35 63 62 61 30 61 35 36 64 63 32 | facef5cba0a56dc2 [LK-030A] < 62 65 62 38 65 65 30 33 65 32 62 65 30 61 37 61 | beb8ee03e2be0a7a [LK-030A] < 65 64 65 65 65 31 38 38 35 61 63 65 30 66 62 35 | edeee1885ace0fb5 [LK-030A] < 35 65 61 38 62 62 66 64 39 38 64 61 64 63 22 2C | 5ea8bbfd98dadc", [LK-030A] < 22 76 65 72 73 69 6F 6E 22 3A 22 31 2E 31 2E 30 | "version":"1.1.0 [LK-030A] < 22 2C 22 75 72 6C 22 3A 22 68 74 74 70 73 3A 2F | ","url":"https:/ [LK-030A] < 2F 6F 74 61 2E 69 6F 74 2D 74 68 69 6E 67 2E 63 | /ota.iot-thing.c [LK-030A] < 6E 2D 73 68 61 6E 67 68 61 69 2E 61 6C 69 79 75 | n-shanghai.aliyu [LK-030A] < 6E 63 73 2E 63 6F 6D 2F 6F 74 61 2F 36 35 64 66 | ncs.com/ota/65df [LK-030A] < 63 64 61 30 34 37 33 62 65 32 39 38 33 36 64 66 | cda0473be29836df [LK-030A] < 64 65 35 38 35 34 37 32 37 65 31 32 2F 63 6B 35 | de5854727e12/ck5 [LK-030A] < 32 6E 79 6F 31 7A 30 30 30 30 33 39 37 69 69 79 | 2nyo1z0000397iiy [LK-030A] < 76 68 6E 72 32 69 2E 62 69 6E 3F 45 78 70 69 72 | vhnr2i.bin?Expir [LK-030A] < 65 73 3D 31 35 37 39 35 39 38 32 32 36 26 4F 53 | es=1579598226&OS [LK-030A] < 53 41 63 63 65 73 73 4B 65 79 49 64 3D 63 53 38 | SAccessKeyId=cS8 [LK-030A] < 75 52 52 79 35 34 52 73 7A 59 57 6E 61 26 53 69 | uRRy54RszYWna&Si [LK-030A] < 67 6E 61 74 75 72 65 3D 42 4F 55 46 51 65 34 68 | gnature=BOUFQe4h [LK-030A] < 39 6E 36 41 64 5A 57 46 42 45 49 7A 33 5A 6A 70 | 9n6AdZWFBEIz3Zjp [LK-030A] < 42 70 59 25 33 44 22 2C 22 73 69 67 6E 4D 65 74 | BpY%3D","signMet [LK-030A] < 68 6F 64 22 3A 22 53 48 41 32 35 36 22 7D 2C 22 | hod":"SHA256"}," [LK-030A] < 69 64 22 3A 31 35 37 39 35 31 31 38 32 36 35 32 | id":157951182652 [LK-030A] < 30 2C 22 6D 65 73 73 61 67 65 22 3A 22 73 75 63 | 0,"message":"suc [LK-030A] < 63 65 73 73 22 7D | cess"} OTA target firmware version: 1.1.0, size: 1248306 Bytes starting download thread in 2 seconds ...... establish mbedtls connection with server(host='ota.iot-thing.cn-shanghai.aliyuncs.com', port=[443]) success to establish mbedtls connection, fd = 4(cost 44868 bytes in total, max used 47604 bytes) [1579511828.699][LK-040B] > GET /ota/65dfcda0473be29836dfde5854727e12/ck52nyo1z0000397iiyvhnr2i.bin?Expires=1579598226&OSSAccessKeyId=cS8uRRy54RszYWna&Signature [1579511828.699][LK-040B] > Host: ota.iot-thing.cn-shanghai.aliyuncs.com [1579511828.699][LK-040B] > Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 [1579511828.699][LK-040B] > Range: bytes=0- [1579511828.699][LK-040B] > Content-Length: 0 [1579511828.699][LK-040B] > [1579511828.699][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_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 30 22 2C 22 64 | ":{"step":"0","d [LK-030A] > 65 73 63 22 3A 22 22 7D 7D | esc":""}} [1579511828.755][LK-040D] < HTTP/1.1 206 Partial Content [1579511828.755][LK-040D] < Date: Mon, 20 Jan 2020 09:17:08 GMT [1579511828.755][LK-040D] < Content-Type: application/octet-stream [1579511828.755][LK-040D] < Content-Length: 1248306 [1579511828.755][LK-040D] < Connection: keep-alive [1579511828.755][LK-040D] < x-oss-request-id: 5E2570140838A332336C4642 [1579511828.755][LK-040D] < Content-Range: bytes 0-1248305/1248306 [1579511828.755][LK-040D] < Accept-Ranges: bytes [1579511828.755][LK-040D] < ETag: "4818CE191A4E2A93EE2DF17938286F86" [1579511828.755][LK-040D] < Last-Modified: Mon, 06 Jan 2020 16:32:55 GMT [1579511828.755][LK-040D] < x-oss-object-type: Normal [1579511828.755][LK-040D] < x-oss-hash-crc64ecma: 14290028752135618122 [1579511828.755][LK-040D] < x-oss-storage-class: Standard [1579511828.755][LK-040D] < Content-MD5: SBjOGRpOKpPuLfF5OChvhg== [1579511828.755][LK-040D] < x-oss-server-time: 47 [1579511828.755][LK-040D] < download 005% done, +8192 bytes [1579511828.777][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 32 2C 20 22 70 61 72 61 6D 73 | {"id":2, "params [LK-030A] > 22 3A 7B 22 73 74 65 70 22 3A 22 35 22 2C 22 64 | ":{"step":"5","d [LK-030A] > 65 73 63 22 3A 22 22 7D 7D | esc":""}} download 010% done, +8192 bytes [1579511828.788][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 33 2C 20 22 70 61 72 61 6D 73 | {"id":3, "params [LK-030A] > 22 3A 7B 22 73 74 65 70 22 3A 22 31 30 22 2C 22 | ":{"step":"10"," [LK-030A] > 64 65 73 63 22 3A 22 22 7D 7D | desc":""}} download 015% done, +8192 bytes [1579511828.788][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 34 2C 20 22 70 61 72 61 6D 73 | {"id":4, "params [LK-030A] > 22 3A 7B 22 73 74 65 70 22 3A 22 31 35 22 2C 22 | ":{"step":"15"," [LK-030A] > 64 65 73 63 22 3A 22 22 7D 7D | desc":""}} download 020% done, +8192 bytes [1579511828.799][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 35 2C 20 22 70 61 72 61 6D 73 | {"id":5, "params [LK-030A] > 22 3A 7B 22 73 74 65 70 22 3A 22 32 30 22 2C 22 | ":{"step":"20"," [LK-030A] > 64 65 73 63 22 3A 22 22 7D 7D | desc":""}} download 025% done, +8192 bytes [1579511828.800][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 36 2C 20 22 70 61 72 61 6D 73 | {"id":6, "params [LK-030A] > 22 3A 7B 22 73 74 65 70 22 3A 22 32 35 22 2C 22 | ":{"step":"25"," [LK-030A] > 64 65 73 63 22 3A 22 22 7D 7D | desc":""}} download 030% done, +8192 bytes [1579511828.800][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 37 2C 20 22 70 61 72 61 6D 73 | {"id":7, "params [LK-030A] > 22 3A 7B 22 73 74 65 70 22 3A 22 33 30 22 2C 22 | ":{"step":"30"," [LK-030A] > 64 65 73 63 22 3A 22 22 7D 7D | desc":""}} download 035% done, +8192 bytes [1579511828.811][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 38 2C 20 22 70 61 72 61 6D 73 | {"id":8, "params [LK-030A] > 22 3A 7B 22 73 74 65 70 22 3A 22 33 35 22 2C 22 | ":{"step":"35"," [LK-030A] > 64 65 73 63 22 3A 22 22 7D 7D | desc":""}} download 040% done, +8192 bytes [1579511828.811][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 39 2C 20 22 70 61 72 61 6D 73 | {"id":9, "params [LK-030A] > 22 3A 7B 22 73 74 65 70 22 3A 22 34 30 22 2C 22 | ":{"step":"40"," [LK-030A] > 64 65 73 63 22 3A 22 22 7D 7D | desc":""}} download 045% done, +8192 bytes [1579511828.811][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 31 30 2C 20 22 70 61 72 61 6D | {"id":10, "param [LK-030A] > 73 22 3A 7B 22 73 74 65 70 22 3A 22 34 35 22 2C | s":{"step":"45", [LK-030A] > 22 64 65 73 63 22 3A 22 22 7D 7D | "desc":""}} download 050% done, +8192 bytes [1579511828.822][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 31 31 2C 20 22 70 61 72 61 6D | {"id":11, "param [LK-030A] > 73 22 3A 7B 22 73 74 65 70 22 3A 22 35 30 22 2C | s":{"step":"50", [LK-030A] > 22 64 65 73 63 22 3A 22 22 7D 7D | "desc":""}} download 055% done, +8192 bytes [1579511828.822][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 31 32 2C 20 22 70 61 72 61 6D | {"id":12, "param [LK-030A] > 73 22 3A 7B 22 73 74 65 70 22 3A 22 35 35 22 2C | s":{"step":"55", [LK-030A] > 22 64 65 73 63 22 3A 22 22 7D 7D | "desc":""}} download 060% done, +8192 bytes [1579511828.822][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 31 33 2C 20 22 70 61 72 61 6D | {"id":13, "param [LK-030A] > 73 22 3A 7B 22 73 74 65 70 22 3A 22 36 30 22 2C | s":{"step":"60", [LK-030A] > 22 64 65 73 63 22 3A 22 22 7D 7D | "desc":""}} download 065% done, +8192 bytes [1579511828.822][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 31 34 2C 20 22 70 61 72 61 6D | {"id":14, "param [LK-030A] > 73 22 3A 7B 22 73 74 65 70 22 3A 22 36 35 22 2C | s":{"step":"65", [LK-030A] > 22 64 65 73 63 22 3A 22 22 7D 7D | "desc":""}} download 070% done, +8192 bytes [1579511828.822][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 31 35 2C 20 22 70 61 72 61 6D | {"id":15, "param [LK-030A] > 73 22 3A 7B 22 73 74 65 70 22 3A 22 37 30 22 2C | s":{"step":"70", [LK-030A] > 22 64 65 73 63 22 3A 22 22 7D 7D | "desc":""}} download 075% done, +8192 bytes [1579511828.833][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 31 36 2C 20 22 70 61 72 61 6D | {"id":16, "param [LK-030A] > 73 22 3A 7B 22 73 74 65 70 22 3A 22 37 35 22 2C | s":{"step":"75", [LK-030A] > 22 64 65 73 63 22 3A 22 22 7D 7D | "desc":""}} download 080% done, +8192 bytes [1579511828.833][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 31 37 2C 20 22 70 61 72 61 6D | {"id":17, "param [LK-030A] > 73 22 3A 7B 22 73 74 65 70 22 3A 22 38 30 22 2C | s":{"step":"80", [LK-030A] > 22 64 65 73 63 22 3A 22 22 7D 7D | "desc":""}} download 085% done, +8192 bytes [1579511828.833][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 31 38 2C 20 22 70 61 72 61 6D | {"id":18, "param [LK-030A] > 73 22 3A 7B 22 73 74 65 70 22 3A 22 38 35 22 2C | s":{"step":"85", [LK-030A] > 22 64 65 73 63 22 3A 22 22 7D 7D | "desc":""}} download 090% done, +8192 bytes [1579511828.833][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 31 39 2C 20 22 70 61 72 61 6D | {"id":19, "param [LK-030A] > 73 22 3A 7B 22 73 74 65 70 22 3A 22 39 30 22 2C | s":{"step":"90", [LK-030A] > 22 64 65 73 63 22 3A 22 22 7D 7D | "desc":""}} download 095% done, +8192 bytes [1579511828.833][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 32 30 2C 20 22 70 61 72 61 6D | {"id":20, "param [LK-030A] > 73 22 3A 7B 22 73 74 65 70 22 3A 22 39 35 22 2C | s":{"step":"95", [LK-030A] > 22 64 65 73 63 22 3A 22 22 7D 7D | "desc":""}} [1579511828.833][LK-0901] digest matched download 100% done, +3122 bytes [1579511828.833][LK-0309] pub: /ota/device/progress/a13FNXXXXXX/ota_demo [LK-030A] > 7B 22 69 64 22 3A 32 31 2C 20 22 70 61 72 61 6D | {"id":21, "param [LK-030A] > 73 22 3A 7B 22 73 74 65 70 22 3A 22 31 30 30 22 | s":{"step":"100" [LK-030A] > 2C 22 64 65 73 63 22 3A 22 22 7D 7D | ,"desc":""}} download completed download thread exit heartbeat response

四、子设备管理

不具备IP地址的设备无法直接连接云物联网平台,需要通过网关接入到云物联网平台,网关需要集成“子设备管理”用于接入不具备IP地址的设备。

限制说明

  • 网关必须通过MQTT方式连接物联网平台

概述

由于子设备不能与物联网平台直接通信,所以网关需要代理子设备进行子设备的身份认证、子设备上线、下线、以及数据收发等工作。当网关离线时,物联网平台会自动将通过该网关接入的子设备全部设置为离线状态,因此每次网关连接到平台时,需要将当前在线的子设备的状态更新到物联网平台。

子设备设备密钥获取

网关在设计时,大概有两种方案获取子设备的设备密钥
  1. 用户输入子设备密钥如果用户知道需要通过网关接入哪些子设备,那么可以通过在网关上提供串口、Web页面等方式将子设备的设备密钥(ProductKey、DeviceName、DeviceSecret)输入给网关,当网关发现子设备连接到网关时,直接使用子设备的设备密钥将子设备连接到物联网平台
  2. 动态获取子设备密钥如果用户无法通过某种方式将子设备的设备密钥输入到网关,那么当子设备连接到网关时,网关需要根据子设备的信息知道子设备对应的ProductKey、ProductSecret、DeviceName,然后动态从物联网平台获取子设备的DeviceSecret,继而将子设备连到物联网平台。这种方式往往需要网关的固件在设计时固化子设备的型号与云ProductKey、ProductSecret之间的映射关系。SDK提供API用于获取子设备的DeviceSecret。

子设备与网关建立拓扑关系

建立拓扑关系是指网关告知物联网平台:某个或者某几个子设备通过自己连接到物联网平台。当物联网平台收到该通知后,将会更新网关连接的子设备信息,当有消息需要发送给子设备时,物联网平台将会把子设备的消息发送给对应的网关,然后由网关将子设备的数据转发给子设备。

子设备与网关解除拓扑关系

解除拓扑关系是指网关告知物联网平台:自己不再代理某个或某几个子设备连接到物联网平台。当网关与子设备解除拓扑关系之后,平台不会将发送给子设备的消息发送给网关。

子设备上线

网关需要告知平台自己代理的子设备的状态,如果子设备已离线,那么当平台发送QoS=0的消息给子设备时将会立即丢弃该消息;如果子设备在线,平台才会尝试将消息发送给子设备。

子设备离线

当设备与网关之间的连接断开时,网关需要告知物联网平台子设备已离线,避免网关收到发送给子设备的消息。

子设备发送消息到物联网平台

子设备可以直接基于MQTT Topic进行消息的收发,复用MQTT的API aiot_mqtt_pub()即可,注意topic中的需要使用子设备的ProductKey和DeviceName。
若子设备通过物模型方式与物联网平台通信,可以调用物模型的API  aiot_dm_send()进行属性上报或者事件上报。

子设备接收来自物联网平台的消息

子设备若直接基于MQTT Topic进行消息通信,可以通过调用MQTT的API aiot_mqtt_sub()订阅消息来接收来自物联网平台的消息。
子设备若通过物模型与物联网平台通信,那么接收到属性设置和服务调用的机制在物模型编程中进行描述,客户编程时需要解析对应设备的ProductKey、DeviceName来区分属性设置或者服务调用是针对网关还是哪个子设备。

API列表

接口名说明
aiot_subdev_init初始化subdev实例并设置默认参数。
aiot_subdev_setopt  配置subdev实例。
aiot_subdev_deinit释放subdev实例句柄的资源。  
aiot_subdev_send_topo_add向云端发送添加子设备与网关的拓扑关系的请求。
aiot_subdev_send_topo_delete向云端发送删除子设备与网关的拓扑关系的请求。
aiot_subdev_send_topo_get向云端发送获取子设备与网关的拓扑关系的请求。
aiot_subdev_send_batch_login向云端发送子设备批量登录的请求。
aiot_subdev_send_batch_logout向云端发送子设备批量登出的请求。
aiot_subdev_send_sub_register向云端发送子设备动态注册的请求。
aiot_subdev_send_product_register向云端发送子设备动态注册的请求(可从其他网关抢绑)。

例程讲解

现对照components/subdev/demos/subdev_basic_demo.c例程,分步骤讲解如何使用API。
  • 设置网关的设备密钥例程使用的三元组是测试使用,客户在体验相关代码时请在demo_mqtt_start中,替换如下的TODO部分, 传入用户自己网关的真实设备密钥。
/* TODO: 替换为自己设备的producKey和deviceName */ char *product_key = "a1tmcXXXXXX"; char *device_name = "subdev_basic_demo";
  将变量g_subdev中的子设备更换为用户自己真实的三元组和product key。  
  • 进入程序入口, 给SDK配置全局的底层依赖和日志回调
       底层依赖描述了硬件平台的资源使用方式, 比如怎样获取时钟, 分配内存等, 日志回调是用户的函数, SDK有log输出的时候会进入这个函数。
int main(int argc, char *argv[]) { ... ... /* 配置SDK的底层依赖 */ aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); /* 配置SDK的日志输出 */ aiot_state_set_logcb(demo_state_logcb);
  • 给连接配置参数
      这些参数包括MQTT句柄和输出SDK从云端接收消息的回调函数
res = demo_mqtt_start(&mqtt_handle); if (res < 0) { printf("demo_mqtt_start failed\n"); return -1; } subdev_handle = aiot_subdev_init(); if (subdev_handle == NULL) { printf("aiot_subdev_init failed\n"); demo_mqtt_stop(&mqtt_handle); return -1; } aiot_subdev_setopt(subdev_handle, AIOT_SUBDEVOPT_MQTT_HANDLE, mqtt_handle); aiot_subdev_setopt(subdev_handle, AIOT_SUBDEVOPT_RECV_HANDLER, demo_subdev_recv_handler);
  • 运行例程
      直接运行例程,SDK将演示添加topo关系和批量loging两个子设备管理的功能,其他功能的使用方法类似,具体可参考demo中注释掉的部分
res = aiot_subdev_send_topo_add(subdev_handle, g_subdev, sizeof(g_subdev)/sizeof(aiot_subdev_dev_t)); if (res < STATE_SUCCESS) { printf("aiot_subdev_send_topo_add failed, res: -0x%04X\n", -res); aiot_subdev_deinit(&subdev_handle); demo_mqtt_stop(&mqtt_handle); return -1; } sleep(2); // aiot_subdev_send_topo_delete(subdev_handle, g_subdev, sizeof(g_subdev)/sizeof(aiot_subdev_dev_t)); // if (res < STATE_SUCCESS) { // printf("aiot_subdev_send_topo_delete failed, res: -0x%04X\n", -res); // aiot_subdev_deinit(&subdev_handle); // demo_mqtt_stop(&mqtt_handle); // return -1; // } // sleep(2); // aiot_subdev_send_topo_get(subdev_handle); // if (res < STATE_SUCCESS) { // printf("aiot_subdev_send_topo_get failed, res: -0x%04X\n", -res); // aiot_subdev_deinit(&subdev_handle); // demo_mqtt_stop(&mqtt_handle); // return -1; // } // sleep(2); // aiot_subdev_send_sub_register(subdev_handle, g_subdev, sizeof(g_subdev)/sizeof(aiot_subdev_dev_t)); // if (res < STATE_SUCCESS) { // printf("aiot_subdev_send_sub_register failed, res: -0x%04X\n", -res); // aiot_subdev_deinit(&subdev_handle); // demo_mqtt_stop(&mqtt_handle); // return -1; // } // sleep(2); // aiot_subdev_send_product_register(subdev_handle, g_subdev, sizeof(g_subdev)/sizeof(aiot_subdev_dev_t)); // if (res < STATE_SUCCESS) { // printf("aiot_subdev_send_product_register failed, res: -0x%04X\n", -res); // aiot_subdev_deinit(&subdev_handle); // demo_mqtt_stop(&mqtt_handle); // return -1; // } // sleep(2); aiot_subdev_send_batch_login(subdev_handle, g_subdev, sizeof(g_subdev)/sizeof(aiot_subdev_dev_t)); if (res < STATE_SUCCESS) { printf("aiot_subdev_send_batch_login failed, res: -0x%04X\n", -res); aiot_subdev_deinit(&subdev_handle); demo_mqtt_stop(&mqtt_handle); return -1; } sleep(2); // aiot_subdev_send_batch_logout(subdev_handle, g_subdev, sizeof(g_subdev)/sizeof(aiot_subdev_dev_t)); // if (res < STATE_SUCCESS) { // printf("aiot_subdev_send_batch_logout failed, res: -0x%04X\n", -res); // aiot_subdev_deinit(&subdev_handle); // demo_mqtt_stop(&mqtt_handle); // return -1; // } [1592187045.763][LK-0309] pub: /sys/a1tmc66UyDK/subdev_basic_demo/thing/topo/add [LK-030A] > 7B 22 69 64 22 3A 22 31 22 2C 22 76 65 72 73 69 | {"id":"1","versi [LK-030A] > 6F 6E 22 3A 22 31 2E 30 22 2C 22 70 61 72 61 6D | on":"1.0","param [LK-030A] > 73 22 3A 5B 7B 22 70 72 6F 64 75 63 74 4B 65 79 | s":[{"productKey [LK-030A] > 22 3A 22 61 31 33 46 4E 35 54 70 6C 4B 71 22 2C | ":"a13FNXXXXXX", [LK-030A] > 22 64 65 76 69 63 65 4E 61 6D 65 22 3A 22 73 75 | "deviceName":"su [LK-030A] > 62 64 65 76 5F 62 61 73 69 63 5F 64 65 6D 6F 5F | bdev_basic_demo_ [LK-030A] > 30 31 22 2C 22 63 6C 69 65 6E 74 49 64 22 3A 22 | 01","clientId":" [LK-030A] > 61 31 33 46 4E 35 54 70 6C 4B 71 2E 73 75 62 64 | a13FNXXXXXX.subd [LK-030A] > 65 76 5F 62 61 73 69 63 5F 64 65 6D 6F 5F 30 31 | ev_basic_demo_01 [LK-030A] > 22 2C 22 74 69 6D 65 73 74 61 6D 70 22 3A 22 31 | ","timestamp":"1 [LK-030A] > 35 39 32 31 38 37 30 34 35 37 36 33 22 2C 22 73 | 592187045763","s [LK-030A] > 69 67 6E 6D 65 74 68 6F 64 22 3A 22 68 6D 61 63 | ignmethod":"hmac [LK-030A] > 53 68 61 32 35 36 22 2C 22 73 69 67 6E 22 3A 22 | Sha256","sign":" [LK-030A] > 39 31 42 33 35 44 43 33 45 35 30 31 39 43 39 44 | 91B35DC3E5019C9D [LK-030A] > 46 43 39 46 35 36 34 38 33 36 33 39 39 37 36 41 | FC9F56483639976A [LK-030A] > 44 38 31 35 37 36 37 43 42 32 44 32 44 30 37 45 | D815767CB2D2D07E [LK-030A] > 32 39 34 42 37 32 46 32 30 45 34 35 43 35 46 44 | 294B72F20E45C5FD [LK-030A] > 22 7D 2C 7B 22 70 72 6F 64 75 63 74 4B 65 79 22 | "},{"productKey" [LK-030A] > 3A 22 61 31 33 46 4E 35 54 70 6C 4B 71 22 2C 22 | :"a13FNXXXXXX"," [LK-030A] > 64 65 76 69 63 65 4E 61 6D 65 22 3A 22 73 75 62 | deviceName":"sub [LK-030A] > 64 65 76 5F 62 61 73 69 63 5F 64 65 6D 6F 5F 30 | dev_basic_demo_0 [LK-030A] > 32 22 2C 22 63 6C 69 65 6E 74 49 64 22 3A 22 61 | 2","clientId":"a [LK-030A] > 31 33 46 4E 35 54 70 6C 4B 71 2E 73 75 62 64 65 | 13FN5TplKq.subde [LK-030A] > 76 5F 62 61 73 69 63 5F 64 65 6D 6F 5F 30 32 22 | v_basic_demo_02" [LK-030A] > 2C 22 74 69 6D 65 73 74 61 6D 70 22 3A 22 31 35 | ,"timestamp":"15 [LK-030A] > 39 32 31 38 37 30 34 35 37 36 33 22 2C 22 73 69 | 92187045763","si [LK-030A] > 67 6E 6D 65 74 68 6F 64 22 3A 22 68 6D 61 63 53 | gnmethod":"hmacS [LK-030A] > 68 61 32 35 36 22 2C 22 73 69 67 6E 22 3A 22 44 | ha256","sign":"D [LK-030A] > 36 37 34 32 34 41 34 35 31 33 45 30 32 44 42 33 | 67424A4513E02DB3 [LK-030A] > 46 34 39 44 31 33 43 35 33 46 41 37 45 43 34 37 | F49D13C53FA7EC47 [LK-030A] > 42 46 37 41 36 37 46 32 30 31 35 38 32 45 39 44 | BF7A67F201582E9D [LK-030A] > 37 34 35 36 32 35 31 36 45 44 30 36 33 41 44 22 | 74562516ED063AD" [LK-030A] > 7D 2C 7B 22 70 72 6F 64 75 63 74 4B 65 79 22 3A | },{"productKey": [LK-030A] > 22 61 31 33 46 4E 35 54 70 6C 4B 71 22 2C 22 64 | "a13FNXXXXXX","d [LK-030A] > 65 76 69 63 65 4E 61 6D 65 22 3A 22 73 75 62 64 | eviceName":"subd [LK-030A] > 65 76 5F 62 61 73 69 63 5F 64 65 6D 6F 5F 30 33 | ev_basic_demo_03 [LK-030A] > 22 2C 22 63 6C 69 65 6E 74 49 64 22 3A 22 61 31 | ","clientId":"a1 [LK-030A] > 33 46 4E 35 54 70 6C 4B 71 2E 73 75 62 64 65 76 | 3FN5TplKq.subdev [LK-030A] > 5F 62 61 73 69 63 5F 64 65 6D 6F 5F 30 33 22 2C | _basic_demo_03", [LK-030A] > 22 74 69 6D 65 73 74 61 6D 70 22 3A 22 31 35 39 | "timestamp":"159 [LK-030A] > 32 31 38 37 30 34 35 37 36 33 22 2C 22 73 69 67 | 2187045763","sig [LK-030A] > 6E 6D 65 74 68 6F 64 22 3A 22 68 6D 61 63 53 68 | nmethod":"hmacSh [LK-030A] > 61 32 35 36 22 2C 22 73 69 67 6E 22 3A 22 34 37 | a256","sign":"47 [LK-030A] > 31 33 38 43 38 31 38 36 34 42 41 38 38 46 31 46 | 138C81864BA88F1F [LK-030A] > 44 34 33 37 46 33 38 42 37 45 31 35 36 31 34 37 | D437F38B7E156147 [LK-030A] > 37 32 45 34 34 44 42 44 32 37 35 34 43 38 44 36 | 72E44DBD2754C8D6 [LK-030A] > 39 30 35 33 41 39 31 39 46 45 31 32 37 37 22 7D | 9053A919FE1277"} [LK-030A] > 2C 7B 22 70 72 6F 64 75 63 74 4B 65 79 22 3A 22 | ,{"productKey":" [LK-030A] > 61 31 33 46 4E 35 54 70 6C 4B 71 22 2C 22 64 65 | a13FNXXXXXX","de [LK-030A] > 76 69 63 65 4E 61 6D 65 22 3A 22 73 75 62 64 65 | viceName":"subde [LK-030A] > 76 5F 62 61 73 69 63 5F 64 65 6D 6F 5F 30 34 22 | v_basic_demo_04" [LK-030A] > 2C 22 63 6C 69 65 6E 74 49 64 22 3A 22 61 31 33 | ,"clientId":"a13 [LK-030A] > 46 4E 35 54 70 6C 4B 71 2E 73 75 62 64 65 76 5F | FN5TplKq.subdev_ [LK-030A] > 62 61 73 69 63 5F 64 65 6D 6F 5F 30 34 22 2C 22 | basic_demo_04"," [LK-030A] > 74 69 6D 65 73 74 61 6D 70 22 3A 22 31 35 39 32 | timestamp":"1592 [LK-030A] > 31 38 37 30 34 35 37 36 33 22 2C 22 73 69 67 6E | 187045763","sign [LK-030A] > 6D 65 74 68 6F 64 22 3A 22 68 6D 61 63 53 68 61 | method":"hmacSha [LK-030A] > 32 35 36 22 2C 22 73 69 67 6E 22 3A 22 37 30 34 | 256","sign":"704 [LK-030A] > 46 43 42 30 31 30 33 32 33 39 33 41 43 39 42 41 | FCB01032393AC9BA [LK-030A] > 39 46 38 37 45 32 44 44 36 41 42 30 31 36 39 35 | 9F87E2DD6AB01695 [LK-030A] > 35 34 36 31 45 33 41 45 32 43 35 36 31 35 39 46 | 5461E3AE2C56159F [LK-030A] > 36 32 34 39 35 43 38 34 37 45 34 44 39 22 7D 5D | 62495C847E4D9"}] [LK-030A] > 7D | } heartbeat response [1592187045.840][LK-0309] pub: /sys/a1tmc66UyDK/subdev_basic_demo/thing/topo/add_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 5B 7B 22 70 72 6F 64 75 63 74 4B 65 79 | a":[{"productKey [LK-030A] < 22 3A 22 61 31 33 46 4E 35 54 70 6C 4B 71 22 2C | ":"a13FNXXXXXX", [LK-030A] < 22 64 65 76 69 63 65 4E 61 6D 65 22 3A 22 73 75 | "deviceName":"su [LK-030A] < 62 64 65 76 5F 62 61 73 69 63 5F 64 65 6D 6F 5F | bdev_basic_demo_ [LK-030A] < 30 32 22 7D 2C 7B 22 70 72 6F 64 75 63 74 4B 65 | 02"},{"productKe [LK-030A] < 79 22 3A 22 61 31 33 46 4E 35 54 70 6C 4B 71 22 | y":"a13FNXXXXXX" [LK-030A] < 2C 22 64 65 76 69 63 65 4E 61 6D 65 22 3A 22 73 | ,"deviceName":"s [LK-030A] < 75 62 64 65 76 5F 62 61 73 69 63 5F 64 65 6D 6F | ubdev_basic_demo [LK-030A] < 5F 30 31 22 7D 2C 7B 22 70 72 6F 64 75 63 74 4B | _01"},{"productK [LK-030A] < 65 79 22 3A 22 61 31 33 46 4E 35 54 70 6C 4B 71 | ey":"a13FNXXXXXX [LK-030A] < 22 2C 22 64 65 76 69 63 65 4E 61 6D 65 22 3A 22 | ","deviceName":" [LK-030A] < 73 75 62 64 65 76 5F 62 61 73 69 63 5F 64 65 6D | subdev_basic_dem [LK-030A] < 6F 5F 30 34 22 7D 2C 7B 22 70 72 6F 64 75 63 74 | o_04"},{"product [LK-030A] < 4B 65 79 22 3A 22 61 31 33 46 4E 35 54 70 6C 4B | Key":"a13FN5TplK [LK-030A] < 71 22 2C 22 64 65 76 69 63 65 4E 61 6D 65 22 3A | q","deviceName": [LK-030A] < 22 73 75 62 64 65 76 5F 62 61 73 69 63 5F 64 65 | "subdev_basic_de [LK-030A] < 6D 6F 5F 30 33 22 7D 5D 2C 22 69 64 22 3A 22 31 | mo_03"}],"id":"1 [LK-030A] < 22 2C 22 6D 65 73 73 61 67 65 22 3A 22 73 75 63 | ","message":"suc [LK-030A] < 63 65 73 73 22 2C 22 6D 65 74 68 6F 64 22 3A 22 | cess","method":" [LK-030A] < 74 68 69 6E 67 2E 74 6F 70 6F 2E 61 64 64 22 2C | thing.topo.add", [LK-030A] < 22 76 65 72 73 69 6F 6E 22 3A 22 31 2E 30 22 7D | "version":"1.0"} msgid : 1 code : 200 product key : a1tmc66UyDK device name : subdev_basic_demo message : success data : [{"productKey":"a13FNXXXXXX","deviceName":"subdev_basic_demo_02"},{"productKey":"a13FNXXXXXX","deviceName":"subdev_basic_demo_01"},{"productKey":"a13FNXXXXXX","deviceName":"subdev_basic_demo_04"},{"productKey":"a13FNXXXXXX","deviceName":"subdev_basic_demo_03"}] [1592187045.874][LK-0309] pub: /sys/a1tmc66UyDK/subdev_basic_demo/thing/topo/change[LK-030A] < 7B 22 6D 65 74 68 6F 64 22 3A 22 74 68 69 6E 67 | {"method":"thing [LK-030A] < 2E 74 6F 70 6F 2E 63 68 61 6E 67 65 22 2C 22 69 | .topo.change","i [LK-030A] < 64 22 3A 22 31 31 38 30 37 38 31 38 36 36 22 2C | d":"1180781866", [LK-030A] < 22 70 61 72 61 6D 73 22 3A 7B 22 73 75 62 4C 69 | "params":{"subLi [LK-030A] < 73 74 22 3A 5B 7B 22 64 65 76 69 63 65 4E 61 6D | st":[{"deviceNam [LK-030A] < 65 22 3A 22 73 75 62 64 65 76 5F 62 61 73 69 63 | e":"subdev_basic [LK-030A] < 5F 64 65 6D 6F 5F 30 31 22 2C 22 70 72 6F 64 75 | _demo_01","produ [LK-030A] < 63 74 4B 65 79 22 3A 22 61 31 33 46 4E 35 54 70 | ctKey":"a13FN5Tp [LK-030A] < 6C 4B 71 22 7D 2C 7B 22 64 65 76 69 63 65 4E 61 | lKq"},{"deviceNa [LK-030A] < 6D 65 22 3A 22 73 75 62 64 65 76 5F 62 61 73 69 | me":"subdev_basi [LK-030A] < 63 5F 64 65 6D 6F 5F 30 32 22 2C 22 70 72 6F 64 | c_demo_02","prod [LK-030A] < 75 63 74 4B 65 79 22 3A 22 61 31 33 46 4E 35 54 | uctKey":"a13FN5T [LK-030A] < 70 6C 4B 71 22 7D 2C 7B 22 64 65 76 69 63 65 4E | plKq"},{"deviceN [LK-030A] < 61 6D 65 22 3A 22 73 75 62 64 65 76 5F 62 61 73 | ame":"subdev_bas [LK-030A] < 69 63 5F 64 65 6D 6F 5F 30 33 22 2C 22 70 72 6F | ic_demo_03","pro [LK-030A] < 64 75 63 74 4B 65 79 22 3A 22 61 31 33 46 4E 35 | ductKey":"a13FN5 [LK-030A] < 54 70 6C 4B 71 22 7D 2C 7B 22 64 65 76 69 63 65 | TplKq"},{"device [LK-030A] < 4E 61 6D 65 22 3A 22 73 75 62 64 65 76 5F 62 61 | Name":"subdev_ba [LK-030A] < 73 69 63 5F 64 65 6D 6F 5F 30 34 22 2C 22 70 72 | sic_demo_04","pr [LK-030A] < 6F 64 75 63 74 4B 65 79 22 3A 22 61 31 33 46 4E | oductKey":"a13FN [LK-030A] < 35 54 70 6C 4B 71 22 7D 5D 2C 22 73 74 61 74 75 | 5TplKq"}],"statu [LK-030A] < 73 22 3A 30 7D 2C 22 76 65 72 73 69 6F 6E 22 3A | s":0},"version": [LK-030A] < 22 31 2E 30 2E 30 22 7D | "1.0.0"} msgid : 1180781866 product key : a1tmc66UyDK device name : subdev_basic_demo params : {"subList":[{"deviceName":"subdev_basic_demo_01","productKey":"a13FNXXXXXX"},{"deviceName":"subdev_basic_demo_02","productKey":"a13FNXXXXXX"},{"deviceName":"subdev_basic_demo_03","productKey":"a13FNXXXXXX"},{"deviceName":"subdev_basic_demo_04","productKey":"a13FNXXXXXX"}],"status":0} [1592187047.767][LK-0309] pub: /ext/session/a1tmc66UyDK/subdev_basic_demo/combine/batch_login [LK-030A] > 7B 22 69 64 22 3A 22 32 22 2C 22 76 65 72 73 69 | {"id":"2","versi [LK-030A] > 6F 6E 22 3A 22 31 2E 30 22 2C 22 70 61 72 61 6D | on":"1.0","param [LK-030A] > 73 22 3A 7B 22 73 69 67 6E 4D 65 74 68 6F 64 22 | s":{"signMethod" [LK-030A] > 3A 22 68 6D 61 63 53 68 61 32 35 36 22 2C 22 64 | :"hmacSha256","d [LK-030A] > 65 76 69 63 65 4C 69 73 74 22 3A 5B 7B 22 70 72 | eviceList":[{"pr [LK-030A] > 6F 64 75 63 74 4B 65 79 22 3A 22 61 31 33 46 4E | oductKey":"a13FN [LK-030A] > 35 54 70 6C 4B 71 22 2C 22 64 65 76 69 63 65 4E | XXXXXX","deviceN [LK-030A] > 61 6D 65 22 3A 22 73 75 62 64 65 76 5F 62 61 73 | ame":"subdev_bas [LK-030A] > 69 63 5F 64 65 6D 6F 5F 30 31 22 2C 22 63 6C 69 | ic_demo_01","cli [LK-030A] > 65 6E 74 49 64 22 3A 22 61 31 33 46 4E 35 54 70 | entId":"a13FNXXX [LK-030A] > 6C 4B 71 2E 73 75 62 64 65 76 5F 62 61 73 69 63 | XXX.subdev_basic [LK-030A] > 5F 64 65 6D 6F 5F 30 31 22 2C 22 74 69 6D 65 73 | _demo_01","times [LK-030A] > 74 61 6D 70 22 3A 22 31 35 39 32 31 38 37 30 34 | tamp":"159218704 [LK-030A] > 37 37 36 37 22 2C 22 63 6C 65 61 6E 53 65 73 73 | 7767","cleanSess [LK-030A] > 69 6F 6E 22 3A 22 66 61 6C 73 65 22 2C 22 73 69 | ion":"false","si [LK-030A] > 67 6E 22 3A 22 46 46 38 44 45 38 36 30 42 46 44 | gn":"FF8DE860BFD [LK-030A] > 30 36 42 39 32 41 37 31 46 32 46 34 43 38 36 42 | 06B92A71F2F4C86B [LK-030A] > 32 31 36 42 37 34 39 44 36 41 37 31 45 37 42 46 | 216B749D6A71E7BF [LK-030A] > 38 35 35 36 33 34 43 41 42 45 44 35 44 37 45 39 | 855634CABED5D7E9 [LK-030A] > 42 38 41 35 39 22 7D 2C 7B 22 70 72 6F 64 75 63 | B8A59"},{"produc [LK-030A] > 74 4B 65 79 22 3A 22 61 31 33 46 4E 35 54 70 6C | tKey":"a13FNXXXX [LK-030A] > 4B 71 22 2C 22 64 65 76 69 63 65 4E 61 6D 65 22 | XX","deviceName" [LK-030A] > 3A 22 73 75 62 64 65 76 5F 62 61 73 69 63 5F 64 | :"subdev_basic_d [LK-030A] > 65 6D 6F 5F 30 32 22 2C 22 63 6C 69 65 6E 74 49 | emo_02","clientI [LK-030A] > 64 22 3A 22 61 31 33 46 4E 35 54 70 6C 4B 71 2E | d":"a13FNXXXXXX. [LK-030A] > 73 75 62 64 65 76 5F 62 61 73 69 63 5F 64 65 6D | subdev_basic_dem [LK-030A] > 6F 5F 30 32 22 2C 22 74 69 6D 65 73 74 61 6D 70 | o_02","timestamp [LK-030A] > 22 3A 22 31 35 39 32 31 38 37 30 34 37 37 36 37 | ":"1592187047767 [LK-030A] > 22 2C 22 63 6C 65 61 6E 53 65 73 73 69 6F 6E 22 | ","cleanSession" [LK-030A] > 3A 22 66 61 6C 73 65 22 2C 22 73 69 67 6E 22 3A | :"false","sign": [LK-030A] > 22 30 33 38 44 35 46 33 31 31 38 46 44 41 37 44 | "038D5F3118FDA7D [LK-030A] > 33 30 42 42 45 39 44 37 41 31 44 30 41 30 30 37 | 30BBE9D7A1D0A007 [LK-030A] > 35 30 39 31 35 39 32 30 39 43 38 33 46 35 32 34 | 509159209C83F524 [LK-030A] > 46 38 43 34 34 37 46 32 44 42 44 38 42 42 45 34 | F8C447F2DBD8BBE4 [LK-030A] > 33 22 7D 2C 7B 22 70 72 6F 64 75 63 74 4B 65 79 | 3"},{"productKey [LK-030A] > 22 3A 22 61 31 33 46 4E 35 54 70 6C 4B 71 22 2C | ":"a13FNXXXXXX", [LK-030A] > 22 64 65 76 69 63 65 4E 61 6D 65 22 3A 22 73 75 | "deviceName":"su [LK-030A] > 62 64 65 76 5F 62 61 73 69 63 5F 64 65 6D 6F 5F | bdev_basic_demo_ [LK-030A] > 30 33 22 2C 22 63 6C 69 65 6E 74 49 64 22 3A 22 | 03","clientId":" [LK-030A] > 61 31 33 46 4E 35 54 70 6C 4B 71 2E 73 75 62 64 | a13FNXXXXXX.subd [LK-030A] > 65 76 5F 62 61 73 69 63 5F 64 65 6D 6F 5F 30 33 | ev_basic_demo_03 [LK-030A] > 22 2C 22 74 69 6D 65 73 74 61 6D 70 22 3A 22 31 | ","timestamp":"1 [LK-030A] > 35 39 32 31 38 37 30 34 37 37 36 37 22 2C 22 63 | 592187047767","c [LK-030A] > 6C 65 61 6E 53 65 73 73 69 6F 6E 22 3A 22 66 61 | leanSession":"fa [LK-030A] > 6C 73 65 22 2C 22 73 69 67 6E 22 3A 22 43 35 44 | lse","sign":"C5D [LK-030A] > 32 35 41 35 46 30 35 35 35 33 45 41 34 44 44 37 | 25A5F05553EA4DD7 [LK-030A] > 43 42 34 37 46 33 45 43 32 45 30 43 35 35 30 33 | CB47F3EC2E0C5503 [LK-030A] > 30 39 44 37 46 30 34 38 35 31 33 32 42 35 31 36 | 09D7F0485132B516 [LK-030A] > 38 39 31 36 34 38 37 39 31 33 31 44 45 22 7D 2C | 89164879131DE"}, [LK-030A] > 7B 22 70 72 6F 64 75 63 74 4B 65 79 22 3A 22 61 | {"productKey":"a [LK-030A] > 31 33 46 4E 35 54 70 6C 4B 71 22 2C 22 64 65 76 | 13FN5TplKq","dev [LK-030A] > 69 63 65 4E 61 6D 65 22 3A 22 73 75 62 64 65 76 | iceName":"subdev [LK-030A] > 5F 62 61 73 69 63 5F 64 65 6D 6F 5F 30 34 22 2C | _basic_demo_04", [LK-030A] > 22 63 6C 69 65 6E 74 49 64 22 3A 22 61 31 33 46 | "clientId":"a13F [LK-030A] > 4E 35 54 70 6C 4B 71 2E 73 75 62 64 65 76 5F 62 | N5TplKq.subdev_b [LK-030A] > 61 73 69 63 5F 64 65 6D 6F 5F 30 34 22 2C 22 74 | asic_demo_04","t [LK-030A] > 69 6D 65 73 74 61 6D 70 22 3A 22 31 35 39 32 31 | imestamp":"15921 [LK-030A] > 38 37 30 34 37 37 36 37 22 2C 22 63 6C 65 61 6E | 87047767","clean [LK-030A] > 53 65 73 73 69 6F 6E 22 3A 22 66 61 6C 73 65 22 | Session":"false" [LK-030A] > 2C 22 73 69 67 6E 22 3A 22 30 38 37 32 36 36 39 | ,"sign":"0872669 [LK-030A] > 36 44 42 42 35 43 36 46 32 30 31 45 43 38 41 44 | 6DBB5C6F201EC8AD [LK-030A] > 45 38 43 46 33 46 35 32 39 31 42 41 41 32 39 45 | E8CF3F5291BAA29E [LK-030A] > 32 31 30 39 38 44 43 30 30 39 35 43 41 37 41 32 | 21098DC0095CA7A2 [LK-030A] > 37 43 37 45 30 32 35 46 34 22 7D 5D 7D 7D | 7C7E025F4"}]}} [1592187048.042][LK-0309] pub: /ext/session/a1tmc66UyDK/subdev_basic_demo/combine/batch_login_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 5B 7B 22 70 72 6F 64 75 63 74 4B 65 79 | a":[{"productKey [LK-030A] < 22 3A 22 61 31 33 46 4E 35 54 70 6C 4B 71 22 2C | ":"a13FNXXXXXX", [LK-030A] < 22 64 65 76 69 63 65 4E 61 6D 65 22 3A 22 73 75 | "deviceName":"su [LK-030A] < 62 64 65 76 5F 62 61 73 69 63 5F 64 65 6D 6F 5F | bdev_basic_demo_ [LK-030A] < 30 31 22 7D 2C 7B 22 70 72 6F 64 75 63 74 4B 65 | 01"},{"productKe [LK-030A] < 79 22 3A 22 61 31 33 46 4E 35 54 70 6C 4B 71 22 | y":"a13FNXXXXXX" [LK-030A] < 2C 22 64 65 76 69 63 65 4E 61 6D 65 22 3A 22 73 | ,"deviceName":"s [LK-030A] < 75 62 64 65 76 5F 62 61 73 69 63 5F 64 65 6D 6F | ubdev_basic_demo [LK-030A] < 5F 30 32 22 7D 2C 7B 22 70 72 6F 64 75 63 74 4B | _02"},{"productK [LK-030A] < 65 79 22 3A 22 61 31 33 46 4E 35 54 70 6C 4B 71 | ey":"a13FNXXXXXX [LK-030A] < 22 2C 22 64 65 76 69 63 65 4E 61 6D 65 22 3A 22 | ","deviceName":" [LK-030A] < 73 75 62 64 65 76 5F 62 61 73 69 63 5F 64 65 6D | subdev_basic_dem [LK-030A] < 6F 5F 30 33 22 7D 2C 7B 22 70 72 6F 64 75 63 74 | o_03"},{"product [LK-030A] < 4B 65 79 22 3A 22 61 31 33 46 4E 35 54 70 6C 4B | Key":"a13FNXXXXX [LK-030A] < 71 22 2C 22 64 65 76 69 63 65 4E 61 6D 65 22 3A | X","deviceName": [LK-030A] < 22 73 75 62 64 65 76 5F 62 61 73 69 63 5F 64 65 | "subdev_basic_de [LK-030A] < 6D 6F 5F 30 34 22 7D 5D 2C 22 69 64 22 3A 22 32 | mo_04"}],"id":"2 [LK-030A] < 22 2C 22 6D 65 73 73 61 67 65 22 3A 22 73 75 63 | ","message":"suc [LK-030A] < 63 65 73 73 22 7D | cess"} msgid : 2 code : 200 product key : a1tmc66UyDK device name : subdev_basic_demo message : success data : [{"productKey":"a13FNXXXXXX","deviceName":"subdev_basic_demo_01"},{"productKey":"a13FNXXXXXX","deviceName":"subdev_basic_demo_02"},{"productKey":"a13FNXXXXXX","deviceName":"subdev_basic_demo_03"},{"productKey":"a13FNXXXXXX","deviceName":"subdev_basic_demo_04"}]

五、时间同步

本文将为您介绍设备如何通过调用SDK获取物联网平台获取标准UTC时间,继而解析时间。

背景说明

云IoT平台提供了网络对时服务(Network Time Protocol,NTP),让设备可以基于MQTT协议从物联网平台获取标准UTC时间。
如上图流程所示,使用C-SDK中的NTP功能,可从云平台查询到当前的UTC时间(自1970年1月1日零时至今过去的毫秒数)。

API列表

以下是完整的NTP部分API列表及简要说明 (详见components/ntp/aiot_ntp_api.h)。
接口名说明
aiot_ntp_init初始化NTP实例并设置默认参数。
aiot_ntp_setopt  配置NTP实例  
aiot_ntp_deinit释放NTP实例句柄的资源。
aiot_ntp_send_request向MQTT服务器发送NTP查询请求。

API使用概述

NTP模块用于从云物联网平台上获取UTC时间,API的使用流程如下:
  1. 首先参考 aiot_mqtt_api.h 的说明,保证成功建立与物联网平台的MQTT连接。
  2. 调用 aiot_ntp_init 初始化NTP会话,获取会话句柄。
  3. 调用 aiot_ntp_setopt 配置NTP会话的参数,常用配置项见 aiot_ntp_setopt 的说明。
  4. 调用 aiot_ntp_send_request 发送NTP请求。
  5. 收到的UTC时间经SDK处理后会调用由 aiot_ntp_setopt 配置的 AIOT_NTPOPT_RECV_HANDLER 回调函数demo_ntp_recv_handler(),通知用户当前的时间。 
  6.  在demos/ntp_posix_demo.c 的demo_ntp_recv_handler()中可以看出结构体 aiot_ntp_recv_t{}中包含当前时区下,年月日时分秒的数值,可在这里把它们解析储存起来。  
下面将您详细介绍API的使用:
  • aiot_ntp_setopt
      常见的配置项如下:
AIOT_NTPOPT_MQTT_HANDLE:已建立连接的MQTT会话句柄。
AIOT_NTPOPT_TIME_ZONE:时区设置,SDK会将收到的UTC时间按配置的时区进行转换。
AIOT_NTPOPT_RECV_HANDLER:时间数据接收回调函数,SDK将UTC时间转换完成后,通过此回调函数输出。
  • aiot_ntp_send_request
发送NTP请求,然后SDK会调用通过 aiot_ntp_setopt 配置的 AIOT_NTPOPT_RECV_HANDLER 回调函数,通知用户当前的时间。

例程讲解

现对照demos/ntp_posix_demo.c例程,分步骤讲解如何使用API。
这个例程适用于Linux这类支持pthread的POSIX设备,它演示了用SDK配置MQTT参数并建立连接,之后创建2个线程。
  • 一个线程用于保活长连接。
  • 一个线程用于接收消息,并在有消息到达时进入默认的数据回调,在连接状态变化时进入事件回调。
接着在MQTT连接上发送NTP查询请求,如果云平台的回应报文到达,从接收线程会调用NTP消息处理的回调函数,把对时后的本地时间打印出来。
需要用户关注或修改的部分,已经用 TODO 在注释中标明。
1、设置设备证书
     例程使用的设备证书是公用的,所以应用于实际业务时,请替换如下的TODO部分,传入用户自己真实的设备证书。
/* TODO: 替换为自己设备的设备证书 */ char *product_key = "a13FNXXXXXX"; char *device_name = "ntp_basic_demo"; char *device_secret = "ohr94yMocVYy1ldknvAkkezKkGXXXXXX";
2、进入程序入口,给SDK配置全局的底层依赖和日志回调
     底层依赖描述了硬件平台的资源使用方式,比如怎样获取时钟,分配内存等,日志回调是用户的函数,SDK有log输出的时候会进入这个函数。
int main(int argc, char *argv[]) { ... ... /* 配置SDK的底层依赖 */ aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); /* 配置SDK的日志输出 */ aiot_state_set_logcb(demo_state_logcb);
3、给MQTT会话配置参数并建立连接
     这些连接参数包括如何建立TLS连接、服务器地址、连接后的数据及事件回调函数等,然后用 aiot_mqtt_connect()建立连接。
int32_t demo_mqtt_start(void **handle) { ... ... /* 创建1个MQTT客户端实例并内部初始化默认参数 */ mqtt_handle = aiot_mqtt_init(); ... /* 与服务器建立MQTT连接 */ res = aiot_mqtt_connect(mqtt_handle);
以下代码创建1个子线程,专门用于保活设备与云平台之间的长连接。
/* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */ g_mqtt_process_thread_running = 1; res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle);
以下代码创建1个子线程,专门用于接收从云平台推送下来的MQTT消息。
/* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */ g_mqtt_recv_thread_running = 1; res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle);
4、创建NTP会话实例并设置回应消息处理回调
    NTP服务的请求和应答是在MQTT长连接上进行的,以下代码设置了请求发送后,如果设备收到了云平台的NTP回应消息进入哪个用户回调函数。
/* 创建1个ntp客户端实例并内部初始化默认参数 */ ntp_handle = aiot_ntp_init(); if (ntp_handle == NULL) { demo_mqtt_stop(&mqtt_handle); printf("aiot_ntp_init failed\n"); return -1; } aiot_ntp_setopt(ntp_handle, AIOT_NTPOPT_MQTT_HANDLE, mqtt_handle); aiot_ntp_setopt(ntp_handle, AIOT_NTPOPT_TIME_ZONE, (int8_t *)&time_zone); /* TODO: NTP消息回应从云端到达设备时, 会进入此处设置的回调函数 */ aiot_ntp_setopt(ntp_handle, AIOT_NTPOPT_RECV_HANDLER, (void *)demo_ntp_recv_handler); aiot_ntp_setopt(ntp_handle, AIOT_NTPOPT_EVENT_HANDLER, (void *)demo_ntp_event_handler);
5、向服务器发送服务请求
使用aiot_ntp_send_request()接口,以上面设置的参数,向NTP服务器发起服务请求。
/* 发送NTP查询请求给云平台 */ res = aiot_ntp_send_request(ntp_handle); if (res < STATE_SUCCESS) { aiot_ntp_deinit(&ntp_handle); demo_mqtt_stop(&mqtt_handle); return -1; }
以下日志就对应被发送的服务请求。
[1581503394.999][LK-0309] pub: /ext/ntp/a13FNXXXXXX/ntp_basic_demo/request [LK-030A] > 7B 22 64 65 76 69 63 65 53 65 6E 64 54 69 6D 65 | {"deviceSendTime [LK-030A] > 22 3A 22 31 35 38 31 35 30 33 33 39 34 39 39 39 | ":"1581503394999 [LK-030A] > 22 7D | "}
6、从接收线程得到服务应答
在 demo_mqtt_start() 中,已开启了1个专门的接收消息线程 demo_mqtt_recv_thread,它会永无休止的调用 aiot_mqtt_recv() 来收取消息。
如果得到NTP的服务应答,那么会根据上面 AIOT_NTPOPT_RECV_HANDLER 选项的设置,进入用户侧的回调函数(以下只是把时间解析和打印出来)。
/* TODO: 数据处理回调, 当SDK从网络上收到ntp消息时被调用 */ void demo_ntp_recv_handler(void *handle, const aiot_ntp_recv_t *packet, void *userdata) { switch (packet->type) { /* TODO: 结构体 aiot_ntp_recv_t{} 中包含当前时区下, 年月日时分秒的数值, 可在这里把它们解析储存起来 */ case AIOT_NTPRECV_LOCAL_TIME: { printf("local time: %llu, %02d/%02d/%02d-%02d:%02d:%02d:%d\n", (long long unsigned int) packet->data.local_time.timestamp, packet->data.local_time.year, packet->data.local_time.mon, packet->data.local_time.day, packet->data.local_time.hour, packet->data.local_time.min, packet->data.local_time.sec, packet->data.local_time.msec); } break; ... ...
以下日志对应的是被成功接收到的服务应答和例程的解析打印。
[1581503395.011][LK-0309] pub: /ext/ntp/a13FNXXXXXX/ntp_basic_demo/response [LK-030A] < 7B 22 64 65 76 69 63 65 53 65 6E 64 54 69 6D 65 | {"deviceSendTime [LK-030A] < 22 3A 22 31 35 38 31 35 30 33 33 39 34 39 39 39 | ":"1581503394999 [LK-030A] < 22 2C 22 73 65 72 76 65 72 53 65 6E 64 54 69 6D | ","serverSendTim [LK-030A] < 65 22 3A 22 31 35 38 31 35 30 33 33 39 35 30 30 | e":"158150339500 [LK-030A] < 30 22 2C 22 73 65 72 76 65 72 52 65 63 76 54 69 | 0","serverRecvTi [LK-030A] < 6D 65 22 3A 22 31 35 38 31 35 30 33 33 39 35 30 | me":"15815033950 [LK-030A] < 30 30 22 7D | 00"} local time: 1581535795009, 2020/02/12-18:29:55:9

基于优特物联家居案例的移植与开发

背景说明
家居的硬件是STM32F407 + ucosii的系统,并且支持lwip的协议栈。因此完成设备连云有3个步骤
1、移植mbedtls库,这样才能支持mqtt的ssl加密传输
2、对csdk的HAL层进行实现,保证csdk的功能api可用,例如OTA功能,物模型功能,子设备管理功能等3
3、编写基于csdk功能api的功能验证demo
第一步的移植mbedtls不在csdk的讨论范围,可自行实现不同平台mbedtls的移植。主要对剩下的步骤进行一个讲解。
一、对csdk的HAL层进行实现
      对下面的hal进行实现,具体参考 硬件云使用手册的《基于MCU+有方N720模组》进行代码下载,源码参考位置:stm32_n720_at_mqtt_linkkit_demo\Src\V4-SDK\V4-SDK\portfiles\freertos_tcp_modem,最终实现的STM32基于ucosii的工程demo在交付件中供参考,因为家居stm32的工程下lwip暂不支持非阻塞模式socket,因此使用了阻塞模式socket和超时实现了HAL层。
     参考N720的HAL层实现原理,主要就进行了网络的适配,mbedtls的认证方式保持一致所以这块流程是不用动的。因此大部分也和N720实现一致。代码实现在工程在工程Third_Party\LinkSDK_4.x\portfiles\aiot_port\posix_port.c
//hal层回调实现 aiot_sysdep_portfile_t g_aiot_sysdep_portfile = { core_sysdep_malloc, core_sysdep_free, core_sysdep_time, core_sysdep_sleep, core_sysdep_network_init, core_sysdep_network_setopt, core_sysdep_network_establish, core_sysdep_network_recv, core_sysdep_network_send, core_sysdep_network_deinit, core_sysdep_rand, core_sysdep_mutex_init, core_sysdep_mutex_lock, core_sysdep_mutex_unlock, core_sysdep_mutex_deinit, };
二、基于csdk的功能api进行demo的验证
     代码实现在工程App\subdev_basic_demo.c,功能API的使用参考上一章的编程指南。demo功能主要实现了下面5个功能的验证。
1、验证连云
    demo_mqtt_start()函数中进行了设置mqtt的连接参数,它对应的默认消息会进入demo_mqtt_default_recv_handler回调函数中,
2、验证物模型
     demo_mqtt_start()函数中进行了物模型的创建实例,并且绑定了mqtt通道,重新设置了接收消息的回调,因此涉及到物模型的数据都会进入aiot_dm_recv_handler中,如果不是,则还是进入默认的mqtt通道demo_mqtt_default_recv_handler回调函数中。
     demo中也实现了物模型上报的功能函数供参考:demo_send_property_post(属性上报),demo_send_event_post(事件上报),demo_send_async_service_post(异步服务上报),demo_send_sync_service_post(同步服务上报)
     而物模型的下发则在物模型的接收回调aiot_dm_recv_handler中,进行数据的接收并且解析。通过判断recv->type就可以知道云端下发的数据是属于 属性,事件,服务哪一种,然后进行业务数据的解析和处理。
     下面就是关于网关接收到物模型,解析物模型获取灯属性的代码,最终获得value值就是灯的开关值,业务方在把value值赋值给硬件设备,就可以达到硬件的控制。
//判断是否是网关的数据 if(strcmp(recv->product_key,product_key) == 0){ //解析json 构造json // { // "LightSwitch":0, // "deviceId:"xx", // ... // } //属性解析 char* payloadTmp =(char*) recv->data.property_set.params; UT_cJSON * root = NULL; root = UT_cJSON_Parse(payloadTmp); if (!root){ UT_cJSON_Delete(root); return; } UT_cJSON* dataJson = UT_cJSON_GetObjectItem(root, "LightSwitch"); if(dataJson == NULL){ UT_cJSON_Delete(root); return; } //获取云端下发的开灯控制属性value int value = dataJson->valueint; //业务方可以根据value值执行开关灯操作,比如运行ledon(value) //构造json数据,属性上报 UT_cJSON* params = UT_cJSON_CreateObject(); UT_cJSON_AddItemToObject(params, "LightSwitch", UT_cJSON_CreateNumber(value)); char* data = UT_cJSON_Print(params); HAL_Printf("data = %s\r\n",data); demo_send_property_post(dm_handle,recv->product_key ,recv->device_name ,data); HAL_Free(data); UT_cJSON_Delete(root);
     同理 服务也一样。详见demo代码。
     硬件云平台的效果展示
    事件上报
属性和服务的操作
3、验证时间同步.
     demo_mqtt_start()函数中进行了ntp客户端的创建实例,并且绑定了mqtt通道,重新设置了接收消息的回调,因此涉及到NTP消息数据都会进入demo_ntp_recv_handler中,如果不是,则还是进入默认的mqtt通道demo_mqtt_default_recv_handler回调函数中。
     最终设备获取到云端下发的时间,可以进行时间的同步
switch (packet->type) { /* TODO: 结构体 aiot_ntp_recv_t{} 中包含当前时区下, 年月日时分秒的数值, 可在这里把它们解析储存起来 */ case AIOT_NTPRECV_LOCAL_TIME: { HAL_Printf("local time: %llu, %02d/%02d/%02d-%02d:%02d:%02d:%d\n", (long long unsigned int)packet->data.local_time.timestamp, packet->data.local_time.year, packet->data.local_time.mon, packet->data.local_time.day, packet->data.local_time.hour, packet->data.local_time.min, packet->data.local_time.sec, packet->data.local_time.msec); } break; default: { } }
4、验证子设备和网关管理
   main_test()函数中进行了子设备的创建和配置,拓扑关系的增加,子设备版本上报。这里注意:demo_subdev_recv_handler的回调是子设备拓扑关系等一些功能的消息,云端下发的消息还是走的mqtt的通道,和网关公用一个通道的。
//子设备初始化 subdev_handle = aiot_subdev_init(); if (subdev_handle == NULL) { HAL_Printf("aiot_subdev_init failed\n"); demo_mqtt_stop(&mqtt_handle); return -1; } //子设备配置 aiot_subdev_setopt(subdev_handle, AIOT_SUBDEVOPT_MQTT_HANDLE, mqtt_handle); aiot_subdev_setopt(subdev_handle, AIOT_SUBDEVOPT_RECV_HANDLER, demo_subdev_recv_handler); //添加拓扑关系 res = aiot_subdev_send_topo_add(subdev_handle, g_subdev, sizeof(g_subdev)/sizeof(aiot_subdev_dev_t)); if (res < STATE_SUCCESS) { HAL_Printf("aiot_subdev_send_topo_add failed, res: -0x%04X\n", -res); aiot_subdev_deinit(&subdev_handle); demo_mqtt_stop(&mqtt_handle); return -1; } osDelay(TIME_2S); //子设备登录请求 aiot_subdev_send_batch_login(subdev_handle, g_subdev, sizeof(g_subdev)/sizeof(aiot_subdev_dev_t)); if (res < STATE_SUCCESS) { HAL_Printf("aiot_subdev_send_batch_login failed, res: -0x%04X\n", -res); aiot_subdev_deinit(&subdev_handle); demo_mqtt_stop(&mqtt_handle); return -1; } //sleep(2); osDelay(TIME_2S); res = aiot_ota_report_version_ext(ota_handle, g_subdev[0].product_key,g_subdev[0].device_name,cur_sub_version); if (res < STATE_SUCCESS) { HAL_Printf("aiot_ota_report_version failed: -0x%04X\r\n", -res); } osDelay(TIME_2S);
因此在云端消息下发需要处理子设备业务的还是在aiot_dm_recv_handler回调中。
//子设备 if(strcmp(recv->product_key,g_subdev[0].product_key) == 0){ //解析json 构造json // { // "temperature":0, // "deviceId:"xx", // } //属性解析 char* payloadTmp =(char*) recv->data.property_set.params; UT_cJSON * root = NULL; root = UT_cJSON_Parse(payloadTmp); if (!root){ UT_cJSON_Delete(root); return; } UT_cJSON* dataJson = UT_cJSON_GetObjectItem(root, "temperature"); if(dataJson == NULL){ UT_cJSON_Delete(root); return; } //获取云端下发的温度属性value int value = dataJson->valueint; //业务方可以根据value值执行开关灯操作,比如运行温度temperature下发给子设备 //构造json数据,属性上报 UT_cJSON* params = UT_cJSON_CreateObject(); UT_cJSON_AddItemToObject(params, "temperature", UT_cJSON_CreateNumber(value)); char* data = UT_cJSON_Print(params); HAL_Printf("data = %s\r\n",data); demo_send_property_post(g_dm_handle,recv->product_key ,recv->device_name ,data); HAL_Free(data); UT_cJSON_Delete(root); }
5、验证OTA功能
demo_mqtt_start()函数中创建一个ota的会话,并且绑定了mqtt通道,重新设置了接收消息的回调,因此涉及到ota消息数据都会进入demo_ota_recv_handler中,在回调中在进行下载的设置,最终在下载回调里demo_download_recv_handler进行数据的获取。
注意:在下载完成后,必须要上报升级包的版本信息,这样云端才能获知设备端升级成功,如果设备端不上报,云端还是会认为升级失败。

优特云