本文以C Link SDK中的Demo文件./demos/subdev_basic_demo.c为例,介绍如何调用Link SDK的API,帮助您实现子设备通过网关接入物联网平台。
背景信息
步骤一:初始化
添加头文件。
…… …… #include "aiot_subdev_api.h"
配置底层依赖和日志输出。
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); aiot_state_set_logcb(demo_state_logcb);
调用aiot_subdev_init,创建
subde
客户端实例,并初始化默认参数。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,配置以下功能。
关联MQTT连接的句柄。
重要在配置网关与子设备功能参数前,请确保已配置网关的设备认证信息,具体操作,请参见MQTT配置连接参数。
示例代码:
aiot_subdev_setopt(subdev_handle, AIOT_SUBDEVOPT_MQTT_HANDLE, mqtt_handle);
相关参数:
配置项
示例
说明
AIOT_SUBDEVOPT_MQTT_HANDLE
mqtt_handle
网关与子设备功能的请求基于MQTT连接,通过该配置项,关联MQTT连接句柄。
配置网关与子设备功能的消息回调。
配置消息回调函数。
示例代码:
aiot_subdev_setopt(subdev_handle, AIOT_SUBDEVOPT_RECV_HANDLER, demo_subdev_recv_handler);
相关参数:
配置项
示例值
说明
AIOT_SUBDEVOPT_RECV_HANDLER
demo_subdev_recv_handler
当设备收到来自物联网平台的网关与子设备的相关消息时,触发该回调函数,根据其设置,执行对应的处理。
定义消息回调函数。
关于消息的Alink数据格式,请参见管理拓扑关系和子设备上下线。
void demo_subdev_recv_handler(void *handle, const aiot_subdev_recv_t *packet, void *user_data) { switch (packet->type) { case AIOT_SUBDEVRECV_TOPO_ADD_REPLY: case AIOT_SUBDEVRECV_TOPO_DELETE_REPLY: case AIOT_SUBDEVRECV_TOPO_GET_REPLY: case AIOT_SUBDEVRECV_BATCH_LOGIN_REPLY: case AIOT_SUBDEVRECV_BATCH_LOGOUT_REPLY: case AIOT_SUBDEVRECV_SUB_REGISTER_REPLY: case AIOT_SUBDEVRECV_PRODUCT_REGISTER_REPLY: { printf("msgid : %d\n", packet->data.generic_reply.msg_id); printf("code : %d\n", packet->data.generic_reply.code); printf("product key : %s\n", packet->data.generic_reply.product_key); printf("device name : %s\n", packet->data.generic_reply.device_name); printf("message : %s\n", (packet->data.generic_reply.message == NULL)?("NULL"):(packet->data.generic_reply.message)); printf("data : %s\n", packet->data.generic_reply.data); } break; case AIOT_SUBDEVRECV_TOPO_CHANGE_NOTIFY: { printf("msgid : %d\n", packet->data.generic_notify.msg_id); printf("product key : %s\n", packet->data.generic_notify.product_key); printf("device name : %s\n", packet->data.generic_notify.device_name); printf("params : %s\n", packet->data.generic_notify.params); } break; default: { } } }
步骤三:添加拓扑关系
获取子设备的认证信息。
为子设备创建对应的产品和设备,创建产品时,节点类型选择为网关子设备。例如,创建子设备对应产品,并添加4个子设备。
产品名称
ProductKey
DeviceName
DeviceSecret
ProductSecret
LightSwitchSD
a13FN******
LightSwitch_SubDev_01
768XBgQwgOakz3K4uhOiLeeh9x******
y7GSILD480******
LightSwitch_SubDev_02
iwTZrbjbgNVChfuJkihjE5asek******
LightSwitch_SubDev_03
fdutq35iKMYdcWWBuIINY26hsN******
LightSwitch_SubDev_04
HCKv50YqgwdKhy5cE0Vz4aydmK******
定义子设备认证信息的变量
g_subdev
。示例代码为预置4个子设备的认证信息,在实际业务中,需自行编写代码,定义获取子设备认证信息的方式。例如:
- 在网关与子设备之间定义协议,实现网关发现子设备,获取子设备的设备证书。该协议由网关厂商与子设备厂商自行定义。
- 网关厂商可以在网关上提供某种配置方式,预置子设备的证书信息。该功能由网关厂商自行实现。
aiot_subdev_dev_t g_subdev[] = { { "a13FN******", "LightSwitch_SubDev_01", "768XBgQwgOakz3K4uhOiLeeh9x******", "y7GSILD480******" }, { "a13FN******", "LightSwitch_SubDev_02", "iwTZrbjbgNVChfuJkihjE5asek******", "y7GSILD480******" }, { "a13FN******", "LightSwitch_SubDev_03", "fdutq35iKMYdcWWBuIINY26hsN******", "y7GSILD480******" }, { "a13FN******", "LightSwitch_SubDev_04", "HCKv50YqgwdKhy5cE0Vz4aydmK******", "y7GSILD480******" } };
调用aiot_subdev_send_topo_add,向物联网平台,发送添加子设备与网关设备的拓扑关系请求。
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; }
可选:当网关设备不再代理子设备接收物联网平台的消息时,您可以调用aiot_subdev_send_topo_delete,删除子设备与网关设备的拓扑关系。
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; }
步骤四:登录子设备
调用aiot_subdev_send_batch_login,向物联网平台发送子设备批量登录的请求,通过建立的拓扑关系,执行登录操作后,子设备将变更为在线状态。
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; }
可选:如果需要主动下线设备,您可以调用aiot_subdev_send_batch_logout,向物联网平台发送断开连接的请求,物联网平台接收请求消息后,执行下线操作,子设备将变更为离线状态。
重要通过该接口,物联网平台更新子设备状态为离线,避免网关收到发送给子设备的消息。
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; }
子设备登录或登出后,物联网平台对发送子设备的消息,根据其对应状态,执行以下操作:
如果子设备离线,物联网平台发送给子设备的
QoS=0
消息立即丢弃。如果子设备在线,物联网平台将子设备的消息发送给对应网关设备,然后由网关设备将子设备的消息转发给子设备。
步骤五:子设备订阅Topic
子设备通过网关设备接入物联网平台后,可以调用aiot_mqtt_sub,订阅子设备的Topic,接收对应Topic的消息。
订阅Topic时,注意区分子设备和网关设备的ProductKey和DeviceName,确保订阅所需设备的Topic。
示例代码:
{ char *sub_topic = "/a13FN******/LightSwitch_SubDev_01/user/get"; res = aiot_mqtt_sub(mqtt_handle, sub_topic, NULL, 1, NULL); if (res < 0) { printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res); return -1; } }
相关参数:
参数
示例
说明
sub_topic
/a13FN******/LightSwitch_SubDev_01/user/get
拥有订阅权限的Topic。其中:
a13FN******
为子设备的ProductKey。LightSwitch_SubDev_01
为子设备的DeviceName。
本示例为子设备默认的自定义Topic,设备通过该Topic,可接收物联网平台的消息。
关于Topic的更多信息,请参见什么是Topic。
步骤六:子设备发布消息
调用aiot_mqtt_pub,向子设备的指定Topic发送消息。
示例代码:
{ char *pub_topic = "/a13FN******/LightSwitch_SubDev_01/user/update"; char *pub_payload = "{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}}"; res = aiot_mqtt_pub(mqtt_handle, pub_topic, (uint8_t *)pub_payload, (uint32_t)strlen(pub_payload), 0); if (res < 0) { printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res); return -1; } }
相关参数:
参数
示例
说明
pub_topic
/a13FN******/LightSwitch_SubDev_01/user/update
拥有发布权限的Topic。其中:
a13FN******
为子设备的ProductKey。LightSwitch_SubDev_01
为子设备的DeviceName。
本示例为子设备默认的自定义Topic,设备通过该Topic向物联网平台发送消息。
关于Topic的更多信息,请参见什么是Topic。
pub_payload
{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}}
步骤七:断开网关连接
MQTT接入常应用于长连接的设备,程序通常不会运行至此。
例程的主线程任务为配置参数并成功建立连接。连接建立后,主线程可进入休眠。
调用aiot_mqtt_disconnect,向物联网平台发送断开连接的报文,然后断开网络连接。
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
return -1;
}
步骤八:退出程序
调用aiot_subdev_deinit,销毁subdev
客户端实例,释放资源。
res = aiot_subdev_deinit(&subdev_handle);
if (res < STATE_SUCCESS) {
printf("aiot_subdev_deinit failed: -0x%04X\n", res);
}
后续步骤
例程文件配置完成后,需进行编译,生成可执行文件../output/subdev-basic-demo。
更多信息,请参见编译与运行。
关于运行结果的详细说明,请参见运行日志。