
# C Demo使用说明
#### 概述
本文以C语言为例，介绍通过MQTTS/MQTT协议接入平台，基于[平台接口](https://support.huaweicloud.com/api-iothub/iot_06_v5_3002.html)实现"属性上报"、"订阅接收命令"等功能。
![](https://support.huaweicloud.com/devg-iothub/public_sys-resources/note_3.0-zh-cn.png)
本文中使用的代码为样例代码，仅用于体验平台通信功能，如需进行商用，可以参考[资源获取](https://support.huaweicloud.com/devg-iothub/iot_02_1004.html)获取对应语言的IoT Device SDK进行集成。
#### 前提条件
- 环境要求：Linux操作系统上，并安装好gcc（建议4.8及以上版本）。
- 库依赖：openssl库（MQTTS需要），paho库。
- 已在[管理控制台](https://console.huaweicloud.com/iotdm/#/dm-portal/home)获取设备接入地址。获取地址的操作步骤，请参考[平台对接信息](https://support.huaweicloud.com/devg-iothub/iot_02_1004.html#ZH-CN_TOPIC_0187644975__section436212919379)。
- 已在[管理控制台](https://console.huaweicloud.com/iotdm/#/dm-portal/home)创建产品和设备。创建产品和设备的具体操作细节，请参考[创建产品](https://support.huaweicloud.com/usermanual-iothub/iot_01_0054.html)、[注册单个设备](https://support.huaweicloud.com/usermanual-iothub/iot_01_0031.html)或[批量注册设备](https://support.huaweicloud.com/usermanual-iothub/iot_01_0032.html)。
 
#### 准备工作
- 编译openssl库
  1. 访问openssl官网（<https://www.openssl.org/source/>）下载最新版本openssl（如openssl-1.1.1d.tar.gz），上传到linux编译机上（以上传到/home/test目录下为例），并使用如下命令解压：
     ```
     tar -zxvf openssl-1.1.1d.tar.gz
     ```
     
  
  2. 配置生成makefile文件。
     执行以下命令进入openssl源码目录
     ```
     cd openssl-1.1.1d
     ```
     运行如下配置命令：
     ```
     ./config shared --prefix=/home/test/openssl --openssldir=/home/test/openssl/ssl
     ```
     其中"prefix"是安装目录，"openssldir"是配置文件目录，"shared"作用是生成动态链接库（即.so库） 。
     如果编译有问题，配置命令加上no-asm（表示不使用汇编代码）
     ./config no-asm shared --prefix=/home/test/openssl --openssldir=/home/test/openssl/ssl
     ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0246050100.png "点击放大")
     
  
  3. 编译出库。 在openssl源码目录下，运行make depend命令。
     ```
     make depend
     ```
     再运行make命令进行编译。
     ```
     make
     ```
     安装openssl。
     ```
     make install
     ```
     在配置的openssl安装目录下home/test/openssl找到lib目录，有生成的库文件：
     "libcrypto.so.1.1"、"libssl.so.1.1"和软链接"libcrypto.so"、"libssl.so"，请将这些文件复制到demo的lib文件夹下（同时将/home/test/openssl/include/openssl里的内容复制到demo的include/openssl下）。
     ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0246050613.png "点击放大")
     **注**：有的编译工具是32位的，如果在64位的linux机器上使用，这时只要将Makefile中的-m64都删除，再进行编译即可。
     
   
- 编译paho库文件
  1. 访问github下载地址：<https://github.com/eclipse/paho.mqtt.c>，下载paho.mqtt.c源码。
  
  2. 解压后上传到linux编译机。
  
  3. 修改makefile
     1. 通过如下命令进行编辑Makefile
        ```
        vim Makefile
        ```
        
     
     2. 查找字符串
        ```
        /DOXYGEN_COMMAND =
        ```
        
     
     3. 在/DOXYGEN_COMMAND =doxygen的下一行添加下面两行（自定义的openssl的头文件和库文件）
        ```
        CFLAGS += -I/home/test/openssl/include
        LDFLAGS += -L/home/test/openssl/lib -lrt
        ```
        ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0246050828.png "点击放大")
        
     
     4. 把如下图的CCFLAGS_SO、LDFLAGS_CS、LDFLAGS_AS、FLAGS_EXES的openssl地址都改成对应的地址 ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0246050829.png "点击放大")
        
      
  
  4. 编译
     1. 执行清空命令
        ```
        make clean
        ```
        
     
     2. 执行编译命令
        ```
        make
        ```
        
      
  
  5. 编译完成后，可以在build/output目录下看到编译成功的库。 ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0246050931.png "点击放大")
     
  
  6. 复制paho库文件。 当前SDK仅用到了libpaho-mqtt3as，请将"libpaho-mqtt3as.so"和"libpaho-mqtt3as.so.1"文件复制到demo的lib文件夹下。（同时回到paho源代码路径，进入src目录，将MQTTAsync.h、MQTTClient.h、MQTTClientPersistence.h、MQTTProperties.h、MQTTReasonCodes.h、MQTTSubscribeOpts.h复制到demo的include/base文件夹下）。
     ![](https://support.huaweicloud.com/devg-iothub/public_sys-resources/note_3.0-zh-cn.png)
     有的paho版本会有MQTTExportDeclarations.h头文件，建议可以将MQTT相关的头文件都添加进去。
     
   
 
#### 导入代码样例
1. 下载[quickStart(C)](https://developer.obs.cn-north-4.myhuaweicloud.com:443/manage/south/quickStart/quickStart(c).zip)样例。
2. 将代码复制到linux运行环境中。可以看到代码文件层级如下图。
   
   ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0246051045.png "点击放大")
   
   **代码目录简述：**
   - **src** **：** **源码目录**
     mqtt_c_demo：demo核心源码；
     util/string_util.c：工具资源文件；
     
   
   - **conf：** **证书目录**
     rootcert.pem：设备校验平台身份的证书，用于设备侧接入物联网平台登录鉴权使用；如果对接的IoTDA版本非基础版，请将该[证书文件](https://iot-developer.obs.cn-north-4.myhuaweicloud.com/cert/v2/ap-southeast-1-deviceCert-standard-instance.zip)中c/ap-southeast-1-device-client-rootcert.pem文件内容复制到conf/rootcert.pem文件中。
     
   
   - **include: 头文件目录**
     base目录：存放依赖的paho头文件
     openssl目录：存放依赖的openssl头文件
     util目录：存放依赖的工具资源头文件
     
   
   - **lib：依赖库文件**
     libcrypto.so\*/libssl.so\*: openssl库文件
     libpaho-mqtt3as.so\*: paho库文件
     
   
   - **Makefile** **：Makefile文件**
   
   
   
   
 
#### 建立连接
设备或网关在接入物联网平台时首先需要和平台建立连接，从而将设备或网关与平台进行关联。开发者通过传入设备信息，将设备或网关连接到物联网平台。
1. 设置参数。
   ```
   char *uri = "ssl://iot-mqtts.cn-north-4.myhuaweicloud.com:8883";
   int port = 8883;
   char *username = "********"; //deviceId
   char *password = "********";
   ```
   注意：MQTTS为8883端口接入，如果使用MQTT协议接入，url为：tcp://域名空间:1883， port为1883，其中域名空间参考[平台对接信息](https://support.huaweicloud.com/devg-iothub/iot_02_1004.html#ZH-CN_TOPIC_0187644975__section436212919379)获取。心跳时间默认设置为120秒，用户如果想修改，可以修改代码中的"keepAliveInterval "参数，心跳时间范围具体可参考[使用限制](https://support.huaweicloud.com/devg-iothub/iot_02_2200.html#ZH-CN_TOPIC_0246021145__section1565109112518)。
   
2. 连接。
   - 在Makefile的第15行最后添加 -lm ，执行make进行编译。如果是32位的操作系统，请删除Makefile中的"-m64"。
   
   - 执行export LD_LIBRARY_PATH=./lib/加载库文件。
   
   - 运行./MQTT_Demo.o。
     ```
     //connect
     int ret = mqtt_connect();
     if (ret != 0) {
     printf("connect failed, result %d\n", ret);
     }
     ```
     
    
3. 连接成功后，打印"connect success"，同时在控制台可看到设备已在线。 ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0246249374.png "点击放大")
   图1设备列表-设备在线   
   ![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0000001950367902.png "点击放大")
   **注：如果连接失败，在mqtt_connect_failure函数中已实现退避重连，代码样例如下：**
   ```
   void mqtt_connect_failure(void *context, MQTTAsync_failureData *response) {
   retryTimes++;
   printf("connect failed: messageId %d, code %d, message %s\n", response->token, response->code, response->message);
   //退避重连
   int lowBound =  defaultBackoff * 0.8;
   int highBound = defaultBackoff * 1.2;
   int randomBackOff = rand() % (highBound - lowBound + 1);
   long backOffWithJitter = (int)(pow(2.0, (double)retryTimes) - 1) * (randomBackOff + lowBound);
   long waitTImeUntilNextRetry = (int)(minBackoff + backOffWithJitter) > maxBackoff ? (minBackoff + backOffWithJitter) : maxBackoff;
   TimeSleep(waitTImeUntilNextRetry);
   //connect
   int ret = mqtt_connect();
   if (ret != 0) {
   printf("connect failed, result %d\n", ret);
   }
   }
   ```
   
 
#### 订阅Topic
订阅某Topic的设备才能接收broker发布的关于该Topic的消息，关于平台预置Topic可参考[Topic定义](https://support.huaweicloud.com/api-iothub/iot_06_v5_3004.html)。
订阅Topic：
```
//subscribe
char *cmd_topic = combine_strings(3, "$oc/devices/", username, "/sys/commands/#");
ret = mqtt_subscribe(cmd_topic);
free(cmd_topic);
cmd_topic = NULL;
if (ret < 0) {
printf("subscribe topic error, result %d\n", ret);
}
```
订阅成功后，demo中会打印"subscribe success"字样。
#### 属性上报
属性上报是指设备主动向平台上报自己的属性，更多信息请参考[设备属性上报](https://support.huaweicloud.com/api-iothub/iot_06_v5_3010.html)。
```
//publish data
char *payload = "{\"services\":[{\"service_id\":\"parameter\",\"properties\":{\"Load\":\"123\",\"ImbA_strVal\":\"456\"}}]}";
char *report_topic = combine_strings(3, "$oc/devices/", username, "/sys/properties/report");
ret = mqtt_publish(report_topic, payload);
free(report_topic);
report_topic = NULL;
if (ret < 0) {
printf("publish data error, result %d\n", ret);
}
```
设备上报属性成功后，demo中会打印"publish success"字样。
同时在"设备详情"页面查看到上报的属性：
图2查看上报数据-parameter   
![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0000001981551833.png "点击放大")
![](https://support.huaweicloud.com/devg-iothub/public_sys-resources/note_3.0-zh-cn.png)
如果在"设备详情"页面没有最新上报数据，请确认设备上报的服务/属性和产品模型中的服务/属性一致。
#### 接收下发命令
订阅了命令Topic后，可以在控制台下发同步命令。详情请参考[MQTT设备同步命令下发](https://support.huaweicloud.com/usermanual-iothub/iot_01_0339.html)。
命令下发后，demo中接收到命令：
![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0246255063.png "点击放大")
demo中接收命令的代码为：
```
//receive message from the server
int mqtt_message_arrive(void *context, char *topicName, int topicLen, MQTTAsync_message *message) {
printf( "mqtt_message_arrive() success, the topic is %s, the payload is %s \n", topicName, message->payload);
return 1; //can not return 0 here, otherwise the message won't update or something wrong would happen
}
```
![](https://support.huaweicloud.com/devg-iothub/public_sys-resources/note_3.0-zh-cn.png)
由于是同步命令需要端侧回复响应可[参考接口](https://support.huaweicloud.com/api-iothub/iot_06_v5_3014.html#section4)。
