本文讲解如何移植Link SDK 3.0.1到Windows操作系统,并在Windows下实现一个虚拟的灯连接到阿里云飞燕平台(生活物联网平台)。本文的示例将涉及以下功能点:

  • 使用MQTT接入阿里云飞燕平台,MQTT连接使用TLS加密
  • 使用一机一密方式对设备进行认证
  • 通过物模型实现一个灯的功能
  • 如何实现相关HAL
  • 使用飞燕调试APP绑定设备并进行控制

开发环境描述

操作系统版本 Win7
IDE Eclipse IDE for C/C++ Developers,版本: 2019-03 (4.11.0)
包管理工具 MSYS2
Toolchain mingw-w64-x86_64-toolchain

示例灯功能描述

示例程序ilop_demo_light.c用于在Windows上模拟一个灯,设备厂商可以将该文件下载后作为参考,该灯的功能如下:

  • 灯的状态默认为关闭状态
  • 灯连接到阿里云物联网平台后,将会把灯的状态上报到云端
  • 灯将会接收来自飞燕的控制命令,对灯进行打开或者关闭的操作,并将灯的最新状态上报云端
  • 在灯上创建了一个线程用于监控灯的温度,如果灯温度大于或者等于80度则报警,告诉云端灯的温度过高。灯的温度值初始为83度,每10秒降低一度,当温度小于80度后,告知云端设备运行正常。

云端操作

  • 创建以太网产品。
  • 创建调试设备。

设备端开发过程

SDK获取

获取Link SDK3.0.1代码。

SDK配置

因为不同的SDK功能依赖的HAL不一样,为了减少设备开发时对接HAL的数量,开发者只需要选中需要支持的功能即可。

运行SDK根目录下的config.bat,进行SDK功能配置:

请确保以下配置被选中:

  • PLATFORM_HAS_STDINT
  • PLATFORM_HAS_DYNMEM
  • PLATFORM_HAS_OS
  • FEATURE_INTRA_NETWORK_PAYLOAD
  • FEATURE_INTRA_LOG
    • MUTE_LEVEL_of_FLOW
    • MUTE_LEVEL_of_DEBUG
    • MUTE_LEVEL_of_INFO
    • MUTE_LEVEL_of_WARNING
    • MUTE_LEVEL_of_ERROR
    • MUTE_LEVEL_of_CRIT
  • FEATURE_MQTT_COMM_ENABLED
    • FEATURE_MQTT_DEFAULT_IMPL
    • FEATURE_MQTT_DIRECT
  • FEATURE_DEVICE_MODEL_ENABLED
  • FEATURE_SUPPORT_TLS
  • FEATURE_DEV_RESET
  • FEATURE_DEV_BIND_ENABLED

功能配置之后,选择“Exit”,并进行配置保存

SDK代码抽取

运行SDK根目录下的extract.bat,将选中功能的代码抽取出来,如下图所示:

抽取出来的代码将放置在SDK的output目录下。

Eclipse工程创建与配置

在Eclipse IDE中创建一个Eclipse C工程:

点击Next按钮后,对工程进行命名,设置Toolchains,如下图所示:

点击Finish按钮,完成工程创建后,右键单击工程显示工程菜单:

点击“Import”按钮后跳出下面的“Import”窗口:

在“General”中选择“File System”,然后点击“Next”按钮,并将output\eng下面的所有目录选中,如下图所示:

点击“Finish”按钮后,Eclipse将把SDK的相关代码复制到Eclipse的工程下面。请使用同样的方法将下载的文件ilop_demo_light.c导入工程,文件添加结束之后的工程如下所示:

在工程上右键单击显示菜单,并点击“Properties”按钮:

点击“Properties”按钮之后,将头文件所在目录进行指定,请参照下面的图示将相关目录添加到includes:

配置完成之后,并点击窗口下方的“Apply”按钮保存配置。

继续点击“Settings”窗口中的“Libraries”菜单,对需要使用到的库进行设置,如下图所示:

配置完成后点击窗口下方的“Apply and Close”结束配置

HAL实现

wrapper.c里面包含SDK运行时需要目标平台进行支持的HAL函数,这些函数需要开发者进行实现

这里是该wrapper.c中HAL的参考实现,开发者可以将该文件下载之后将工程中wrapper.c的内容进行替换。下面是替换之后仍然需要用户修改或者注意的地方:

设备身份获取

参考实现的wrapper.c中函数HAL_GetProductKey()、HAL_GetProductSecret()、HAL_GetDeviceName()、HAL_GetDeviceSecret()直接返回了调试设备的上述身份信息,设备厂商需要对这几个函数进行重新实现。

比如在产线上为了快速的将设备证书(ProductKey、DeviceName、DeviceSecret)到设备,产线可以将设备的身份信息加密之后写入到某个文件并烧写到设备的文件系统,设备上电之后需要使用设备厂商定义的解密方法从这个文件中将设备的身份信息读取出来,因此设备厂商的就需要重新实现上述HAL从加密文件中读取指定内容。

网络接口指定

设备可能具备多个网络接口,参考实现中的函数HAL_Wifi_Get_IP()、HAL_Wifi_Get_Mac()从指定的一个网络接口去读取IP地址和MAC地址,因此设备厂商需要将wrapper.c中的接口名修改为设备的接口名:

static wchar_t* gIfName = L"\\DEVICE\\TCPIP_{C9E93150-C6B1-4B15-8A2E-C08747261CD2}";
            

KV文件指定

SDK在运行的时候有些配置会进行保存,将会调用函数HAL_Kv_Set()、HAL_Kv_Get()进行配置的保存和读取。由于需要保存的数据并不多,在参考实现中配置是按照一行保存一个配置的方式进行实现,并写入到了下面定义的文件:

static char *kvFileName="c:\\iotKv.data";
            

设备厂商需要将kvFileName指向一个非易失文件系统中的文件,也即设备重启该文件中的内容不会丢失。

示例产品代码说明

本节对ilop_demo_light.c中的关键代码进行说明。

主程序说明

在main()函数中:

  • 注册事件处理函数
  • 调用IOT_Linkkit_Open()创建设备ID;
  • 调用IOT_Linkkit_Connect()连接阿里云;
  • 创建一个while(1)循环,在其中调用IOT_Linkkit_Yield去接收来自云端的数据

其中添加事件处理函数的语句如下:

/* Register Callback for events*/
    IOT_RegisterCallback(ITE_CONNECT_SUCC, user_connected_event_handler);
    IOT_RegisterCallback(ITE_DISCONNECTED, user_disconnected_event_handler);
    IOT_RegisterCallback(ITE_SERVICE_REQUEST, user_service_request_event_handler);
    IOT_RegisterCallback(ITE_PROPERTY_SET, user_property_set_event_handler);
    IOT_RegisterCallback(ITE_REPORT_REPLY, user_report_reply_event_handler);
    IOT_RegisterCallback(ITE_TRIGGER_EVENT_REPLY, user_trigger_event_reply_event_handler);
    IOT_RegisterCallback(ITE_TIMESTAMP_REPLY, user_timestamp_reply_event_handler);
    IOT_RegisterCallback(ITE_INITIALIZE_COMPLETED, user_initialized);
    IOT_RegisterCallback(ITE_FOTA, user_fota_event_handler);
    IOT_RegisterCallback(ITE_COTA, user_cota_event_handler);
            

上面的代码中将常见的事件都进行了注册,厂商在开发时只需要对相应的函数体进行修改即可。

下面是创建设备ID以及连接云端的代码片段:

需要注意的是:

  • IOT_Linkkit_Connect()如果返回失败,SDK不会再次尝试连接云端,因此上图的第二个橙色框选部分,如果失败了之后会每过一秒再次调用连接函数来确保连接成功
  • 如果IOT_Linkkit_Connect()成功后,设备与云端的连接出现异常断开,那么SDK会自动尝试与云端重新建立连接,在这种情况下厂商无需调用IOT_Linkkit_Connect()去连接物联网平台
  • 第三个橙色圈选部分,调用了IOT_Linkkit_Yield()去收包、以及是否需要与云端保持激活等公牛,HAL_SleepMs(200)是指每次调用IOT_Linkkit_Yield()之后等待200毫秒,如果一个设备的数据收发量很大,那么可以缩短HAL_SleepMs()等待的时间
  • 如果一个设备与云端的数据收发量非常大,通过缩短HAL_SleepMs()等待间隔导致设备功耗很大或者效率不高,可以参考SDK的同步与异步通信中描述的异步通信方式进行数据收发。

设备连接到云端后的处理

在示例代码中,当设备通过认证并连接到云端后会调用回调函数user_connected_event_handler,该函数的内容如下:

其中user_post_property()用于上报设备的各个属性的最新状态到云端,厂商在开发产品时需要根据自己的产品具有的属性来对该函数进行修改。

示例代码当设备连接到云端之后,还启动了一个线程用于周期的上报事件,厂商在实际产品开发时,应该在该线程中监控设备是否有属性发生了变化(比如用户手动进行开灯、关灯操作),如果有属性发生变化时将变化了的属性上报云端;同时该线程还应检测设备是否工作异常,如果异常需要将异常通过事件上报云端

属性上报的代码说明

在示例代码中调用函数user_post_property()进行了属性上报,函数代码如下所示:

其中IOT_Linkkit_Report()用于向云端发送数据,其中参数ITM_MSG_POST_PROPERTY表示上报的是设备属性,property_payload是上报的数据的内容。

属性是使用JSON格式进行描述的,由于上报的属性比较简单,并没有调用CJSON来组装JSON对象,而是直接使用了snprintf对灯的状态进行了格式化。

属性设置的处理

用户可以通过飞燕的APP或者云端控制台对属性进行设置,然后云端会将该设置命令发送给设备进行处理,在示例代码中调用了函数user_property_set_event_handler()对属性设置进行处理,代码如下所示:

在属性处理函数中,调用了CJSON的函数cJSON_GetObjectItem(root,"LightSwitch")来判断设置的是否是灯的开关。注意,云端可能同时对多个属性进行设置,所以厂商在实现时需要遍历设备支持的属性来确定用户设置的是哪个或者哪几个属性。

在示例代码中发现设置的是灯的状态时,仅仅是把设置的状态保存到设备的一个变量中,如上图的第二个橙色圈选部分。在实际产品开发时,厂商应该去操作实际的硬件去进行灯的打开或者关闭。

在示例代码的最后,调用了user_post_property()将设备的最新状态上报云端。**设备厂商需要注意的是**,当设备的属性发生了变化时,即使该变化是由于云端操作导致,也需要将设备的最新状态上报到云端,否则在云端显示的仍然是上一次设备上报的属性值。

如果某个属性的操作非常耗时,或者可能导致阻塞,设备厂商可以创建一个线程去进行属性设置。

事件上报

示例代码中调用了函数()进行事件上报,函数体代码如下所示:

与属性上报不同,事件上报时需要指定事件ID(如上图中圈选的event_id),然后构造event的内容(如上图中圈选的event_payload的构造语句),然后再调用IOT_Linkkit_TriggerEvent()上报事件。

设备离线处理

示例代码对设备与云端连接断开事件注册了回调函数user_disconnected_event_handler,在示例代码中仅仅只是进行了打印,厂商实际开发时需要按照自己产品的业务逻辑对该函数进行重写:

编译与调试

设备端

右键单击项目显示菜单,并点击其中的“Build Project”,如下图所示:

编译成功之后,在Eclipse之中将项目以“Local C/C++ Application”运行,下面是正确运行时的console中的显示:

其中:“HAL_SSL_Establish successfully”表示设备与阿里云物联网平台的SSL连接已成功建立。

如果设备认证成功,将会有如下的输出:

灯连接到云端之后会将灯的状态上报云端,其中的“LightSwitch”为0表示灯的状态为“关”:

> {

>     "id": "1",

>     "version": "1.0",

>     "params": {

>         "LightSwitch":  0

>     },

>     "method": "thing.event.property.post"

> }
            

然后每隔10秒,测试灯将会上报事件到云端:

> {

>     "id": "2",

>     "version": "1.0",

>     "params": {

>         "ErrorCode": 1

>     },

>     "method": "thing.event.Error.post"

> }
            

其中的ErrorCode为1表示温度超过80度的报警,为0表示告警消失

物联网平台端

开发者也可以在飞燕平台上查看设备的运行状态,如果设备已正常连接云端,那么云端显示如下图所示:

其中可以看见设备的状态为“在线”,设备的“主灯开关”的状态为“关闭”

手机APP端

下载飞燕的调试APP

飞燕的APP名叫“云智能”,请访问自有品牌APP和公版APP页面,扫描该页面中提供的二维码进行开发版app下载和安装,如下图橙色圈选的二维码:

注:请不要扫描本页面中的“开发版”二维码,而是扫描“自有品牌APP和公版APP”页面中的二维码。

使用调试APP绑定设备

确保设备已经正确的连接到了物联网平台,将手机连接到设备连接的同一个局域网,然后打开开发版“云智能”APP,如果一切正常,手机的页面将会如下图所示:

注意:上图右上角的“+”号上方的红点表示手机APP发现了设备。

点击按钮“添加设备”,会打开设备添加页面,如果设备端代码工作正常,将会显示发现的设备的品牌、名称、型号以及设备的DeviceName,如下图所示:

然后点击发现的设备右边的“+”号,设备添加成功之后将会出现设备的控制面板,如下图所示:

使用调试APP控制设备

用户可以在手机APP的面板上点击“开灯”将灯打开,也可以对已经打开的灯点击“关灯”进行关闭。在设备端的串口上,当打开灯时会出现如下所示的信息:

1;32;40muser_property_set_event_handler.123: Property Set Received, Request: {"LightSwitch":1}
> {
>     "id": "8",
>     "version": "1.0",
>     "params": {
>         "LightSwitch":  1
>     },
>     "method": "thing.event.property.post"
> }
            

上面的打印信息中“LightSwitch”:1 表示设置灯的状态为开。

产品发布

当产品功能运行正常之后,厂商可以将产品在飞燕的控制台对产品进行发布,然后为每台需要生产的设备申请激活码。

文档参考

  • 设备Reset

当一个设备被一个用户绑定之后,在默认的绑定方式下另外一个用户是不能再对这个设备进行绑定和控制的,必须原用户对设备进行解绑。有时候一个设备可能被转卖给另外一个客户之后,设备的拥有者已经无法联系都设备曾经的拥有者,此时设备端调用IOT_DevReset_Report()可以让飞燕平台解除这个设备与原拥有者的绑定关系。

因此,设备商需要在产品上设计一个reset按键,并当用户触按按键时调用IOT_DevReset_Report()通知飞燕平台进行设备解绑,请参考“[设备Reset](https://help.aliyun.com/document_detail/100494.html)”了解API调用详情。

  • 设备固件升级

请参考“OTA开发”了解如何进行设备固件升级设计。