全部产品
云市场

旧版本快速体验

更新时间:2019-03-14 16:26:32

适用于版本2.2.0, 2.2.1

快速开始

本章描述如何申请自己的设备, 并结合C-SDK快速体验该设备通过MQTT协议以topic的方式和通过物模型的编程方式, 上报和接收业务报文.

2.1 准备本地开发环境

安装Ubuntu16.04

本SDK的编译环境是64位主机上的Ubuntu16.04, 在其它Linux上尚未测试过, 所以推荐安装与阿里一致的发行版

如果您使用Windows操作系统, 建议安装虚拟机软件Virtualbox, 下载地址: https://www.virtualbox.org/wiki/Downloads

然后安装64位的Desktop版本的Ubuntu 16.04.x LTS, 下载地址: http://releases.ubuntu.com

安装必备软件

本SDK的开发编译环境使用如下软件: make-4.1, git-2.7.4, gcc-5.4.0, gcov-5.4.0, lcov-1.12, bash-4.3.48, tar-1.28, mingw-5.3.1

可使用如下命令行安装必要的软件:

  1. apt-get install -y build-essential make git gcc

2.2 准备云端控制台环境

注册/登录阿里云账号

访问阿里云:https://www.aliyun.com/, 点击[免费注册], 免费获得一个阿里云账号. 若您已有账号, 可直接登录

访问物联网套件控制台

登入之后, 鼠标悬停在产品上, 弹出层叠菜单, 并单击物联网平台

注: 本处以在物联网平台创建产品作为示例, 用户也可以在阿里云IoT的其它物联网平台, 比如生活物联网平台, 上进行产品创建

image

然后, 点击立即开通, 即可进入IoT控制台

image

2.3 体验MQTT Topic编程方式接入设备

MQTT协议是IBM开发的一个即时通讯协议, 是为大量计算能力有限, 且工作在低带宽, 不可靠的网络的远程传感器和控制设备通讯而设计的协议

利用MQTT协议是一种基于二进制消息的发布/订阅编程模式的消息协议

本节将要介绍的例子程序先在阿里云IoT平台订阅(Subscribe)一个Topic成功, 然后自己向该Topic做发布(Publish)动作

阿里云IoT平台收到被发布的消息之后, 就会将该消息原样推送回给例子程序

因为该程序之前已经通过订阅(Subscribe)动作成为该Topic的一个接收者, 发布到这个Topic上的任何消息, 都会被推送到已订阅该Topic的所有终端上

创建基础版产品和设备

进入IoT控制台后, 点击页面左侧导航栏的产品管理, 再点击右侧的创建产品, 如下图所示:

image

在弹出的创建产品中, 选择基础版(也即直接对topic进行消息发布或者订阅), 填写产品信息:

image

填写产品名称, 选择节点类型.

填写好产品信息后, 点击确认即可生成该产品:

image

点击产品右侧的查看, 可跳转到产品详情页面:

image

在该页面中, 有四个主要的选项卡:

  • 产品信息: 展示产品相关信息, 其中ProductKey用于标示产品的品类, 该产品下所有设备的ProductKey均一致
  • 消息通信: 展示产品用于上下行数据的主要Topic
  • 服务端订阅: 在这里可以选择设备消息类型并推送到MNS队列中, 用户服务端从队列里获得设备数据. 这样简化服务端订阅设备数据的流程, 让客户的服务端能够简单方便并高可靠的获得设备数据.
  • 日志服务: 此处可浏览设备的历史上下行消息

产品创建好后, 接下来可以在该产品/品类下创建设备实例了

点击产品详情页面中设备数旁的前往管理, 即可看到当前产品下的设备列表, 目前为空:

image

点击上图右侧的添加设备, 开始创建设备:

image

在填写好DeviceName后, 点击确认即可创建该设备, 生成设备的三元组:

  • ProductKey: 标识产品的品类, 相同产品的所有设备ProductKey均相同
  • DeviceName: 标识产品下的每个设备, 相同产品的所有设备DeviceName均不相同
  • DeviceSecret: 设备密钥, 每个设备均不相同

三元组用于标识阿里云上的每个设备, 用于连接阿里云服务器时完成设备认证

image

至此, 基础版产品与设备创建完成.

此外, 为了example演示, 我们在产品的消息通信中定义一个自定义的topic, 用于向服务端发送数据和从服务端接收数据:

image

编译运行基础版例程

下载解压物联网平台设备端C-SDK

以获取 V2.2.0 版本SDK为例, 可用如下命令行获取:

  1. cd ${HOME}
  2. mkdir srcs
  3. cd srcs
  4. wget https://linkkit-sdk-download.oss-cn-shanghai.aliyuncs.com/linkkit2.2.tar.gz
  5. tar xvf linkkit2.2.tar.gz

如下则在 ~/srcs/iotx-sdk-c 目录得到了解压之后的C-SDK

  1. cd iotx-sdk-c/
  2. ls
  3. aos.makefile build-rules CMakeLists.txt doc examples include LICENSE makefile make.settings prebuilt project.mk README.md src tests

填写设备三元组到例程中

打开文件 iotx-sdk-c/examples/mqtt/mqtt-example.c, 编辑如下代码段, 填入之前创建基础版产品和设备步骤中得到的设备三元组:

  1. #if defined(SUPPORT_ITLS)
  2. ...
  3. ...
  4. #else
  5. #if defined(ON_DAILY)
  6. ...
  7. ...
  8. #else
  9. #define PRODUCT_KEY "a1ExpAkj9Hi"
  10. #define DEVICE_NAME "Example1"
  11. #define DEVICE_SECRET "cNzcn2Lbqzh4UiXKLwW77hxI9GFmcRgb"
  12. #endif
  13. #endif

编译基础版例程

运行如下命令:

  1. cd ~/srcs/iotx-sdk-c
  2. make distclean
  3. make

编译成功完成后, 生成的样例程序在当前路径的 output/release/bin 目录下:

  1. $ tree output/release
  2. output/release/
  3. +-- bin
  4. ...
  5. ...
  6. | +-- mqtt-example
  7. ...
  8. ...

运行基础版例程

执行如下命令:

  1. cd ~/srcs/iotx-sdk-c
  2. ./output/release/bin/mqtt-example

得到运行结果如下:

  1. [inf] iotx_device_info_init(39): device_info created successfully!
  2. [dbg] iotx_device_info_set(49): start to set device info!
  3. [dbg] iotx_device_info_set(63): device_info set successfully!
  4. [inf] guider_print_dev_guider_info(279): ....................................................
  5. [inf] guider_print_dev_guider_info(280): ProductKey : a1ExpAkj9Hi
  6. [inf] guider_print_dev_guider_info(281): DeviceName : Example1
  7. [inf] guider_print_dev_guider_info(282): DeviceID : a1ExpAkj9Hi.Example1
  8. [inf] guider_print_dev_guider_info(284): ....................................................
  9. [inf] guider_print_dev_guider_info(285): PartnerID Buf : ,partner_id=example.demo.partner-id
  10. [inf] guider_print_dev_guider_info(286): ModuleID Buf : ,module_id=example.demo.module-id
  11. [inf] guider_print_dev_guider_info(287): Guider URL :
  12. [inf] guider_print_dev_guider_info(289): Guider SecMode : 2 (TLS + Direct)
  13. [inf] guider_print_dev_guider_info(291): Guider Timestamp : 2524608000000
  14. [inf] guider_print_dev_guider_info(292): ....................................................
  15. [inf] guider_print_dev_guider_info(298): ....................................................
  16. [inf] guider_print_conn_info(256): -----------------------------------------
  17. [inf] guider_print_conn_info(257): Host : a1ExpAkj9Hi.iot-as-mqtt.cn-shanghai.aliyuncs.com
  18. [inf] guider_print_conn_info(258): Port : 1883
  19. [inf] guider_print_conn_info(261): ClientID : a1ExpAkj9Hi.Example1|securemode=2,timestamp=2524608000000,signmethod=hmacsha1 ...
  20. [inf] guider_print_conn_info(263): TLS PubKey : 0x437636 ('-----BEGIN CERTI ...')
  21. [inf] guider_print_conn_info(266): -----------------------------------------
  22. [inf] IOT_MQTT_Construct(3005): CONFIG_MQTT_SUBTOPIC_MAXNUM : 65535
  23. [dbg] IOT_MQTT_Construct(3007): sizeof(iotx_mc_client_t) = 1573144!
  24. [inf] iotx_mc_init(2098): MQTT init success!
  25. [inf] _ssl_client_init(142): Loading the CA root certificate ...
  26. [inf] _ssl_client_init(149): ok (0 skipped)
  27. [inf] _TLSConnectNetwork(315): Connecting to /a1ExpAkj9Hi.iot-as-mqtt.cn-shanghai.aliyuncs.com/1883...
  28. [inf] mbedtls_net_connect_timeout(257): setsockopt SO_SNDTIMEO timeout: 10s
  29. [inf] mbedtls_net_connect_timeout(260): connecting IP_ADDRESS: 106.15.100.2
  30. [inf] _TLSConnectNetwork(328): ok
  31. [inf] _TLSConnectNetwork(333): . Setting up the SSL/TLS structure...
  32. [inf] _TLSConnectNetwork(343): ok
  33. [inf] _TLSConnectNetwork(382): Performing the SSL/TLS handshake...
  34. [inf] _TLSConnectNetwork(390): ok
  35. [inf] _TLSConnectNetwork(394): . Verifying peer X.509 certificate..
  36. [inf] _real_confirm(90): certificate verification result: 0x00
  37. [wrn] MQTTConnect(204): NOT USING pre-malloced buf 0xc4a010, malloc per packet
  38. [dbg] MQTTConnect(204): ALLOC: curr buf = 0xc56890, curr buf_size = 320, required payload_len = 256
  39. [dbg] MQTTConnect(224): FREED: curr buf = (nil), curr buf_size = 0
  40. [inf] iotx_mc_connect(2449): mqtt connect success!
  41. ...
  42. ...
  43. mqtt_client|309 :: packet-id=7, publish topic msg={"attr_name":"temperature", "attr_value":"1"}
  44. [dbg] iotx_mc_cycle(1591): PUBACK
  45. event_handle|132 :: publish success, packet-id=7
  46. [dbg] iotx_mc_cycle(1608): PUBLISH
  47. [dbg] iotx_mc_handle_recv_PUBLISH(1412): Packet Ident : 00035641
  48. [dbg] iotx_mc_handle_recv_PUBLISH(1413): Topic Length : 26
  49. [dbg] iotx_mc_handle_recv_PUBLISH(1417): Topic Name : /a1ExpAkj9Hi/Example1/data
  50. [dbg] iotx_mc_handle_recv_PUBLISH(1420): Payload Len/Room : 45 / 992
  51. [dbg] iotx_mc_handle_recv_PUBLISH(1421): Receive Buflen : 1024
  52. [dbg] iotx_mc_handle_recv_PUBLISH(1432): delivering msg ...
  53. [dbg] iotx_mc_deliver_message(1170): topic be matched
  54. _demo_message_arrive|166 :: ----
  55. _demo_message_arrive|167 :: packetId: 35641
  56. _demo_message_arrive|171 :: Topic: '/a1ExpAkj9Hi/Example1/data' (Length: 26)
  57. _demo_message_arrive|175 :: Payload: '{"attr_name":"temperature", "attr_value":"1"}' (Length: 45)
  58. _demo_message_arrive|176 :: ----
  59. ...
  60. ...
  61. main|361 :: out of sample!

观察基础版例程

基础版初始化说明

基础版示例代码位于 examples/mqtt/mqtt-example.c

  1. int mqtt_client(void)
  2. {
  3. ...
  4. ...

在与服务器尝试建立MQTT连接前, 需要进行设备认证

  1. if (0 != IOT_SetupConnInfo(__product_key, __device_name, __device_secret, (void **)&pconn_info)) {
  2. EXAMPLE_TRACE("AUTH request failed!");
  3. rc = -1;
  4. goto do_exit;
  5. }
  6. /* Initialize MQTT parameter */
  7. ...
  8. ...

尝试建立与服务器的MQTT连接

  1. pclient = IOT_MQTT_Construct(&mqtt_params);
  2. if (NULL == pclient) {
  3. EXAMPLE_TRACE("MQTT construct failed");
  4. rc = -1;
  5. goto do_exit;
  6. }

尝试向”/a1ExpAkj9Hi/Example1/update”这个topic上报消息

  1. ...
  2. ...
  3. topic_msg.payload = (void *)msg_pub;
  4. topic_msg.payload_len = strlen(msg_pub);
  5. rc = IOT_MQTT_Publish(pclient, TOPIC_UPDATE, &topic_msg);
  6. if (rc < 0) {
  7. IOT_MQTT_Destroy(&pclient);
  8. EXAMPLE_TRACE("error occur when publish");
  9. rc = -1;
  10. goto do_exit;
  11. }
  12. EXAMPLE_TRACE("\n publish message: \n topic: %s\n payload: \%s\n rc = %d", TOPIC_UPDATE, topic_msg.payload, rc);

订阅我们刚才在云端控制台上定义的”/a1ExpAkj9Hi/Example1/data”这个topic

  1. rc = IOT_MQTT_Subscribe(pclient, TOPIC_DATA, IOTX_MQTT_QOS1, _demo_message_arrive, NULL);
  2. if (rc < 0) {
  3. IOT_MQTT_Destroy(&pclient);
  4. EXAMPLE_TRACE("IOT_MQTT_Subscribe() failed, rc = %d", rc);
  5. rc = -1;
  6. goto do_exit;
  7. }
  8. IOT_MQTT_Yield(pclient, 200);
  9. HAL_SleepMs(2000);
  10. ...
  11. ...

尝试向”/a1ExpAkj9Hi/Example1/data”这个topic上报消息

  1. do {
  2. cnt++;
  3. msg_len = snprintf(msg_pub, sizeof(msg_pub), "{\"attr_name\":\"temperature\", \"attr_value\":\"%d\"}", cnt);
  4. ...
  5. ...
  6. rc = IOT_MQTT_Publish(pclient, TOPIC_DATA, &topic_msg);
  7. if (rc < 0) {
  8. EXAMPLE_TRACE("error occur when publish");
  9. rc = -1;
  10. break;
  11. }
  12. EXAMPLE_TRACE("packet-id=%u, publish topic msg=%s", (uint32_t)rc, msg_pub);

处理收到的MQTT消息并调用订阅时注册的回调函数

  1. IOT_MQTT_Yield(pclient, 200);
  2. /* infinite loop if running with 'loop' argument */
  3. if (user_argc >= 2 && !strcmp("loop", user_argv[1])) {
  4. HAL_SleepMs(2000);
  5. cnt = 0;
  6. }
  7. } while (cnt < 1);
  8. ...
  9. ...
  10. return rc;
  11. }

初始化日志系统, 将SDK日志级别设置为Debug

  1. int main(int argc, char **argv)
  2. {
  3. IOT_OpenLog("mqtt");
  4. IOT_SetLogLevel(IOT_LOG_DEBUG);
  5. ...
  6. ...

设置设备三元组到HAL中

  1. HAL_SetProductKey(PRODUCT_KEY);
  2. HAL_SetDeviceName(DEVICE_NAME);
  3. HAL_SetDeviceSecret(DEVICE_SECRET);
  4. ...

选择要登录的服务器

  1. int domain_type = IOTX_CLOUD_DOMAIN_SH;
  2. IOT_Ioctl(IOTX_IOCTL_SET_DOMAIN, (void *)&domain_type);

选择直连设备是否需要使用一型一密

  1. int dynamic_register = 0;
  2. IOT_Ioctl(IOTX_IOCTL_SET_DYNAMIC_REGISTER, (void *)&dynamic_register);

开始例程

  1. mqtt_client();

打印SDK内存占用信息, 仅在Linux平台上当WITH_MEM_STATS=1时有效

  1. IOT_DumpMemoryStats(IOT_LOG_DEBUG);

关闭日志系统

  1. IOT_CloseLog();
  2. EXAMPLE_TRACE("out of sample!");
  3. return 0;
  4. }

观察消息上报

在执行mqtt-example示例程序时, 如果需要改变上报值, 只需修改如下代码片段即可:

  1. examples/mqtt/mqtt-example.c
  2. ...
  3. ...
  4. do {
  5. /* Generate topic message */
  6. cnt++;
  7. msg_len = snprintf(msg_pub, sizeof(msg_pub), "{\"attr_name\":\"temperature\", \"attr_value\":\"%d\"}", cnt);
  8. if (msg_len < 0) {
  9. EXAMPLE_TRACE("Error occur! Exit program");
  10. rc = -1;
  11. break;
  12. }
  13. topic_msg.payload = (void *)msg_pub;
  14. topic_msg.payload_len = msg_len;
  15. rc = IOT_MQTT_Publish(pclient, TOPIC_DATA, &topic_msg);
  16. if (rc < 0) {
  17. EXAMPLE_TRACE("error occur when publish");
  18. rc = -1;
  19. break;
  20. }
  21. ...
  22. ...
  23. 上述代码中, msg_pub的值即为上报到topic: `/a1ExpAkj9Hi/Example1/data` 的值

如下日志信息显示样例程序正在通过MQTTPublish类型消息, 上报业务数据到/${prodcutKey}/${deviceName}/data

  1. mqtt_client|309 :: packet-id=7, publish topic msg={"attr_name":"temperature", "attr_value":"1"}

观察消息下推

当SDK收到服务器的下推信息时, 会进入如下回调函数:

  1. static void _demo_message_arrive(void *pcontext, void *pclient, iotx_mqtt_event_msg_pt msg)
  2. {
  3. iotx_mqtt_topic_info_pt ptopic_info = (iotx_mqtt_topic_info_pt) msg->msg;
  4. /* print topic name and topic message */
  5. EXAMPLE_TRACE("----");
  6. EXAMPLE_TRACE("packetId: %d", ptopic_info->packet_id);
  7. EXAMPLE_TRACE("Topic: '%.*s' (Length: %d)",
  8. ptopic_info->topic_len,
  9. ptopic_info->ptopic,
  10. ptopic_info->topic_len);
  11. EXAMPLE_TRACE("Payload: '%.*s' (Length: %d)",
  12. ptopic_info->payload_len,
  13. ptopic_info->payload,
  14. ptopic_info->payload_len);
  15. EXAMPLE_TRACE("----");
  16. }

如下日志信息显示该消息因为是到达已被订阅的Topic, 所以又被服务器原样推送到基础版例程, 并进入相应的回调函数

  1. _demo_message_arrive|166 :: ----
  2. _demo_message_arrive|167 :: packetId: 35641
  3. _demo_message_arrive|171 :: Topic: '/a1ExpAkj9Hi/Example1/data' (Length: 26)
  4. _demo_message_arrive|175 :: Payload: '{"attr_name":"temperature", "attr_value":"1"}' (Length: 45)
  5. _demo_message_arrive|176 :: ----

观察控制台日志

可以登录物联网套件控制台, 到产品管理, 找到刚才创建的产品, 点击查看, 选择日志服务选项卡, 可以看到刚才上报的消息(Message ID为7)

image

2.4 体验物模型编程方式接入设备

创建高级版产品和设备

进入IoT控制台后, 点击页面左侧导航栏的产品管理, 再点击右侧的创建产品, 如下图所示:

image

在弹出的创建产品中, 选择高级版(即物模型), 填写产品信息:

image

在此需要填写以下信息:

  • 填写产品名称
  • 选择节点类型
  • 选择设备类型, 如果没有需要的设备类型, 可以填空, 稍后在产品功能定义时再定义具体的功能
  • 选择数据格式, 目前有Alink JSON透传/自定义两种格式可选(在这里我们使用Alink JSON格式作为示例)
    • Alink JSON: 使用标准的Alink协议格式来进行物联网套件与云端的服务/属性/事件的数据交换
    • 透传/自定义: 使用物联网套件的透传接口来进行与云端的数据交换, 在云端需要用户完成透传数据与Alink格式的转换脚本
  • ID2: 是否使用ID2认证

填写好产品信息后, 点击确认即可生成该产品:

image

点击产品右侧的查看, 可跳转到产品详情页面:

image

在该页面中, 有五个主要的选项卡:

  • 产品信息: 展示产品相关信息, 其中ProductKey用于标示产品的品类, 该产品下所有设备的ProductKey均一致
  • 消息通信: 展示产品用于上下行数据的主要Topic
  • 功能定义: 在这里可以定义设备的物模型, 定义物的服务/属性/事件
  • 日志服务: 此处可浏览设备的历史上下行消息
  • 在线调试: 此处可对该产品下的设备进行在线调试, 如下发服务到设备, 设置设备的属性, 观察设备的事件上报

产品创建好后, 接下来可以创建设备了, 点击产品详情页面中设备数旁的前往管理, 即可看到当前产品下的设备列表, 目前为空:

image

点击上图右侧的添加设备, 开始创建设备:

image

在填写好DeviceName后, 点击确认即可创建该设备, 生成设备的三元组:

  • ProductKey: 标识产品的品类, 相同产品的所有设备ProductKey均相同
  • DeviceName: 标识产品下的每个设备, 相同产品的所有设备DeviceName均不相同
  • DeviceSecret: 设备密钥, 每个设备均不相同

三元组用于标识阿里云上的每个设备, 用于连接阿里云服务器时完成设备认证

image

至此, 高级版产品与设备创建完成.

为高级版设备创建物模型

在高级版物联网平台中, 用户可以将物理空间中的实体, 例如各类传感器, 或者由传感器组成的设备等进行数字化, 并在云端构建该实体的数据模型

通过定义物模型(TSL)来对设备是什么, 能做什么, 可以对外提供哪些服务进行描述, 定义好物模型(TSL)也就定义好了产品功能

稍后会展示高级版服务/属性/事件的示例程序, 所以在这里我们回到产品管理, 选择我们刚才创建的产品, 进入产品详情页, 选择功能定义选项卡:

image

选择右侧的新增, 我们先创建一个属性:

该属性是一个字符串属性, 最大长度为2048个字节

image

点击确认, 这样一个属性就创建好了

image

接下来我们创建一个服务:

关于服务的创建要注意的是, 服务只能由服务端下发, 设备端被动接收, 每个服务有自己的输入参数和输出参数, 说明如下:

  • 服务的输入参数: 指的是当从云端下发服务时, 云端向设备端发送的数据内容
  • 服务的输出参数: 指的是当设备端收到从云端下发的服务时, 如果有需要对该服务返回一些业务数据, 那么就是输出参数的内容

在这个服务中我们创建一个输入参数(标识符为transparency, 数据类型为int32)和一个输出参数(标识符为Contrastratio, 数据类型为int32)

image

最后, 再创建一个事件:

关于事件要注意的是, 事件只能由设备端主动上报给服务端, 每个事件只有输出参数, 表示从设备端需要上报的数据内容

在这个事件中我们创建一个输出参数(标识符为ErrorCode, 数据类型为enum, 取值范围为0, 1)

image

至此, 我们创建的产品中服务/属性/事件各有一个:

image

编译运行高级版例程

高级版以单品API为例演示设备服务/属性/事件的上报和下行

首先参考下载解压物联网平台设备端C-SDK一节, 下载及解压得到C-SDK的源码, 然后再执行以下环节

下载设备物模型

高级版单品的exmaple位于 iotx-sdk-c/examples/linkkit/deprecated/solo.c, 该example用到的TSL位于同个父目录下的data/solo_tsl.data.

接下来我们需要将SDK默认的设备信息更换成我们在前一章节中创建的高级版设备, 需要替换设备的三元组以及设备的物模型描述(TSL)

进入产品管理, 选择我们刚才创建的设备AdvUserExample, 进入产品详情, 选择功能定义选项卡, 这里可以看到之前定义的服务/属性和事件.

点击右侧的查看物模型, 如下图所示:

image

点击导出模型文件将物模型导出, 则可以下载到JSON格式的, 设备的物模型(TSL)描述文件, model.json

填写设备物模型到例程中

运行如下命令:

  1. make distclean
  2. make

iotx-sdk-c/output/release/bin 目录下会产生一个转义工具 linkkit_tsl_convert, 将刚才下载的物模型文件model.json拷贝到这里, 执行如下命令:

  1. $ ./linkkit_tsl_convert -i model.json

命令执行成功后会在当前目录下产生一个conv.txt文件, 这个就是转义好的物模型字符串了. (转义工具网上很多, 也可自行寻找)

conv.txt文件中的字符串替换iotx-sdk-c/examples/linkkit/data/solo_tsl.data中变量TSL_STRING的字符串即可.

image

填写设备三元组到例程中

iotx-sdk-c/examples/linkkit/deprecated/solo.c 中的三元组替换成刚才创建的设备

image

这样三元组和TSL都已换成我们刚才创建的设备.

之前我们在为高级版设备创建物模型一节中, 定义了由一个服务, 一个属性和一个事件组成的物模型(TSL)

这些与当前 deprecated/solo.c 中, 编写回调函数时对应的物模型(TSL)相同, 所以不需要对回调结构体 linkkit_ops, 及其成员回调函数做出修改.

编译高级版例程

运行如下命令:

  1. $ make distclean
  2. $ make

编译成功完成后, 生成的高级版例子程序在当前路径的 output/release/bin 目录下, 名为linkkit-example-solo

运行高级版例程

执行如下命令:

  1. cd ~/srcs/iotx-sdk-c
  2. ./output/release/bin/linkkit-example-solo

得到运行结果如下:

  1. linkkit_example|587 :: linkkit start
  2. [dbg] _dm_mgr_search_dev_by_devid(52): Device Not Found, devid: 0
  3. [inf] iotx_cm_init(77): cm verstion 0.3
  4. [inf] iotx_device_info_init(39): device_info created successfully!
  5. [dbg] iotx_device_info_set(49): start to set device info!
  6. [dbg] iotx_device_info_set(63): device_info set successfully!
  7. [inf] guider_print_dev_guider_info(279): ....................................................
  8. [inf] guider_print_dev_guider_info(280): ProductKey : a1csED27mp7
  9. [inf] guider_print_dev_guider_info(281): DeviceName : AdvExample1
  10. [inf] guider_print_dev_guider_info(282): DeviceID : a1csED27mp7.AdvExample1
  11. [inf] guider_print_dev_guider_info(284): ....................................................
  12. [inf] guider_print_dev_guider_info(285): PartnerID Buf : ,partner_id=example.demo.partner-id
  13. [inf] guider_print_dev_guider_info(286): ModuleID Buf : ,module_id=example.demo.module-id
  14. [inf] guider_print_dev_guider_info(287): Guider URL :
  15. [inf] guider_print_dev_guider_info(289): Guider SecMode : 2 (TLS + Direct)
  16. [inf] guider_print_dev_guider_info(291): Guider Timestamp : 2524608000000
  17. [inf] guider_print_dev_guider_info(292): ....................................................
  18. [inf] guider_print_dev_guider_info(298): ....................................................
  19. [inf] guider_print_conn_info(256): -----------------------------------------
  20. [inf] guider_print_conn_info(257): Host : a1csED27mp7.iot-as-mqtt.cn-shanghai.aliyuncs.com
  21. [inf] guider_print_conn_info(258): Port : 1883
  22. [inf] guider_print_conn_info(261): ClientID : a1csED27mp7.AdvExample1|securemode=2,timestamp=2524608000000,signmethod=hmacsha1 ...
  23. [inf] guider_print_conn_info(263): TLS PubKey : 0x47ffd6 ('-----BEGIN CERTI ...')
  24. [inf] guider_print_conn_info(266): -----------------------------------------
  25. [inf] iotx_cm_init(126): cm context initialize
  26. [inf] linked_list_insert(120): linked list(cm event_cb list) insert node@0x18a2760,size:1
  27. [inf] linked_list_insert(120): linked list(cm connectivity list) insert node@0x18a28c0,size:1
  28. [err] iotx_cm_add_connectivity(113): Add Connectivity Success, Type: 1
  29. [inf] iotx_cm_conn_mqtt_init(358): CONFIG_MQTT_TX_MAXLEN : 1024
  30. [inf] iotx_cm_conn_mqtt_init(359): CONFIG_MQTT_RX_MAXLEN : 5000
  31. [wrn] iotx_cm_conn_mqtt_init(369): WITH_MQTT_DYNBUF = 1, skipping malloc sendbuf of 1024 bytes
  32. [inf] IOT_MQTT_Construct(3005): CONFIG_MQTT_SUBTOPIC_MAXNUM : 65535
  33. [dbg] IOT_MQTT_Construct(3007): sizeof(iotx_mc_client_t) = 1573144!
  34. [inf] iotx_mc_init(2098): MQTT init success!
  35. [inf] _ssl_client_init(142): Loading the CA root certificate ...
  36. [inf] _ssl_client_init(149): ok (0 skipped)
  37. [inf] _TLSConnectNetwork(315): Connecting to /a1csED27mp7.iot-as-mqtt.cn-shanghai.aliyuncs.com/1883...
  38. [inf] mbedtls_net_connect_timeout(257): setsockopt SO_SNDTIMEO timeout: 10s
  39. [inf] mbedtls_net_connect_timeout(260): connecting IP_ADDRESS: 106.15.100.2
  40. [inf] _TLSConnectNetwork(328): ok
  41. [inf] _TLSConnectNetwork(333): . Setting up the SSL/TLS structure...
  42. [inf] _TLSConnectNetwork(343): ok
  43. [inf] _TLSConnectNetwork(382): Performing the SSL/TLS handshake...
  44. [inf] _TLSConnectNetwork(390): ok
  45. [inf] _TLSConnectNetwork(394): . Verifying peer X.509 certificate..
  46. [inf] _real_confirm(90): certificate verification result: 0x00
  47. [dbg] MQTTConnect(204): ALLOC: curr buf = 0x18af8f0, curr buf_size = 320, required payload_len = 256
  48. [dbg] MQTTConnect(224): FREED: curr buf = (nil), curr buf_size = 0
  49. [inf] iotx_mc_connect(2449): mqtt connect success!
  50. ...
  51. ...

观察高级版例程

高级版例程代码说明

  1. int linkkit_example()
  2. {
  3. ...
  4. ...

用户回调函数, 高级版产生事件时, 会自动调用用户的回调函数

  1. linkkit_ops_t linkkit_ops = {
  2. .on_connect = on_connect, /* connect handler */
  3. .on_disconnect = on_disconnect, /* disconnect handler */
  4. .raw_data_arrived = raw_data_arrived, /* receive raw data handler */
  5. .thing_create = thing_create, /* thing created handler */
  6. .thing_enable = thing_enable, /* thing enabled handler */
  7. .thing_disable = thing_disable, /* thing disabled handler */
  8. .thing_call_service = thing_call_service, /* self-defined service handler */
  9. .thing_prop_changed = thing_prop_changed, /* property set handler */
  10. .linkit_data_arrived = linkit_data_arrived, /* transparent transmission data handler */
  11. };
  12. EXAMPLE_TRACE("linkkit start");
  13. /*
  14. * linkkit start
  15. * max_buffered_msg = 16, set the handle msg max numbers.
  16. * if it is enough memory, this number can be set bigger.
  17. * if get_tsl_from_cloud = 0, it will use the default tsl [TSL_STRING]; if get_tsl_from_cloud =1, it will get tsl from cloud.
  18. */

启动Linkkit, 开始尝试与云端建立连接

  1. if (-1 == linkkit_start(16, get_tsl_from_cloud, linkkit_loglevel_debug, &linkkit_ops, linkkit_cloud_domain_shanghai,
  2. &sample_ctx)) {
  3. EXAMPLE_TRACE("linkkit start fail");
  4. return -1;
  5. }

如果没有选择从云端动态拉取TSL, 那么在这里本地静态设置TSL

  1. if (!get_tsl_from_cloud) {
  2. /*
  3. * if get_tsl_from_cloud = 0, set default tsl [TSL_STRING]
  4. * please modify TSL_STRING by the TSL's defined.
  5. */
  6. linkkit_set_tsl(TSL_STRING, strlen(TSL_STRING));
  7. }

初始化Config OTA的回调函数

  1. linkkit_cota_init(linkkit_cota_callback);

初始化Firmware OTA的回调函数

  1. linkkit_fota_init(linkkit_fota_callback);
  2. EXAMPLE_TRACE("linkkit enter loop");

此循环中尝试对设备的属性和事件进行上报

  1. while (1) {
  2. ...
  3. ...
  4. }

高级版SDK正常退出

  1. linkkit_end();
  2. return 0;
  3. }
  4. int main(int argc, char **argv)
  5. {

初始化日志系统, 将SDK日志级别设置为Debug

  1. IOT_OpenLog("linkkit");
  2. IOT_SetLogLevel(IOT_LOG_DEBUG);
  3. EXAMPLE_TRACE("start!\n");

设置设备三元组到HAL中

  1. HAL_SetProductKey("a13Npv1vjZ4");
  2. HAL_SetDeviceName("example_zc");
  3. HAL_SetDeviceSecret("ZlexLJ4G0aXiSwkGmUFWuZBLLySKcG8h");
  4. /*
  5. * linkkit dome
  6. * please check document: https://help.aliyun.com/document_detail/73708.html
  7. * API introduce: https://help.aliyun.com/document_detail/68687.html
  8. */
  9. linkkit_example();

打印SDK内存占用信息, 仅在Linux平台上当编译宏开关WITH_MEM_STATS=1时有效

  1. IOT_DumpMemoryStats(IOT_LOG_DEBUG);

关闭日志系统

  1. IOT_CloseLog();
  2. EXAMPLE_TRACE("out of sample!\n");
  3. return 0;
  4. }

观察属性的上报

示例中使用linkkit_post_property上报属性:

  1. /* 此函数在示例代码中每30s被调用一次 */
  2. int post_all_prop(sample_context_t *sample)
  3. {
  4. /* 这里第二个参数填NULL表示上报所有属性 */
  5. return linkkit_post_property(sample->thing, NULL, post_property_cb);
  6. }

对于属性(Property), 示例程序会每隔30s上报(Post)一次所有属性, 因为我们定义了一个属性 DeviceStatus, 所以应该看到如下日志:

  1. ```
  2. [dbg] iotx_dm_post_property_end(450): Current Property Post Payload, Length: 19, Payload: {"DeviceStatus":""}
  3. [dbg] _dm_mgr_search_dev_by_devid(46): Device Found, devid: 0
  4. [inf] dm_msg_request_all(265): DM Send Message, URI: /sys/a1csED27mp7/AdvExample1/thing/event/property/post, Payload: {"id":"1","version":"1.0","params":{"DeviceStatus":""},"method":"thing.event.property.post"}
  5. [inf] iotx_cm_conn_mqtt_publish(531): mqtt publish: topic=/sys/a1csED27mp7/AdvExample1/thing/event/property/post, topic_msg={"id":"4","version":"1.0","params":{"DeviceStatus":""},"method":"thing.event.property.post"}
  6. ```

可以看出, 由于我们尚未设置该属性的值, 所以默认该值为空字符串, 此时在物联网控制台上可以查询到刚才的上报记录:

image

从上图可以看出, 一条 Property Post 消息已上报至服务端

观察属性的设置

在高级版中, 也可从服务端主动向这个属性(Property)设置(Set)一个值

打开产品管理->产品详情->在线调试选项卡, 选择我们要调试的设备:

image

如上图所示, 选择 DeviceStatus 这个属性, 然后选择设置, 在下方的输入框中将”Hello World”填入该属性的值, 然后点击发送指令,

在示例代码中当收到属性set请求时, 会进入如下回调函数:

  1. static int thing_prop_changed(const void *thing_id, const char *property, void *ctx) {
  2. ...
  3. ...
  4. /* 这里将被设置的属性值发送到云端, 这样在云端控制台才能看到属性的新值 */
  5. response_id = linkkit_post_property(thing_id, property, post_property_cb);
  6. EXAMPLE_TRACE("post property(%s) response id: %d\n", property, response_id);
  7. }

此时在设备端的日志中可以看到从服务端set下来的值:

  1. [dbg] iotx_mc_cycle(1608): PUBLISH
  2. [dbg] iotx_mc_handle_recv_PUBLISH(1412): Packet Ident : 00000000
  3. [dbg] iotx_mc_handle_recv_PUBLISH(1413): Topic Length : 55
  4. [dbg] iotx_mc_handle_recv_PUBLISH(1417): Topic Name : /sys/a1csED27mp7/AdvExample1/thing/service/property/set
  5. [dbg] iotx_mc_handle_recv_PUBLISH(1420): Payload Len/Room : 112 / 4940
  6. [dbg] iotx_mc_handle_recv_PUBLISH(1421): Receive Buflen : 5000
  7. [dbg] iotx_mc_handle_recv_PUBLISH(1432): delivering msg ...
  8. [dbg] iotx_mc_deliver_message(1170): topic be matched
  9. [inf] iotx_cloud_conn_mqtt_event_handle(180): event_type 12
  10. [inf] iotx_cloud_conn_mqtt_event_handle(325): mqtt received: topic=/sys/a1csED27mp7/AdvExample1/thing/service/property/set, topic_msg={"method":"thing.service.property.set","id":"65822254","params":{"DeviceStatus":"HelloWorld"},"version":"1.0.0"}

这样, 一条从服务端设置属性的命令就到达设备端了

又由于在例程里, 收到这条属性的设置指令之后, 会调用 linkkit_post_property 将该属性值上报给服务端, 所以有如下的日志打印:

  1. [dbg] iotx_dm_post_property_end(450): Current Property Post Payload, Length: 29, Payload: {"DeviceStatus":"HelloWorld"}
  2. [dbg] _dm_mgr_search_dev_by_devid(46): Device Found, devid: 0
  3. [inf] dm_msg_request_all(265): DM Send Message, URI: /sys/a1csED27mp7/AdvExample1/thing/event/property/post, Payload: {"id":"34","version":"1.0","params":{"DeviceStatus":"HelloWorld"},"method":"thing.event.property.post"}
  4. [inf] iotx_cm_conn_mqtt_publish(531): mqtt publish: topic=/sys/a1csED27mp7/AdvExample1/thing/event/property/post, topic_msg={"id":"34","version":"1.0","params":{"DeviceStatus":"HelloWorld"},"method":"thing.event.property.post"}

注意: 只有当设备端主动上报某个属性的值到服务端后, 才能在服务端查询到该属性的值.

设备调试选项卡中, 选择 DeviceStatus 这个属性, 然后选择获取, 点击发送指令, 稍后在下方的输入框中就可以看到刚才设置的属性了:

image

观察事件的上报

示例中使用linkkit_post_property上报属性:

  1. /* 此函数在示例代码中每45s被调用一次 */
  2. int trigger_event(sample_context_t *sample)
  3. {
  4. char event_output_identifier[64];
  5. snprintf(event_output_identifier, sizeof(event_output_identifier), "%s.%s", EVENT_ERROR_IDENTIFIER,
  6. EVENT_ERROR_OUTPUT_INFO_IDENTIFIER);
  7. /* 设置Error事件的输出参数值 */
  8. int errorCode = 0;
  9. linkkit_set_value(linkkit_method_set_event_output_value,
  10. sample->thing,
  11. event_output_identifier,
  12. &errorCode, NULL);
  13. /* 上报Error事件到云端 */
  14. return linkkit_trigger_event(sample->thing, EVENT_ERROR_IDENTIFIER, post_property_cb);
  15. }

示例程序中 Error 事件(Event)是每45s上报一次, 日志如下:

  1. [inf] dm_msg_request_all(265): DM Send Message, URI: /sys/a1csED27mp7/AdvExample1/thing/event/Error/post, Payload: {"id":"36","version":"1.0","params":{"ErrorCode":0},"method":"thing.event.Error.post"}
  2. [inf] iotx_cm_conn_mqtt_publish(531): mqtt publish: topic=/sys/a1csED27mp7/AdvExample1/thing/event/Error/post, topic_msg={"id":"36","version":"1.0","params":{"ErrorCode":0},"method":"thing.event.Error.post"}

相应地, 在物联网控制台的日志服务选项卡中可以看到对应的信息:

image

在物联网控制台的在线调试选项卡中可以看到对应的信息:

image

观察服务的下发

在物联网控制台中打开设备调试选项卡, 选择我们创建的服务Custom

由于该服务的输入参数数据类型为int型, 标识为 transparency, 所以在下方的输入框中填入参数, 并点击发送指令:

image

在设备端示例程序中, 当收到服务调用请求时, 会进入如下回调函数:

  1. #ifdef RRPC_ENABLED
  2. static int thing_call_service(const void *thing_id, const char *service, int request_id, int rrpc, void *ctx)
  3. #else
  4. static int thing_call_service(const void *thing_id, const char *service, int request_id, void *ctx)
  5. #endif /* RRPC_ENABLED */
  6. {
  7. sample_context_t *sample_ctx = ctx;
  8. EXAMPLE_TRACE("service(%s) requested, id: thing@%p, request id:%d\n",
  9. service, thing_id, request_id);
  10. /* 这里判断收到的服务调用请求的服务ID是否是Custom, 然后执行相关逻辑 */
  11. if (strcmp(service, "Custom") == 0) {
  12. #ifdef RRPC_ENABLED
  13. handle_service_custom(sample_ctx, thing_id, service, request_id, rrpc);
  14. #else
  15. /* 在此函数中, 我们获取了服务调用的输入参数transparency的值, 将之加1赋值给输出参数Contrastratio, 然后上报给云端 */
  16. handle_service_custom(sample_ctx, thing_id, service, request_id);
  17. #endif /* RRPC_ENABLED */
  18. }
  19. return 0;
  20. }

此时在设备端可以看到如下日志:

  1. [dbg] iotx_mc_cycle(1608): PUBLISH
  2. [dbg] iotx_mc_handle_recv_PUBLISH(1412): Packet Ident : 00000000
  3. [dbg] iotx_mc_handle_recv_PUBLISH(1413): Topic Length : 49
  4. [dbg] iotx_mc_handle_recv_PUBLISH(1417): Topic Name : /sys/a1csED27mp7/AdvExample1/thing/service/Custom
  5. [dbg] iotx_mc_handle_recv_PUBLISH(1420): Payload Len/Room : 95 / 4946
  6. [dbg] iotx_mc_handle_recv_PUBLISH(1421): Receive Buflen : 5000
  7. [dbg] iotx_mc_handle_recv_PUBLISH(1432): delivering msg ...

我们可以看到, 设备端example已经收到从云端下发的服务 Custom, 其中服务的输入参数 transparency 的值为5

  1. [dbg] iotx_mc_deliver_message(1170): topic be matched
  2. [inf] iotx_cloud_conn_mqtt_event_handle(180): event_type 12
  3. [inf] iotx_cloud_conn_mqtt_event_handle(325): mqtt received: topic=/sys/a1csED27mp7/AdvExample1/thing/service/Custom, topic_msg={"method":"thing.service.Custom","id":"65850626","params":{"transparency":5},"version":"1.0.0"}
  4. [inf] iotx_cm_cloud_conn_response_callback(121): rsp_type 11
  5. [inf] iotx_cm_cloud_conn_response_handler(525): URI = /sys/a1csED27mp7/AdvExample1/thing/service/Custom{"method":"thing.service.Custom","id":"65850626","params":{"transparency":5},"version":"1.0.0"}
  6. [inf] dm_disp_event_new_data_received_handler(1289): IOTX_CM_EVENT_NEW_DATA_RECEIVED

而在example中, 当收到服务 Custom 的请求后, 进入相应的处理回调函数 handle_service_custom()

接着会将该输入参数的值+1赋给输出参数 Contrastratio, 从上面的日志中可以看到Contrastratio的值被设成了6, 并被上报给服务端.

  1. [inf] dm_cmw_topic_callback(13): DMGR TOPIC CALLBACK
  2. [inf] dm_cmw_topic_callback(20): DMGR Receive Message: /sys/a1csED27mp7/AdvExample1/thing/service/Custom{"method":"thing.service.Custom","id":"65850626","params":{"transparency":5},"version":"1.0.0"}
  3. ...
  4. ...
  5. [dbg] iotx_dm_send_service_response(597): Current Service Response Payload, Length: 19, Payload: {"Contrastratio":6}
  6. [dbg] _dm_mgr_search_dev_by_devid(46): Device Found, devid: 0
  7. [dbg] dm_mgr_upstream_thing_service_response(2098): Current Service Name: thing/service/Custom_reply
  8. [dbg] dm_msg_response_with_data(384): Send URI: /sys/a1csED27mp7/AdvExample1/thing/service/Custom_reply, Payload: {"id":"65850626","code":200,"data":{"Contrastratio":6}}
  9. [inf] iotx_cm_conn_mqtt_publish(531): mqtt publish: topic=/sys/a1csED27mp7/AdvExample1/thing/service/Custom_reply, topic_msg={"id":"65850626","code":200,"data":{"Contrastratio":6}}

关于高级版单品例程中服务/属性/事件的说明就此结束