This article describes how to call the API operations of Link SDK for C to connect sub-devices to IoT Platform by using gateways. In this example./demos/fota_posix_demo.c sample code file is used.

Background information

  • For more information about gateways and sub-devices, see Overview.
  • Gateways must maintain persistent connections with IoT Platform. The connection method of gateways is the same as that of directly connected devices. For more information, see Overview.

    The following table describes the authentication information of the gateway that is used in this example.

    ProductKey DeviceName DeviceSecret
    a18wP****** LightSwitchGW uwMTmVAMnGGHaAkqmeDY6cHxxB******

Step 1: Initialize a client

  1. Add header files.
    ...
    ...
    
    #include "aiot_subdev_api.h"
  2. Add the underlying dependency and configure the log output feature.
        aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
        aiot_state_set_logcb(demo_state_logcb);
  3. Call the aiot_subdev_init operation to create a subdev client instance and initialize the default parameters.
        subdev_handle = aiot_subdev_init();
        if (subdev_handle == NULL) {
            printf("aiot_subdev_init failed\n");
            demo_mqtt_stop(&mqtt_handle);
            return -1;
        }

Step 2: Configure required features

Call the aiot_subdev_setopt operation to configure the following items:

  1. Associate with MQTT connection handles.
  2. Configure message callbacks.
  1. Associate with an MQTT connection handle.
    Notice Before you set parameters that are related to the gateway and sub-device, make sure that the authentication information of the gateway is configured. For more information, see Example.
    • Sample code
          aiot_subdev_setopt(subdev_handle, AIOT_SUBDEVOPT_MQTT_HANDLE, mqtt_handle);
    • Parameters:
      Parameter Example Description
      AIOT_SUBDEVOPT_MQTT_HANDLE mqtt_handle You must establish an MQTT connection. This parameter is used to associate with the MQTT connection handle.
  2. Configure the message callbacks.
    1. Specify the message callbacks.
      • Sample code
            aiot_subdev_setopt(subdev_handle, AIOT_SUBDEVOPT_RECV_HANDLER, demo_subdev_recv_handler);
      • Parameters:
        Parameter Example Description
        AIOT_SUBDEVOPT_RECV_HANDLER demo_subdev_recv_handler When the gateway receives a message from IoT Platform, the callback is called to perform the required operations.
    2. Define the message callbacks.
      For more information about the Alink data format of messages, see Manage topological relationships and Connect or disconnect sub-devices.
      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: {
      
              }
          }
      }

Step 3: Add a topological relationship

  1. Obtain the authentication information of the sub-device.
    Create a product and device for the sub-device. When you create the product, set the node type to Gateway sub-device. In this example, the following product and sub-devices are created.
    ProductName ProductKey DeviceName DeviceSecret ProductSecret
    LightSwitchSD a13FN****** LightSwitch_SubDev_01 768XBgQwgOakz3K4uhOiLeeh9x****** y7GSILD480******
    LightSwitch_SubDev_02 iwTZrbjbgNVChfuJkihjE5asek******
    LightSwitch_SubDev_03 fdutq35iKMYdcWWBuIINY26hsN******
    LightSwitch_SubDev_04 HCKv50YqgwdKhy5cE0Vz4aydmK******

    For more information, see Create a product and Create a device.

  2. Define the g_subdev variable that indicates the authentication information of the sub-device.
    In this example, the authentication information of four sub-devices is provided. In actual business scenarios, you must write code and define the method to obtain the authentication information of sub-devices. Sample code:
    • After a gateway discovers a connected sub-device, the gateway can obtain the certificate of the sub-device based on a protocol that is defined between the gateway and the sub-device. The protocol is defined by the provider of the gateway and the provider of the sub-device.
    • The provider of the gateway provides a configuration method that allows the gateway to preset certificate information about the sub-device. This feature is implemented by the provider of the gateway.
    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******"
        }
    };
  3. Call the aiot_subdev_send_topo_add operation to add a topological relationship between the sub-device and the gateway. The request is sent IoT Platform.
        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;
        }
  4. Optional:If you do not need the gateway to receive messages from IoT Platform as a proxy, call the aiot_subdev_send_topo_delete operation to delete the topological relationship between the sub-device and the gateway.
         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;
         }

Step 4: Connect the sub-device to IoT Platform

  1. Call the aiot_subdev_send_batch_login operation to connect one or more sub-devices with IoT Platform. After a sub-device is connected to IoT Platform, the status of the sub-device changes to Online.
        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;
        }
  2. Optional:If you need to disconnect the sub-device, call the aiot_subdev_send_batch_logout operation to send a disconnection request. After IoT Platform receives the request, IoT Platform ends the connection. Then, the status of the sub-device changes to Offline.
    Notice IoT Platform updates the status of the sub-device to be Offline. In this case, the gateway no longer receives messages that IoT Platform sends to the sub-device.
         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;
         }
The messages that IoT Platform sends to the sub-device are processed based on the online status of the sub-device.
  • If the sub-device is offline, QoS 0 messages are discarded.
  • If the sub-device is online, IoT Platform sends messages to the gateway. Then, the gateway forwards the messages to the sub-device.

Step 5: Subscribe to topics

After a sub-device is connected to IoT Platform, you can call the aiot_mqtt_sub operation to subscribe to topics. Then, the sub-device can receive messages by using the topics.

Notice When specify topics, differentiate the ProductKey and DeviceName of the sub-device and the gateway.
  • Sample code
        {
            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;
            }
        }
  • Parameters:
    Parameter Example Description
    sub_topic /a13FN******/LightSwitch_SubDev_01/user/get
    The topic that has the Subscribe permission.
    • a1oGs****** indicates the ProductKey of the sub-device.
    • LightSwitch_SubDev_01 indicates the DeviceName of the sub-device.

    In this example, the default custom topic is specified. The device can receive messages from IoT Platform by using this topic.

    For more information about topics, see What is a topic?.

Step 6: Publish messages from the sub-device

Call the aiot_mqtt_pub operation to send a message to a specified topic.

  • Sample code
         {
            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;
            }
        }
  • Parameters:
    Parameter Example Description
    pub_topic /a13FN******/LightSwitch_SubDev_01/user/update The topic that has the Publish permission.
    • a1oGs****** indicates the ProductKey of the sub-device.
    • LightSwitch_SubDev_01 indicates the DeviceName of the sub-device.
    In this example, the default custom topic is specified. The device can send messages to IoT Platform by using this topic.

    For more information about topics, see What is a topic?.

    pub_payload {\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}} The message that is sent to IoT Platform.

    In this example, a custom topic is used. Therefore, you can customize the message format.

    For more information about message formats, see Data formats.

Step 7: Disconnect the gateway

Note

MQTT connections are applied to devices that remain persistently connected. You can manually disconnect devices from IoT Platform.

In this example, the main thread is used to set parameters and establish a connection. After the connection is established, you can put the main thread to sleep.

Call the aiot_mqtt_disconnect operation to disconnect the device from IoT Platform.

    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;
    }

Step 8: Exit the program

Call aiot_subdev_deinit operation to destroy the subdev client instance and release resources.

    res = aiot_subdev_deinit(&subdev_handle);
    if (res < STATE_SUCCESS) {
        printf("aiot_subdev_deinit failed: -0x%04X\n", res);
    }

What to do next

After you configure the sample code file, compile the file to generate an executable file In this example, the ./output/subdev-basic-demo executable file is generated. For more information, see Compilation and running