本文以C Link SDK中的Demo文件./demos/fota_posix_demo.c为例,介绍如何调用Link SDK的API,帮助您实现子设备通过网关接入物联网平台。
背景信息
- 网关与子设备的更多信息,请参见概述。
- 子设备的网关设备需与物联网平台保持长连接,接入方法与直连设备一致。更多信息,请参见MQTT接入概述。
本文示例中,网关设备的身份认证信息如下:
ProductKey |
DeviceName |
DeviceSecret |
a18wP****** |
LightSwitchGW |
uwMTmVAMnGGHaAkqmeDY6cHxxB****** |
步骤一:初始化
- 添加头文件。
……
……
#include "aiot_subdev_api.h"
- 配置底层依赖和日志输出。
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
aiot_state_set_logcb(demo_state_logcb);
- 调用aiot_subdev_init,创建
subdev
客户端实例,并初始化默认参数。 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连接的句柄。
注意 在配置网关与子设备功能参数前,请确保已配置网关的设备认证信息,具体操作,请参见
MQTT配置连接参数。
- 配置网关与子设备功能的消息回调。
- 配置消息回调函数。
- 定义消息回调函数。
关于消息的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}} |
上报至物联网平台的消息内容。 由于示例消息的Topic类型为自定义,因此数据格式可自定义。
关于数据格式的更多信息,请参见数据格式。
|
步骤七:断开网关连接
说明
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。
更多信息,请参见编译与运行。
- 关于运行结果的详细说明,请参见运行日志。