网关产品的开发流程如下图所示:

网关产品及子设备开发指南

术语约定

  • SDK:Software Development Kit(软件开发工具包)
  • 网关设备:也叫主设备,是指可以挂载子设备的直连设备,网关具有子设备管理模块,维持子设备的拓扑关系,并且可以将拓扑关系同步到云端;
  • 子设备:不能直接连接到云端,只能通过挂接到网关上,通过网关间接连云的设备,比如使用zigbee协议联网的设备;
  • 设备三要素:在网关SDK中,将设备的能力分成三种,分别是属性、事件和服务,这三种能力简称设备的三要素;
  • 设备ID:设备句柄,在网关SDK中用于标识一个具体的设备;
  • 设备标识三元组:设备标识三元组指设备的productKey、deviceName和deviceSecret,用于在阿里IoT云中唯一标识设备;
  • 动态注册:子设备无需烧录一机一密(三元组),只需要烧录productKey,然后基于子设备原厂的唯一标识作为三元组中的deviceName,例如SN,再使用网关SDK到云端动态注册获取deviceSecret。这样,子设备就可以动态获取三元组与云端通信,并可以在云端管理子设备。
  • 静态注册:为了进一步保证子设备的安全性,厂商可以预先在平台注册设备的节点类型,并通过云端控制台或者调用云端API的方式获取三元组,然后将三元组烧录在子设备上,子设备传递已经烧录的三元组给网关进而与云端通信;
  • TSL:Thing Specification Language,基于JSON格式,用于描述设备所具备的功能和能力,详细说明请参阅《物的模型TSL》

网关开发流程

从此处下载嵌入式C Link SDK,下面的文档均以版本v2.2.1进行编写,商家后台文档的相关链接提供了智能生活开放平台的文档。

说明: 1. 商家后台定义网关产品的过程请参见前面章节的“[注册产品]”描述,创建网关产品时可以选择“所属分类”->“网络设备”中的“网关”,确保其后在“新建产品”页面的“节点类型”中选择选项“网关”,并指定该款网关支持的下联协议类型(可以多选),目前商家后台露出的下联协议包括“Zigbee”、“其它”,如果网关支持zigbee子设备接入则选择zigbee,否则选择“其它”; 2. 底色为蓝色的步骤表示该步骤中的功能完全由网关厂商自己定义与实现 3. 网关如何去发现以及连接子设备是由网关厂商与子设备厂商去协商和定义的,平台并不参与网关与子设备之间的通信过程和数据格式定义。但是当网关添加一个子设备后,需要使用平台提供的 子设备验证机制 来验证子设备的合法性; 4. 当手机APP对子设备进行控制时,命令将会通过手机发送到云端、再由云端发送到网关设备后转发给子设备。网关与子设备之间的数据格式由网关厂商和子设备厂商定义,因此网关需要进行数据格式从平台格式到厂商数据格式的转换; 5. 平台提供的网关SDK并不包含网关如何获取IP地址的功能。网关可能使用以太网、WiFi、或者蜂窝网技术(GRPS、3G、4G等)方式连接路由器,网关上如何通过DHCP获取IP地址、域名服务器地址、下一跳路由器的IP地址等功能由网关厂商实现; 6. 如果网关使用WiFi接入网络,并且网关具有串口、或者web server这样的功能可以让用户输入WiFi热点的SSID/密码,那么网关厂商可以不用适配与集成阿里的WiFi配网功能。

终端用户添加子设备的交互示意图

本节介绍用户添加子设备的整个交互示意,以让网关厂商了解子设备是添加到云端的整体过程。设备用户购买了网关和子设备之后,需要先添加网关,然后再将子设备添加到网关,之后才能对子设备进行控制。下面是网关已添加后,添加子设备的多端交互如下所示:

当用户发起子设备添加时,手机将会向云端发送PermitJoin命令,之后云端会将该命令转发给网关,此时网关需要去发现与连接子设备(参见 下文设置事件回调 中的LINKKIT_EVENT_SUBDEV_PERMITED),网关如何去发现与连接子设备是由网关厂商与子设备厂商来定义和实现的。当网关收到PermitJoin后,将发现的子设备上报到云端。

云端与网关之间 数据格式由阿里云IoT定义,网关与子设备之间的数据格式是网关厂商与子设备厂商定义,因此网关需要实现数据格式在阿里云IoT数据格式与子设备之间数据格式的转换工作

当云端添加子设备到网关之后,将会发送消息到手机端,从而让用户可以看见添加的子设备,继而对子设备进行控制。

注:

  • 如果网关没有收到来自云端的permitJoin消息,即使子设备已连接到网关,也不要向云端添加子设备;可以等到收到permitJoin之后再将已连接的子设备通知云端。
  • 如果网关产品不是在智能生活开放平台上创建,那么不需要等到接收到PermitJoin之后再向物联网平台添加子设备,因为并没有相关的手机APP会发送permitJoin给物联网平台、继而发送该PermitJoin给到网关。

网关的适配开发工作

网关的适配开发主要包括以下步骤: 1. SDK功能配置 2. 适配硬件抽象层(HAL)接口; 3. 调用初始化接口对网关SDK进行初始化; 4. 调用SDK接口创建网关设备,注册网关相关的回调函数并启动网关服务; 5. 调用SDK接口创建子设备,注册子设备相关的回调函数;

SDK功能配置

在智能生活平台上使用WiFi模组、蜂窝网模组开发产品时,SDK的功能配置、HAL接口移植大多均由模组商进行实现,因此设备商开发工作较少。对于网关产品而言,由于没有模组商的存在,网关厂商需要配置SDK具备的功能模块,配置交叉编译工具,才能编译出可以在目标硬件上运行的代码。

SDK应用于网关时的推荐配置

解压获取到的sdk包后,在主目录下有一个make.settings文件,其中定义功能模块的需求,下面是针对网关的推荐配置:

FEATURE_OTA_ENABLED          = y          # 是否打开linkkit中OTA功能的分开关
FEATURE_MQTT_COMM_ENABLED    = y          # 是否打开MQTT通道的总开关
FEATURE_MQTT_DIRECT          = y          # 是否打开MQTT直连的分开关
FEATURE_COAP_COMM_ENABLED    = n          # 是否打开CoAP通道的总开关
FEATURE_HTTP_COMM_ENABLED    = n          # 是否打开HTTP通道的总开关
FEATURE_HTTP2_COMM_ENABLED   = n          # 是否打开HTTP/2通道的总开关
FEATURE_SDK_ENHANCE          = y          # 是否打开Linkkit高级版功能的总开关
FEATURE_SUPPORT_TLS          = y          # 选择TLS安全连接的开关,此开关与iTLS开关互斥
FEATURE_SUPPORT_ITLS         = n          # 选择iTLS安全连接的开关,此开关与TLS开关互斥,ID2功能需要打开此开关
FEATURE_SUBDEVICE_ENABLED    = n          # 是否打开主子设备功能的总开关,该套接口不再建议使用
FEATURE_ALCS_ENABLED         = y          # 是否打开本地通信功能的总开关
FEATURE_ENHANCED_GATEWAY     = y          # 是否使用推荐的网关、子设备管理功能
			

交叉编译配置

请参考编程指南中的“移植指南”

HAL接口移植

SDK通过一个HAL层将SDK和硬件平台相关的代码进行隔离,从而确保SDK的实现与操作系统和硬件平台无关,HAL层接口的移植和实现请参考编程指南中的“HAL说明”。对于运行Linux操作系统的网关来说,可以参考SDK中src/ref-impl/hal/os/ubuntu的HAL实现。

注:对于很多的Linux网关来说,具备通过串口、Web页面配置WiFi热点的SSID/密码,因此可以不用去支持阿里定义的WiFi配网功能。

网关SDK初始化

网关SDK的初始化主要包括以下步骤: 1. 获取默认参数; 2. 修改默认参数; 3. 设置事件回调; 4. 调用linkkit_gateway_init()对网关SDK进行初始化。

获取默认参数

为了简化网关SDK默认参数的初始化,确保用户无需重新编译代码,只需要替换网关SDK生成的动态库即可完成设备的升级,网关SDK并没有将用于保存SDK初始化参数的linkkit_params_t结构体暴露在头文件中,该结构体实例需要通过以下接口获取:

linkkit_params_t *linkkit_gateway_get_default_params(void);
			

注意:linkkit_gateway_get_default_params()返回的默认参数用户无需释放。

修改默认参数

网关SDK默认参数的修改需要通过以下接口进行修改:

int linkkit_gateway_set_option(linkkit_params_t *params, int option, void *value, int value_len);
			

其中params为linkkit_gateway_get_default_params()返回的默认参数实例,option为需要修改的参数项目,value为option对应的值,value_len为value的字节长度。可选的option如下表所示:

option 值类型 默认值 取值范围 说明
LINKKIT_OPT_MAX_MSG_SIZE int 20480 512 - 51200 设置最大消息长度
LINKKIT_OPT_MAX_MSG_QUEUE_SIZE int 16 2 - 32 设置消息队列长度
LINKKIT_OPT_THREAD_POOL_SIZE int 4 1 - 16 用于处理消息的线程池线程数,用户注册的回调会在线程池中调用
LINKKIT_OPT_THREAD_STACK_SIZE int 8192 1024-8388608 线程池的线程栈栈大小
LINKKIT_OPT_LOG_LEVEL int 4 0-5 日志打印等级

在实际使用过程中,用户可以根据自身业务的特点和硬件的性能选择合适的参数。

设置事件回调

网关SDK通过注册事件回调的方式通知用户SDK内部状态的变化,可以通过以下接口设置网关SDK的事件处理回调:

int linkkit_gateway_set_event_callback(linkkit_params_t *params, int (*event_cb)(linkkit_event_t *ev, void *ctx), void *ctx);
			

其中params为linkkit_gateway_get_default_params()返回的默认参数实例,event_cb为需要注册的事件处理回调,ctx为用户私有数据,内部在调用event_cb时会将ctx作为event_cb的第二个参数传递给event_cb。 网关SDK使用linkkit_event_t结构体封装了事件及事件对应的数据,linkkit_event_t的声明如下所示:

typedef struct {
    int event_type; /* see LINKKIT_EVENT_XXX for more details */

    union {
        struct {
            char *productKey;
            char *deviceName;
        } subdev_deleted;

        struct {
            char *productKey;
            int   timeoutSec;
        } subdev_permited;

        struct {
            char *subdevList;
        } subdev_setup;

        struct {
            char *productKey;
            char *deviceName;
        } reset_success;
    } event_data;
} linkkit_event_t;
			

其中event_type为事件类型,event_data为事件对应的数据。event_data为union类型,网关SDK内部会根据不同的事件类型给event_data对应的成员赋值,例如,当事件类型为LINKKIT_EVENT_SUBDEV_DELETED时,用户应该使用event_datasubdev_deleted成员结构体。

网关SDK支持event_type如下表所示:

事件类型 枚举值 event_data 说明
LINKKIT_EVENT_CLOUD_DISCONNECTED 0 设备断开阿里IoT云
LINKKIT_EVENT_CLOUD_CONNECTED 1 设备连接上阿里IoT云。在连上IoT云时,应该post设备的所有属性,确保云端的影子设备与设备端同步
LINKKIT_EVENT_SUBDEV_DELETED 2 subdev_deleted 云端删除子设备,删除的子设备由event_data的subdev_deleted指定
LINKKIT_EVENT_SUBDEV_PERMITED 3 subdev_permited 允许子设备接入网关。允许接入网关的子设备由event_data的subdev_permited指定,如果product Key为NULL,则允许所有子设备接入,如果productKey不为NULL,则只允许productKey为对应值的子设备接入。timeoutSec为允许子设备接入的时间窗口。如果timeoutSec为0,则禁止子设备接入。
LINKKIT_EVENT_SUBDEV_SETUP 4 subdev_setup ZigBee网关置换专用,用户在APP端触发网关置换请求,云端通过属性设置接口设置ZigBee网络参数到新的网关,再从云端同步被置换网关子设备信息到新的网关,subdevList json字符串格式:[{"productKey":"","deviceName":""},...]
LINKKIT_EVENT_RESET_SUCCESS 5 reset_success 设备复位成功,复位成功的设备由event_data的reset_success指定

事件回调的处理流程示例代码如下所示:

static int event_cb(linkkit_event_t *ev, void *ctx)
{
    switch (ev->event_type) {
    case LINKKIT_EVENT_CLOUD_CONNECTED:
        {
            printf("cloud connected\n");
            /* TODO: 在此处处理成功连接阿里IoT云的消息 */
        }
        break;
    case LINKKIT_EVENT_CLOUD_DISCONNECTED:
        {
            printf("cloud disconnected\n");
            /* TODO: 在此处处理断开阿里IoT云的消息 */
        }
        break;
    case LINKKIT_EVENT_SUBDEV_DELETED:
        {
            char *productKey = ev->event_data.subdev_deleted.productKey;
            char *deviceName = ev->event_data.subdev_deleted.deviceName;
            printf("delete subdev %s<%s>\n", productKey, deviceName);
            /* TODO: 在此处删除productKey和deviceName指定的子设备 */
        }
        break;
    case LINKKIT_EVENT_SUBDEV_PERMITED:
        {
            char *productKey = ev->event_data.subdev_permited.productKey;
            int   timeoutSec = ev->event_data.subdev_permited.timeoutSec;
            printf("permit subdev %s in %d seconds\n", productKey, timeoutSec);
            /* TODO: 在此处调用网关的配网接口,使网关去发现以及连接子设备 */
        }
        break;
    case LINKKIT_EVENT_SUBDEV_SETUP:
        {
            char *subdevs = ev->event_data.subdev_setup.subdevList;
            printf("setup subdevs: %s\n", subdevs);
            /* TODO: 在此处调用Zigbee网关子设备pk、dn配置接口,将ZigBee设备迁移到当前网关上 */
        }
        break;
    }

    return 0;
}
			

注意linkkit_gateway_set_event_callback()需要在调用linkkit_gateway_init()前设置好事件回调,否则事件回调将无法生效。

调用linkkit_gateway_init()进行网关SDK初始化

用户在设置好初始化参数后,可以将初始化参数作为linkkit_gateway_init()的输入参数,通过调用linkkit_gateway_init()对SDK进行实际的初始化。linkkit_gateway_init()的声明如下:

int linkkit_gateway_init(linkkit_params_t *initParams)
			

注意:网关SDK中的接口除了linkkit_gateway_get_default_params()linkkit_gateway_set_event_callback()linkkit_gateway_set_option()需要在linkkit_gateway_init()前调用外,其他的接口需要在linkkit_gateway_init()调用成功后才能调用。

示例代码

网关SDK的初始化示例代码如下所示:

linkkit_params_t *initParams = linkkit_gateway_get_default_params();
if (!initParams)
    return -1;

int maxMsgSize = 40 * 1024;
linkkit_gateway_set_option(initParams, LINKKIT_OPT_MAX_MSG_SIZE, &maxMsgSize, sizeof(int));

int maxMsgQueueSize = 8;
linkkit_gateway_set_option(initParams, LINKKIT_OPT_MAX_MSG_QUEUE_SIZE, &maxMsgQueueSize, sizeof(int));

int threadPoolSize = 4;
linkkit_gateway_set_option(initParams, LINKKIT_OPT_THREAD_POOL_SIZE, &threadPoolSize, sizeof(int));

int threadStackSize = 8 * 1024;
linkkit_gateway_set_option(initParams, LINKKIT_OPT_THREAD_STACK_SIZE, &threadStackSize, sizeof(int));

linkkit_gateway_set_event_callback(initParams, event_cb, &user_data);

if (linkkit_gateway_init(initParams) < 0) {
    DPRINT("linkkit_gateway_init failed\n");
    return -1;
}
			

在上述SDK的初始化示例代码中,将最大消息大小设置为40KB,将消息队列最大长度设置为8,将线程池的最大线程数设置为4,将线程池的线程栈大小设置为8KB。注册的事件回调为event_cb,event_cb的实现请参考本文设置事件回调一节的示例代码。

网关设备创建和销毁

网关设备的创建

用户通过调用linkkit_gateway_start()创建网关设备和对网关设备进行初始化,linkkit_gateway_start()的声明如下所示:

int linkkit_gateway_start(linkkit_cbs_t *cbs, void *ctx);
			

在调用linkkit_gateway_start()的时候需要传入linkkit_cbs_t定义的一组用于操作网关设备的三要素的函数指针。linkkit_cbs_t的声明如下所示:

typedef struct {
    int (*get_property)(char *in, char *out, int out_len, void *ctx);
    int (*set_property)(char *in, void *ctx);
    int (*call_service)(char *identifier, char *in, char *out, int out_len, void *ctx);
} linkkit_cbs_t;
			

其中,云端获取设备的属性时会调用get_property指定的函数,云端设置设备的属性时会调用set_property指定的函数,云端调用设备的服务时会调用call_service指定的函数。

linkkit_gateway_start()调用成功会返回网关设备的设备ID,用户可以通过设备ID对设备进行管理。用户可以通过设备ID对网关设备进行以下操作: 1. linkkit_gateway_trigger_event_json_sync:以同步的方式上报网关设备相关的事件; 2. linkkit_gateway_post_property_json_sync:上报网关设备相关的属性变更; 3. linkkit_gateway_post_extinfos:上报网关设备相关的扩展信息; 4. linkkit_gateway_delete_extinfos:删除网关设备的扩展信息; 5. linkkit_gateway_get_devinfo:获取网关设备的设备信息和当前状态; 6. linkkit_gateway_stop:停止和销毁网关设备;

网关设备的销毁

用户可以通过以下接口销毁网关设备:

int linkkit_gateway_stop(int devid);
			

其中devid为网关设备的设备ID,该设备ID由调用linkkit_gateway_start()时返回。

注意:调用linkkit_gateway_stop()前需要销毁和解绑所有的子设备。

示例代码

网关的创建和初始化的示例代码如下所示:

static int gateway_get_property(char *in, char *out, int out_len, void *ctx)
{
    /*
     * 1.解析in,获取属性列表;
     * 2.根据属性列表获取属性列表中指定属性的值;
     * 3.创建属性-值列表并填充到out指针指定的buffer中;
     */
    return 0;
}

static int gateway_set_property(char *in, void *ctx)
{
    /*
     * 1.解析in,获取属性-值列表;
     * 2.根据属性-值列表设置网关对应的属性的值
     * 3.调用linkkit_gateway_post_property_json_sync()接口将修改后的属性-值
     */
    return 0;
}

static int gateway_call_service(char *identifier, char *in, char *out, int out_len, void *ctx){
{
    /*
     * 1.解析in,获取服务的输入参数;
     * 2.根据identifier识别云端调用的具体服务;
     * 3.调用服务;
     * 4.将服务调用的结果填充到out指针指定的buffer中;
     */
    return 0;
}

static linkkit_cbs_t alinkcbs = {
    .get_property = gateway_get_property,
    .set_property = gateway_set_property,
    .call_service = gateway_call_service,
};

int gateway_devid = linkkit_gateway_start(&alinkcbs, &user_data);
			

网关上子设备的创建和管理

子设备的初始化

子设备的初始化流程如下所示: 1. 子设备初次入网(连接到网关)之后,需要通知云端该子设备已连接到网关,创建网关和子设备的拓扑关系,该过程称为子设备注册; 2. 创建子设备,为子设备分配相关的资源,并添加到网关的子设备列表中。

子设备注册

网关设备和子设备在云端是单独创建产品,两者之间并没有联系,只有通过注册操作,才能在网关设备和子设备间建立拓扑关系。用户通过以下接口将子设备添加到网关,建立拓扑关系:

int linkkit_gateway_subdev_register(char *productKey, char *deviceName, char *deviceSecret);
			

其中productKey、deviceName和deviceSecret为子设备的设备标识三元组。linkkit_gateway_subdev_register()支持子设备的动态注册和静态注册,当使用动态注册时,deviceSecret为NULL,由云端分配deviceSecret。注:如果子设备选择动态注册,需要把子设备的DeviceName事先上传到商家后台。

设备只需要在第一次连接到网关时执行子设备注册操作,同时需要确保网关已经在线连接到阿里IoT云,因为设备注册需要验证子设备的合法性。子设备不能同时注册到两个或两个以上的网关,子设备在注册到另外一个网关时需要解除与原网关的拓扑关系。

__注意:__网关SDK并不能创建或删除子设备,子设备的创建和删除必须在云端控制台进行。只有在云端控制台预先创建好对应的网关和子设备,才能通过linkkit_gateway_subdev_register()进行注册。

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

子设备离网且需要解除子设备与网关的拓扑关系时,可以调用以下接口解除子设备和网关的拓扑关系,从而允许子设备注册到其他网关:

int linkkit_gateway_subdev_unregister(char *productKey, char *deviceName);
			

注意:调用该接口前需要销毁productKey和deviceName对应的子设备。

创建子设备

用户通过以下接口创建子设备:

int linkkit_gateway_subdev_create(char *productKey, char *deviceName, linkkit_cbs_t *cbs, void *ctx);
			

子设备创建时需要提供子设备的productKey、deviceName和用于操作设备的回调函数。

设备在创建的过程中也需要注册与网关设备类似的linkkit_cbs_t,用于传入操作子设备的三要素的函数指针。linkkit_gateway_subdev_create()在调用成功后返回子设备的设备ID,通过设备ID,用户可以调用以下接口对子设备进行操作和管理: 1. linkkit_gateway_trigger_event_json_sync:上报网关设备相关的事件; 2. linkkit_gateway_post_property_json_sync:上报网关设备相关的属性变更; 3. linkkit_gateway_post_extinfos:上报网关设备相关的扩展信息; 4. linkkit_gateway_delete_extinfos:删除网关设备的扩展信息; 5. linkkit_gateway_get_devinfo:获取网关设备的设备信息和当前状态; 6. linkkit_gateway_subdev_destroy:销毁子设备,释放子设备相关的资源; 7. linkkit_gateway_subdev_login:子设备登录,云端显示对应的子设备在线; 8. linkkit_gateway_subdev_logout:子设备登出,云端显示对应的子设备离线;

销毁子设备

可以调用以下接口销毁由linkkit_gateway_subdev_create()创建的子设备:

int linkkit_gateway_subdev_destroy(int devid);
			

该接口会释放子设备关联的各种连接资源和内存资源。

注意linkkit_gateway_subdev_destroy()只会销毁devid对应的子设备,不会解除子设备与网关的绑定关系,如果在销毁子设备后需要解除子设备与网关的绑定关系,可以调用linkkit_gateway_subdev_unregister()。如果子设备只是关机或重启等情况下临时断网,应该调用linkkit_gateway_subdev_logout()

子设备登入和登出

子设备登入

子设备创建后,子设备并不会处于在线状态,需要调用子设备的登入(login)接口,子设备才会处于在线状态,此时云端控制台也才会显示子设备处于在线状态。子设备登入接口声明如下:

int linkkit_gateway_subdev_login(int devid);
			

调用子设备login接口后,子设备会处于在线状态,云端控制态也会显示该子设备处于在线状态。子设备在线后可以调用linkkit_gateway_trigger_event_json_sync()linkkit_gateway_post_property_json_sync()等接口上报事件和属性,进行设备管理。

子设备登出

子设备在掉线后,需要调用子设备的登出接口,以提示云端控制台设备处于离线状态。子设备的登出接口声明如下:

int linkkit_gateway_subdev_logout(int devid);
			

调用子设备登出接口后,子设备会处于离线状态,云端控制态也会显示该子设备处于离线状态。

示例代码

static int light_get_property(char *in, char *out, int out_len, void *ctx)
{
    /*
     * 1.解析in,获取属性列表;
     * 2.根据属性列表获取属性列表中指定属性的值;
     * 3.创建属性-值列表并填充到out指针指定的buffer中;
     */
    return 0;
}

static int light_set_property(char *in, void *ctx)
{
    /*
     * 1.解析in,获取属性-值列表;
     * 2.根据属性-值列表设置网关对应的属性的值
     * 3.调用linkkit_gateway_post_property_json_sync()接口将修改后的属性-值
     */
    return 0;
}

static int light_call_service(char *identifier, char *in, char *out, int out_len, void *ctx)
{
    /*
     * 1.解析in,获取服务的输入参数;
     * 2.根据identifier识别云端调用的具体服务;
     * 3.调用服务;
     * 4.将服务调用的结果填充到out指针指定的buffer中;
     */
    return 0;
}

static linkkit_cbs_t light_cbs = {
    .get_property = light_get_property,
    .set_property = light_set_property,
    .call_service = light_call_service,
};

if (linkkit_gateway_subdev_register(PRODUCTKEY, DEVICENAME, DEVICESECRET) < 0)
    return -1;

int devid = linkkit_gateway_subdev_create(PRODUCTKEY, DEVICENAME, NULL, 0, &light_cbs, light);
if (devid < 0)
    return -1;

if (linkkit_gateway_subdev_login(light->devid) < 0)
    return -1;
			

事件上报

网关SDK除了支持云端调用设备的服务,也支持设备主动上报事件和属性变更。通过事件上报和属性变更上报,云端可以及时获取设备状态的变化,进而进行相应的处理。

事件的构成主要有两部分: 1. 用于标识事件类型的identifier; 2. 事件对应的的事件参数。

网关SDK事件上报的接口声明如下:

int linkkit_gateway_trigger_event_json_sync(int devid, char *identifier, char *events, int timeout_ms)
			

其中,devid为网关或子设备的设备ID,identifier为事件名,events为事件的具体参数,使用JSON格式的字符串表示,timeout_ms为事件上报的超时时间。当timeout_ms=0时,不等待云端确认。

事件上报的示例代码如下所示:

linkkit_gateway_trigger_event_json_sync(devid, "Error", "{\"ErrorCode\": 0}", 10000);
			

上述示例代码中事件的identifier为"Error",事件体为{"ErrorCode":0},超时时间为10秒。

属性上报

当设备的属性发生变化时,可以调用以下接口上报变更后的属性:

int linkkit_gateway_post_property_json_sync(int devid, char *props, int timeout_ms);
			

其中,devid为网关或子设备的设备ID,props为属性的名值对列表,使用JSON格式的字符串表示,timeout_ms为属性上报的超时时间。当timeout_ms=0时,不等待云端确认。

属性上报的示例代码如下所示:

linkkit_gateway_post_property_json_sync(devid, "{\"Temperature\": 35.8, \"Color\": \"Green\"}", 10000);
			

上述示例代码中,上报的属性有两个,分别是Temperature,对应的值为浮点数35.8,另外一个是Color,对应的值为字符串Green。属性上报的超时时间为10秒。

扩展信息上报

网关SDK支持扩展信息上报。扩展信息是用户自定义的键值对,用户可以通过云端控制台创建扩展信息,扩展信息的创建无需审核。在云端控制台创建好之后,用户可以调用以下接口上报扩展信息:

int linkkit_gateway_post_extinfos(int devid, linkkit_extinfo_t *extinfos, int nb_extinfos, int timeout_ms);
			

linkkit_gateway_post_extinfos()支持同时上报多个扩展信息键值对,键值对使用linkkit_extinfo_t表示,键值对的数目由nb_extinfos确定,timeout_ms为扩展信息上报的超时时间,以毫秒为单位。

linkkit_extinfo_t用于表示扩展信息的键值对,linkkit_extinfo_t的声明如下所示:

typedef struct {
    char *attrKey;    /* the key of extend info.   */
    char *attrValue;  /* the value of extend info. */
} linkkit_extinfo_t;
			

其中attrKey为扩展信息的键,attrValue为扩展信息的值。

扩展信息上报的示例代码如下所示:

linkkit_extinfo_t extinfos[3];
extinfos[0].attrKey = "Red";
extinfos[0].attrValue = "128";
extinfos[1].attrKey = "Green";
extinfos[1].attrValue = "64";
extinfos[2].attrKey = "Blue";
extinfos[2].attrValue = "96";

linkkit_gateway_post_extinfos(devid, extinfos, 3, 10000);
			

在上述示例代码中,创建了三个扩展信息键值对,并通过linkkit_gateway_post_extinfos()一次上报到云端,并设置超时时间为10秒,如果10秒内不能成功将扩展信息上报到云端,linkkit_gateway_post_extinfos()将返回-1。

获取设备列表

网关SDK内部维护着用户已经注册的设备列表,可以通过以下接口获取这个设备列表:

int linkkit_get_num_devices(void);
int linkkit_gateway_get_devinfos(linkkit_devinfo_t *devinfos, int nb_devinfos);
			

其中linkkit_get_num_devices()用于获取已经注册到SDK中的总设备个数,linkkit_gateway_get_devinfos()用户获取设备信息列表。linkkit_gateway_get_devinfos()包含两个参数,其中devinfos为linkkit_devinfo_t数组,用于保存获取到的设备信息列表,nb_devinfos代表devinfos数组的长度,linkkit_gateway_get_devinfos()返回值为实际返回的设备信息个数。

以下代码演示了如何获取并打印所有设备(包括网关)的设备信息:

int nb_devices = linkkit_get_num_devices();
if (nb_devices == 0)
    return 0;

linkkit_devinfo_t *devinfos = malloc(sizeof(linkkit_devinfo_t) * nb_devices);
if (!devinfos)
    return -1;

int total = linkkit_gateway_get_devinfos(devinfos, nb_devices);

int i;
for (i = 0; i < total; i++) {
    printf("device %s<%s>\n", devinfos[i].deviceName, devinfos[i].productKey);
}

free(devinfos);
			

注意事项

当手机和网关位于同一路由器时,可以使用公版APP(支持iOS和Android)的设备搜索功能搜索网关,网关通过端口号为5683的UDP端口来接收APP多播的设备发现消息,因此网关需要开放端口号为5683的UDP端口。在Linux系统中可以使用iptables命令开放该端口,命令如下所示:

iptables -I INPUT 1 -p udp --dport 5683 -j ACCEPT
			

示例代码

SDK中的示例文件examples/linkkit/linkkit_example_gateway.c中实现了网关的创建,以及子设备添加、删除、释放资源的过程,厂商可以参考该代码实现自己的产品。

子设备产品开发

子设备产品开发流程

说明: 1. 每个子设备也需要在智能生活平台上创建一个产品,并定义该产品支持的属性、事件、服务; 2. 子设备产品的注册方式需要设置为“动态注册” 3. 每个子设备都需要一个有效且唯一的阿里IoT激活码,目前该激活码不需要烧写到子设备上,但是子设备厂商需要为每个产品申请激活码 4. 子设备的激活码的DeviceName需要由子设备厂商指定,并上传到智能生活开放平台

注意事项

当网关产品在智能生活开放平台创见时:

  • 只有网关的Owner可以发起子设备的添加操作
  • 网关的Owner把网关分享给用户B,用户B并不能添加子设备
  • 网关的Owner把网关分享给用户B,网关连接的子设备并没有分享给B,如果用户B需要控制子设备,需要Owner把子设备单独分享给B
  • 当网关的Owner解绑子设备之后,子设备与网关的拓扑关系也会被解除,该子设备需要重新发起子设备添加过程来添加到一个网关

其它限制

目前一个网关最多可以添加的子设备的数量限制为200。