Sub-devices cannot directly connect to IoT Platform. Instead, they must connect to IoT Platform through a gateway. After a sub-device connects to a gateway, the gateway queries the topological relationship with the sub-device, reports the information about the sub-device to IoT Platform, and then connects the sub-device to IoT Platform.

Develop a sub-device

A sub-device does not directly connect to IoT Platform, so you do no need to install a device SDK of IoT Platform on the sub-device. The sub-device supplier develops the sub-device.

Gateway obtains the certificate of the sub-device

The gateway reports the certificate of the sub-device including the ProductKey, DeviceName, and DeviceSecret for IoT Platform to authenticate the connection. The gateway supplier implements the method how the gateway obtains the certificate. Alibaba Cloud does not provide corresponding code. The gateway obtains the certificate of the sub-device in three ways:

  • The gateway obtains the certificate directly from the sub-device.

    The gateway discovers a sub-device and obtains the certificate of the sub-device based on a protocol defined between the gateway and the sub-device. The gateway supplier and the sub-device supplier define this protocol.

  • Preset the certificate of the sub-device on the gateway.

    The gateway supplier implements the configuration method for presetting the certificate of the sub-device on the gateway. Alibaba Cloud does not provide corresponding code.

  • The gateway obtains the certificate of the sub-device during dynamic registration.
    1. When you create a sub-device in IoT Platform, set the DeviceName parameter to the serial number or MAC address of the sub-device. Afterward, enable dynamic registration for the sub-device.
    2. Develop a gateway that can discover the sub-device based on a specified protocol and obtain the model and identifier such as the serial number or MAC address of the sub-device. Also, the gateway can map the sub-device model to the ProductKey in IoT Platform.
    3. Obtain the DeviceSecret of the sub-device from IoT Platform during dynamic registration.

Connect the sub-device to IoT Platform through the gateway

The DeviceTopoManager file in the directory java/src/main/com.aliyun.iot.api.common.openApi contains the code for the gateway to manage topological relationships and dynamically register sub-devices. In this file, you can configure the gateway and the sub-device to create a topological relationship and dynamically register the sub-device.

  1. Connect the sub-device to the gateway.
    The gateway supplier and the sub-device supplier develop protocols for connecting the sub-device to the gateway. The gateway also uses these protocols to discover the sub-device, detect the online and offline states of the sub-device, and forward messages from IoT Platform to the sub-device.
  2. The gateway queries the topological relationship with the specified sub-device.
         /**
         * Queries the topological relationships of the gateway, and checks whether a topological relationship exists between the gateway and the specified sub-device.
         */
        private void getGWDeviceTopo() {
            LinkKit.getInstance().getGateway().gatewayGetSubDevices(new IConnectSendListener() {
    
                @Override
                public void onResponse(ARequest request, AResponse aResponse) {
                    ALog.i(TAG, "The topological relationship of the gateway is queried: " + JSONObject.toJSONString(aResponse));
    
                    // Queries the sub-devices.
                    try {
                        ResponseModel<List<DeviceInfo>> response = JSONObject.parseObject(aResponse.data.toString(), new TypeReference<ResponseModel<List<DeviceInfo>>>() {
                        }.getType());
                        // Processes the request according to actual application scenarios.
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
    
                @Override
                public void onFailure(ARequest request, AError error) {
                    ALog.i(TAG, "Failed to query the topological relationship of the gateway: " + JSONObject.toJSONString(error));
                }
            });
        }
  3. Dynamically register the sub-device. This is an optional operation. Skip this step if the gateway has obtained the certificate of the sub-device.
    Note To dynamically register the sub-device, you must go to the IoT Platform console, and on the Product Details page of the product that the sub-device belongs to, select Dynamic Registration.

    The gateway reports the ProductKey and DeviceName of the sub-device to IoT platform to register the sub-device. IoT Platform authenticates the ProductKey and DeviceName of the sub-device, and after the sub-device passes the authentication, dynamically assigns the corresponding DeviceSecret to the sub-device.

    The following example shows the code for dynamically registering the sub-device.

         /**
         * The sub-device obtains the deviceSecret during dynamic registration.
         * When you create the sub-device in IoT Platform, set the DeviceName parameter to the serial number or MAC address of the sub-device.
         */
        private void gatewaySubDevicRegister() {
    
            List<BaseInfo> subDevices = new ArrayList<>();
            BaseInfo baseInfo1 = new BaseInfo();
            baseInfo1.productKey = "a1j7SyR***";
            baseInfo1.deviceName = "safasdf";
            subDevices.add(baseInfo1);
    
            LinkKit.getInstance().getGateway().gatewaySubDevicRegister(subDevices, new IConnectSendListener() {
    
                @Override
                public void onResponse(ARequest request, AResponse response) {
                    ALog.i(TAG, "The sub-device is registered: " + JSONObject.toJSONString(response));
                }
    
                @Override
                public void onFailure(ARequest request, AError error) {
                    ALog.i(TAG, "Failed to register the sub-device: " + JSONObject.toJSONString(error));
                }
            });
        }

    For more information about dynamic registration of devices, see Authenticate devices.

  4. Add the topological relationship between the sub-device and the gateway.
        /**
         * The information about the sub-device in the topological relationship.
         */
        private void gatewayAddSubDevice() {
            BaseInfo baseInfo1 = new BaseInfo();
            baseInfo1.productKey = "a1j7SyR****";
            baseInfo1.deviceName = "safasdf";
            String deviceSecret = "7lzCJIWHmGFpZpDKbJdVucDHUz6C****";
    
            LinkKit.getInstance().getGateway().gatewayAddSubDevice(baseInfo1, new ISubDeviceConnectListener() {
                @Override
                public String getSignMethod() {
                    // The method that you use to sign the request.
                    return "hmacsha1";
                }
    
                @Override
                public String getSignValue() {
                    // Generates the signature based on the deviceSecret.
                    Map<String, String> signMap = new HashMap<>();
                    signMap.put("productKey", baseInfo1.productKey);
                    signMap.put("deviceName", baseInfo1.deviceName);
                    //signMap.put("timestamp", String.valueOf(System.currentTimeMillis()));
                    signMap.put("clientId", getClientId());
                    return SignUtils.hmacSign(signMap, deviceSecret);
                }
    
                @Override
                public String getClientId() {
                    // Specifies any value as clientId.
                    return "id";
                }
    
                @Override
                public Map<String, Object> getSignExtraData() {
                    return null;
                }
    
                @Override
                public void onConnectResult(boolean isSuccess, ISubDeviceChannel iSubDeviceChannel, AError aError) {
    
    
                    // The result of the operation for adding the topological relationship between the sub-device and the gateway.
                    if (isSuccess) {
                        // Connects the sub-device to IoT Platform through the gateway after the topological relationship between the sub-device and the gateway is added.
                        ALog.i(TAG, "The topological relationship is added: " + JSONObject.toJSONString(iSubDeviceChannel));
                    } else {
                        ALog.i(TAG, "Failed to add the topological relationship: " + JSONObject.toJSONString(aError));
                    }
                }
    
                @Override
                public void onDataPush(String s, AMessage aMessage) {
    
                }
            });
        }
    IoT Platform confirms the topological relationship between the gateway and the sub-device. Afterward, the sub-device can use the physical channel of the gateway to communicate with IoT Platform.
  5. Connect the sub-device to IoT Platform.
         /**
         * Before the sub-device is connected to IoT Platform through the gateway, make sure that you have established the topological relationship between the sub-device and the gateway. After the gateway discovers the sub-device,
         * the gateway reports the sub-device to IoT Platform. After the sub-device is connected to IoT Platform, the gateway can subscribe to topics and publish messages for the sub-device.
         */
        public void gatewaySubDeviceLogin(){
    
            BaseInfo baseInfo1 = new BaseInfo();
            baseInfo1.productKey = "a1j7SyR****";
            baseInfo1.deviceName = "safasdf";
    
            LinkKit.getInstance().getGateway().gatewaySubDeviceLogin(baseInfo1, new ISubDeviceActionListener() {
                @Override
                public void onSuccess() {
                    // The sub-device is connected to IoT Platform through the gateway.
                    // Subscribes to topics, publishes messages, and deletes or disables the online sub-device.
                    // subDevDisable(null);
                    // subDevDelete(null);
                }
                @Override
                public void onFailed(AError aError) {
                    ALog.d(TAG, "onFailed() called with: aError = [" + aError + "]");
                }
            });
    
        }
    
    }

Appendix: Sample code

The gateway discovers and reports the sub-device, and establishes a logical channel between the sub-device and IoT Platform. Afterward, the sub-device can use the physical channel of the gateway to connect to IoT Platform. The following example shows the code for these features:

package com.aliyun.iot.api.common.openApi;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.aliyun.alink.dm.api.BaseInfo;
import com.aliyun.alink.dm.api.DeviceInfo;
import com.aliyun.alink.dm.api.InitResult;
import com.aliyun.alink.dm.api.SignUtils;
import com.aliyun.alink.dm.model.ResponseModel;
import com.aliyun.alink.linkkit.api.ILinkKitConnectListener;
import com.aliyun.alink.linkkit.api.IoTMqttClientConfig;
import com.aliyun.alink.linkkit.api.LinkKit;
import com.aliyun.alink.linkkit.api.LinkKitInitParams;
import com.aliyun.alink.linksdk.channel.gateway.api.subdevice.ISubDeviceActionListener;
import com.aliyun.alink.linksdk.channel.gateway.api.subdevice.ISubDeviceChannel;
Import com. aliyun. alink. linksdk. channel. gateway. api. subdevice. ISubDeviceConnectListener;
import com.aliyun.alink.linksdk.channel.gateway.api.subdevice.ISubDeviceRemoveListener;
import com.aliyun.alink.linksdk.cmp.core.base.AMessage;
import com.aliyun.alink.linksdk.cmp.core.base.ARequest;
import com.aliyun.alink.linksdk.cmp.core.base.AResponse;
import com.aliyun.alink.linksdk.cmp.core.listener.IConnectSendListener;
import com.aliyun.alink.linksdk.tools.AError;
import com.aliyun.alink.linksdk.tools.ALog;

import java.util.*;

import static com.aliyun.alink.linksdk.tools.ALog.LEVEL_DEBUG;
import static java.awt.SystemColor.info;

public class DeviceTopoManager {
    private static String regionId = "cn-shanghai";
    private static final String TAG = "TOPO";

    //The gateway.
    private static String GWproductKey = "a1BxptK****";
    private static String GWdeviceName = "XMtrv3yvftEHAzrTfX1U";
    private static String GWdeviceSecret = "19xJNybifnmgcK057vYhazYK4b64****";


    public static void main(String[] args) {
        /**
         * The information about the MQTT connection.
         */
        DeviceTopoManager manager = new DeviceTopoManager();

        /**
         * The Java HTTP client of this device supports TSLv1.2.
         */
        System.setProperty("https.protocols", "TLSv2");

        manager.init();
    }

    public void init() {
        LinkKitInitParams params = new LinkKitInitParams();
        /**
         * Specifies the parameters for the MQTT initialization.
         */
        IoTMqttClientConfig config = new IoTMqttClientConfig();
        config.productKey = GWproductKey;
        config.deviceName = GWdeviceName;
        config.deviceSecret = GWdeviceSecret;
        config.channelHost = GWproductKey + ".iot-as-mqtt." + regionId + ".aliyuncs.com:1883";
        /**
         * Specifies whether to receive offline messages.
         * The cleanSession field corresponding to the MQTT connection.
         */
        config.receiveOfflineMsg = false;
        params.mqttClientConfig = config;
        ALog.setLevel(LEVEL_DEBUG);
        ALog.i(TAG, "mqtt connetcion info=" + params);

        /**
         * Specifies the initialization and passes in the certificate information of the specified sub-device.
         */
        DeviceInfo deviceInfo = new DeviceInfo();
        deviceInfo.productKey = GWproductKey;
        deviceInfo.deviceName = GWdeviceName;
        deviceInfo.deviceSecret = GWdeviceSecret;
        params.deviceInfo = deviceInfo;

        /** Establishes a connection. **/
        LinkKit.getInstance().init(params, new ILinkKitConnectListener() {
            public void onError(AError aError) {
                ALog.e(TAG, "Init Error error=" + aError);
            }

            public void onInitDone(InitResult initResult) {
                ALog.i(TAG, "onInitDone result=" + initResult);


                //Queries the topological relationships under the gateway, and checks whether a topological relationship exists between the gateway and the specified sub-device.
                //Connects the sub-device to IoT Platform if the topological relationship exists.
                getGWDeviceTopo();

                //Dynamically registers the sub-device to obtain the deviceSecret of the sub-device, and if the gateway has obtained the certificate, skips this step and adds the topological relationship.
                //When you create the sub-device in IoT Platform, set the DeviceName parameter to the serial number or MAC address of the sub-device.
                gatewaySubDevicRegister();

                //The information about the sub-device in the topological relationship.
                gatewayAddSubDevice();

                //Connects the sub-device to IoT Platform.
                gatewaySubDeviceLogin();

            }
        });
    }

    /**
     * Queries the topological relationships under the gateway, and checks whether a topological relationship exists between the gateway and the specified sub-device.
     */
    private void getGWDeviceTopo() {
        LinkKit.getInstance().getGateway().gatewayGetSubDevices(new IConnectSendListener() {

            @Override
            public void onResponse(ARequest request, AResponse aResponse) {
                ALog.i(TAG, "The topological relationship under the gateway is queried: " + JSONObject.toJSONString(aResponse));

                // Queries the sub-devices.
                try {
                    ResponseModel<List<DeviceInfo>> response = JSONObject.parseObject(aResponse.data.toString(), new TypeReference<ResponseModel<List<DeviceInfo>>>() {
                    }.getType());
                    // Processes the request according to actual application scenarios.
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onFailure(ARequest request, AError error) {
                ALog.i(TAG, "Failed to query the topological relationship under the gateway: " + JSONObject.toJSONString(error));
            }
        });
    }

    /**
     * Dynamically registers the sub-device to obtain the deviceSecret of the sub-device, and if the gateway has obtained the certificate, skips this step.
     * When you create the sub-device in IoT Platform, set the DeviceName parameter to the serial number or MAC address of the sub-device.
     */
    private void gatewaySubDevicRegister() {

        List<BaseInfo> subDevices = new ArrayList<>();
        BaseInfo baseInfo1 = new BaseInfo();
        baseInfo1.productKey = "a1j7SyR****";
        baseInfo1.deviceName = "safasdf";
        subDevices.add(baseInfo1);

        LinkKit.getInstance().getGateway().gatewaySubDevicRegister(subDevices, new IConnectSendListener() {

            @Override
            public void onResponse(ARequest request, AResponse response) {
                ALog.i(TAG, "The sub-device is registered: " + JSONObject.toJSONString(response));
            }

            @Override
            public void onFailure(ARequest request, AError error) {
                ALog.i(TAG, "Failed to register the sub-device: " + JSONObject.toJSONString(error));
            }
        });
    }

    /**
     * The information about the sub-device in the topological relationship.
     */
    private void gatewayAddSubDevice() {
        BaseInfo baseInfo1 = new BaseInfo();
        baseInfo1.productKey = "a1j7SyR****";
        baseInfo1.deviceName = "safasdf";
        String deviceSecret = "7lzCJIWHmGFpZpDKbJdVucDHUz6C****";

        LinkKit.getInstance().getGateway().gatewayAddSubDevice(baseInfo1, new ISubDeviceConnectListener() {
            @Override
            public String getSignMethod() {
                // The method that you use to sign the request.
                return "hmacsha1";
            }

            @Override
            public String getSignValue() {
                // Generates the signature based on the deviceSecret.
                Map<String, String> signMap = new HashMap<>();
                signMap.put("productKey", baseInfo1.productKey);
                signMap.put("deviceName", baseInfo1.deviceName);
               // signMap.put("timestamp", String.valueOf(System.currentTimeMillis()));
                signMap.put("clientId", getClientId());
                return SignUtils.hmacSign(signMap, deviceSecret);
            }

            @Override
            public String getClientId() {
                // Specifies any value as clientId.
                return "id";
            }

            @Override
            public Map<String, Object> getSignExtraData() {
                return null;
            }

            @Override
            public void onConnectResult(boolean isSuccess, ISubDeviceChannel iSubDeviceChannel, AError aError) {


                // The result of the operation for adding the topological relationship between the sub-device and the gateway.
                if (isSuccess) {
                    // Connects the sub-device to IoT Platform through the gateway after the topological relationship between the sub-device and the gateway is added.
                    ALog.i(TAG, "The topological relationship is added: " + JSONObject.toJSONString(iSubDeviceChannel));
                } else {
                    ALog.i(TAG, "Failed to add the topological relationship: " + JSONObject.toJSONString(aError));
                }
            }

            @Override
            public void onDataPush(String s, AMessage aMessage) {

            }
        });
    }

    Public void gatewayDeleteSubDevice (){
        BaseInfo baseInfo1 = new BaseInfo();
        baseInfo1.productKey = "a1j7SyR****";
        baseInfo1.deviceName = "safasdf";

        LinkKit.getInstance().getGateway().gatewayDeleteSubDevice(baseInfo1, new ISubDeviceRemoveListener() {
            @Override
            public void onSuceess() {
                // The sub-device is deleted. Before you delete the sub-device, you can disconnect the sub-device from IoT Platform.
            }
            @Override
            public void onFailed(AError aError) {
                // Failed to delete the sub-device.
            }
        });
    }

    /**
     * Before the sub-device is connected to IoT Platform through the gateway, make sure that you have established the topological relationship between the sub-device and the gateway. After the gateway discovers the sub-device,
     * the gateway reports the sub-device to IoT Platform. After the sub-device is connected to IoT Platform, the gateway can subscribe to topics and publish messages.
     */
    public void gatewaySubDeviceLogin(){

        BaseInfo baseInfo1 = new BaseInfo();
        baseInfo1.productKey = "a1j7SyR****";
        baseInfo1.deviceName = "safasdf";

        LinkKit.getInstance().getGateway().gatewaySubDeviceLogin(baseInfo1, new ISubDeviceActionListener() {
            @Override
            public void onSuccess() {
                // The sub-device is connected to IoT Platform through the gateway.
                // Subscribes to topics, publishes messages, and deletes or disables the online sub-device.
                // subDevDisable(null);
                // subDevDelete(null);
            }
            @Override
            public void onFailed(AError aError) {
                ALog.d(TAG, "onFailed() called with: aError = [" + aError + "]");
            }
        });

    }

}