A sub-device does not connect to IoT Platform directly. Instead, it connects through a gateway. After a sub-device connects to a gateway, the gateway queries the topological relationship, reports the sub-device's information to IoT Platform, and then proxies the sub-device's connection to IoT Platform.
Prerequisites
Ensure you have completed the following:
Background
- Develop a sub-device
Because a sub-device does not connect to IoT Platform directly, you do not need to install the IoT Platform device SDK on it. The sub-device's manufacturer develops its device-side software.
- This demo
The DeviceTopoManager file in the java/src/main/java/com/aliyun/iot/api/common/deviceApi directory contains the code for managing topological relationships, retrieving sub-device certificates, and logging in a sub-device.
Step 1: Manage the topological relationship
After the gateway connects to IoT Platform, it must synchronize the topological relationship with IoT Platform. This allows the gateway to proxy communication between the sub-device and IoT Platform. You can view and add the topological relationship on the console or by using the sample code.
- On the IoT Platform console, under the corresponding , view and add the topological relationship between the gateway and the sub-device.
- In the left-side navigation pane, choose , and find the gateway device in the list.
- Click Sub-device in the gateway's row to go to the sub-device management page. View the information about the gateway's sub-devices.
- Click Add Sub-device to add the sub-device that you created in the Create a gateway and a sub-device step.
- Use the following sample code to query and add the topological relationship.
-
Query the topological relationship:
/** * Get the topological relationship for the gateway to check if one already exists between the gateway and a sub-device. */ private void getGWDeviceTopo() { LinkKit.getInstance().getGateway().gatewayGetSubDevices(new IConnectSendListener() { @Override public void onResponse(ARequest request, AResponse aResponse) { ALog.i(TAG, "Successfully retrieved the topological relationship for the gateway: " + JSONObject.toJSONString(aResponse)); // Get the list of sub-devices. try { ResponseModel<List<DeviceInfo>> response = JSONObject.parseObject(aResponse.data.toString(), new TypeReference<ResponseModel<List<DeviceInfo>>>() { }.getType()); // TODO: Process the data based on your business scenario. } catch (Exception e) { e.printStackTrace(); } } @Override public void onFailure(ARequest request, AError error) { ALog.i(TAG, "Failed to retrieve the topological relationship for the gateway: " + JSONObject.toJSONString(error)); } }); } -
Add a topological relationship:Note
- The next step explains how to obtain the device certificate for a sub-device.
- After IoT Platform confirms the topological relationship between the sub-device and the gateway, the sub-device can go online and reuse the gateway's physical channel to communicate with IoT Platform.
/** * Information about the sub-device for which to add the topological relationship. */ private void gatewayAddSubDevice() { BaseInfo baseInfo1 = new BaseInfo(); baseInfo1.productKey = "a1j7SyR****"; baseInfo1.deviceName = "safa***"; String deviceSecret = "7lzCJIWHmGFpZpDKbJdVucDHUz6C****"; LinkKit.getInstance().getGateway().gatewayAddSubDevice(baseInfo1, new ISubDeviceConnectListener() { @Override public String getSignMethod() { // The signing method to use. return "hmacsha1"; } @Override public String getSignValue() { // Get the signature. The user uses the deviceSecret to generate the signature. 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() { // The clientId can be any string. return "id"; } @Override public Map<String, Object> getSignExtraData() { return null; } @Override public void onConnectResult(boolean isSuccess, ISubDeviceChannel iSubDeviceChannel, AError aError) { // Result of the add operation. if (isSuccess) { // The sub-device is added. You can now proceed to log in the sub-device. ALog.i(TAG, "Successfully added the topological relationship: " + JSONObject.toJSONString(iSubDeviceChannel)); //Log in the sub-device. gatewaySubDeviceLogin(); } else { ALog.i(TAG, "Failed to add the topological relationship: " + JSONObject.toJSONString(aError)); } } @Override public void onDataPush(String s, AMessage aMessage) { } }); }
-
Step 2: Obtain sub-device certificate
- Use unique-certificate-per-device authentication.
After the sub-device is created, obtain its ProductKey, DeviceName, and DeviceSecret from the device details page on the console.
- Define a protocol between the gateway and the sub-device that allows the gateway to discover the sub-device and obtain its device certificate. The gateway and sub-device manufacturers define this protocol.
- Alternatively, the gateway manufacturer can provide a method to preconfigure the sub-device's certificate information on the gateway.
- Use dynamic registration for the sub-device.
The gateway registers the sub-device by reporting its ProductKey and DeviceName to IoT Platform. After IoT Platform validates the ProductKey and DeviceName, it dynamically issues a DeviceSecret for the sub-device.
- When you create a sub-device, use its serial number (SN) or MAC address as the DeviceName. After the sub-device is created, enable dynamic registration for the product.On the IoT Platform console, go to the Product Details page for the corresponding product and enable Dynamic Registration.
- When you develop the gateway, implement a protocol that allows the gateway to discover sub-devices and retrieve their model and unique identifier (SN or MAC address). You must also map the sub-device model to a ProductKey in Alibaba Cloud IoT Platform.
- Use the dynamic registration feature of IoT Platform to obtain the DeviceSecret for the sub-device.
Sample code:
/** * Use dynamic registration to obtain the deviceSecret for a sub-device. * When you create a sub-device on IoT Platform in advance, you can use its MAC address or serial number (SN) as the DeviceName. */ 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, "Sub-device registered successfully: " + 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 device registration, see Dynamic registration of sub-devices.
- When you create a sub-device, use its serial number (SN) or MAC address as the DeviceName. After the sub-device is created, enable dynamic registration for the product.
Step 3: Log in a sub-device
/**
* Before you call the API to log in the sub-device, make sure the topological relationship is established. After the gateway discovers a connected sub-device, it must notify IoT Platform that the sub-device is online.
* After the sub-device is online, you can perform operations such as subscribing to topics and publishing 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 logged in by proxy.
// After the sub-device is online, you can subscribe to topics, publish messages, and delete or disable the sub-device.
// subDevDisable(null);
// subDevDelete(null);
}
@Override
public void onFailed(AError aError) {
ALog.d(TAG, "onFailed() called with: aError = [" + aError + "]");
}
});
}
}
Appendix: Code demo
The following code demonstrates how a gateway discovers and reports sub-device information, establishes a logical channel between the sub-device and IoT Platform, and allows the sub-device to reuse the gateway's physical channel to connect to IoT Platform:
package com.aliyun.iot.api.common.deviceApi;
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;
public class DeviceTopoManager {
private static String regionId = "cn-shanghai";
private static final String TAG = "TOPO";
// Gateway device
private static String GWproductKey = "a1Bxp*********";
private static String GWdeviceName = "XMtrv3y*************";
private static String GWdeviceSecret = "19xJNybifnmgc*************";
public static void main(String[] args) {
/**
* MQTT connection information.
*/
DeviceTopoManager manager = new DeviceTopoManager();
/**
* The server-side Java HTTP client uses TLSv1.2.
*/
System.setProperty("https.protocols", "TLSv1.2");
manager.init();
}
public void init() {
LinkKitInitParams params = new LinkKitInitParams();
/**
* Set MQTT initialization parameters.
*/
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.
* This corresponds to the cleanSession field in MQTT.
*/
config.receiveOfflineMsg = false;
params.mqttClientConfig = config;
ALog.setLevel(LEVEL_DEBUG);
ALog.i(TAG, "mqtt connection info=" + params);
/**
* Set initialization parameters and pass in the device certificate of the gateway.
*/
DeviceInfo deviceInfo = new DeviceInfo();
deviceInfo.productKey = GWproductKey;
deviceInfo.deviceName = GWdeviceName;
deviceInfo.deviceSecret = GWdeviceSecret;
params.deviceInfo = deviceInfo;
/**Initialize the LinkKit instance and establish 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);
// Get the topological relationship for the gateway to check if one already exists between the gateway and the sub-device.
// If a relationship exists, log in the sub-device directly.
getGWDeviceTopo();
// Use dynamic registration to obtain the DeviceSecret for the sub-device. If the device certificate is known, skip this step and add the topological relationship directly.
// When you create the device on IoT Platform in advance, you can use its MAC address or serial number (SN) as the DeviceName.
gatewaySubDevicRegister();
// Information about the sub-device for which to add the topological relationship.
gatewayAddSubDevice();
}
});
}
/**
* Get the topological relationship for the gateway to check if one already exists between the gateway and a sub-device.
*/
private void getGWDeviceTopo() {
LinkKit.getInstance().getGateway().gatewayGetSubDevices(new IConnectSendListener() {
@Override
public void onResponse(ARequest request, AResponse aResponse) {
ALog.i(TAG, "Successfully retrieved the topological relationship for the gateway: " + JSONObject.toJSONString(aResponse));
// Get the list of sub-devices.
try {
ResponseModel<List<DeviceInfo>> response = JSONObject.parseObject(aResponse.data.toString(), new TypeReference<ResponseModel<List<DeviceInfo>>>() {
}.getType());
// TODO: Process the data based on your business scenario.
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onFailure(ARequest request, AError error) {
ALog.i(TAG, "Failed to retrieve the topological relationship for the gateway: " + JSONObject.toJSONString(error));
}
});
}
/**
* Use dynamic registration to obtain the deviceSecret for a sub-device. If the gateway has already obtained the device certificate for the sub-device, skip this step.
* When you create the device on IoT Platform in advance, you can use its MAC address or serial number (SN) as the DeviceName.
*/
private void gatewaySubDevicRegister() {
List<BaseInfo> subDevices = new ArrayList<>();
BaseInfo baseInfo1 = new BaseInfo();
baseInfo1.productKey = "a1j7SyR**********";
baseInfo1.deviceName = "test123*********";
subDevices.add(baseInfo1);
LinkKit.getInstance().getGateway().gatewaySubDevicRegister(subDevices, new IConnectSendListener() {
@Override
public void onResponse(ARequest request, AResponse response) {
ALog.i(TAG, "Sub-device registered successfully: " + JSONObject.toJSONString(response));
}
@Override
public void onFailure(ARequest request, AError error) {
ALog.i(TAG, "Failed to register the sub-device: " + JSONObject.toJSONString(error));
}
});
}
/**
* Adds the topological relationship for a sub-device.
*/
private void gatewayAddSubDevice() {
BaseInfo baseInfo1 = new BaseInfo();
baseInfo1.productKey = "a1j7Sy*************";
baseInfo1.deviceName = "safasd********";
String deviceSecret = "7lzCJIWHmGF**************";
LinkKit.getInstance().getGateway().gatewayAddSubDevice(baseInfo1, new ISubDeviceConnectListener() {
@Override
public String getSignMethod() {
// The signing method to use.
return "hmacsha1";
}
@Override
public String getSignValue() {
// Get the signature. The user uses the DeviceSecret to generate the signature.
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() {
// The clientId can be any string.
return "id";
}
@Override
public Map<String, Object> getSignExtraData() {
return null;
}
@Override
public void onConnectResult(boolean isSuccess, ISubDeviceChannel iSubDeviceChannel, AError aError) {
// Result of the add operation.
if (isSuccess) {
// The sub-device is added. You can now proceed to log in the sub-device.
ALog.i(TAG, "Successfully added the topological relationship: " + JSONObject.toJSONString(iSubDeviceChannel));
//Log in the sub-device.
gatewaySubDeviceLogin();
} 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 = "a1j7S**************";
baseInfo1.deviceName = "saf*********";
LinkKit.getInstance().getGateway().gatewayDeleteSubDevice(baseInfo1, new ISubDeviceRemoveListener() {
@Override
public void onSuccess() {
// The listener is called when the sub-device is successfully deleted. You can take the sub-device offline before deleting it.
}
@Override
public void onFailed(AError aError) {
// Failed to delete the sub-device.
}
});
}
/**
* Before you call the API to log in the sub-device, make sure the topological relationship is established. After the gateway discovers a connected sub-device, it must notify IoT Platform that the sub-device is online.
* After the sub-device is online, you can perform operations such as subscribing to topics and publishing messages.
*/
public void gatewaySubDeviceLogin(){
BaseInfo baseInfo1 = new BaseInfo();
baseInfo1.productKey = "a1j7SyR***********";
baseInfo1.deviceName = "safa*********";
LinkKit.getInstance().getGateway().gatewaySubDeviceLogin(baseInfo1, new ISubDeviceActionListener() {
@Override
public void onSuccess() {
// The sub-device is logged in by proxy.
// After the sub-device is online, you can subscribe to topics, publish messages, and delete or disable the sub-device.
// subDevDisable(null);
// subDevDelete(null);
}
@Override
public void onFailed(AError aError) {
ALog.d(TAG, "onFailed() called with: aError = [" + aError + "]");
}
});
}
}