Check device online status from the device

Updated at:
Copy as MD

Devices that connect using MQTT use heartbeats to maintain their online status. Because heartbeats are periodic, sent and received automatically, and trigger a reconnection on timeout, it is difficult to actively check whether a device is online. This topic describes how to determine the online status of a device by checking whether it can send and receive messages. This topic covers the principle, process, and implementation.

Principle

If a device can send and receive messages, its communication link is working correctly and the device is considered online.

Sending and receiving messages is a core feature of IoT Platform. Therefore, this method is unaffected by platform upgrades or business changes. It also works regardless of the client that the device uses, which makes it the most universal way for a device to check its own online status.

To implement this principle, a device sends a message and then verifies that it receives the same message.

Process

  1. Create a topic named /yourProductKey/yourDeviceName/user/checkstatus.

    You can customize the topic name, but the topic must have both publish and subscribe permissions.

  2. Subscribe the device to the topic that you created in the previous step.
  3. From the device, send a message, such as the following, and set the Quality of Service (QoS) to 0.{"id":123,"version":"1.0","time":1234567890123}

    You can customize the message content. We recommend that you use the following format.

    The following table describes the parameters.

    FieldTypeDescription
    idObjectUsed to verify that the sent and received messages are the same. Ensure uniqueness at the business layer.
    versionStringThe version number. Fixed at 1.0.
    timeLongThe timestamp when the message was sent. You can use it to calculate the round-trip delay and assess the current communication quality.
  4. The device waits to receive the message that it sent in the previous step.

    Determining offline status

    • Strict: The device is considered offline if it fails to receive the message within 5 seconds after the message is sent.
    • Normal: The device is considered offline if it fails to receive the message within 5 seconds in two consecutive attempts.
    • Loose: The device is considered offline if it fails to receive the message within 5 seconds in three consecutive attempts.
    Note You can customize the offline determination logic as needed.

Implementation

This example is based on the Java SDK Demo and implements strict logic to determine whether a device is online.

For more information about Java SDK development, see the related document.
Note You can choose a different device SDK for development as needed.

First, download the demo project. Then, add the class and enter your device certificate information. The device-side code is as follows:

import java.io.UnsupportedEncodingException;

import com.aliyun.alink.dm.api.DeviceInfo;
import com.aliyun.alink.dm.api.InitResult;
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.cmp.connect.channel.MqttPublishRequest;
import com.aliyun.alink.linksdk.cmp.connect.channel.MqttSubscribeRequest;
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.base.ConnectState;
import com.aliyun.alink.linksdk.cmp.core.listener.IConnectNotifyListener;
import com.aliyun.alink.linksdk.cmp.core.listener.IConnectSendListener;
import com.aliyun.alink.linksdk.cmp.core.listener.IConnectSubscribeListener;
import com.aliyun.alink.linksdk.tools.AError;

public class CheckDeviceStatusOnDevice {

    // ===================Parameters to configure: start===========================
    // The ProductKey, a device certificate parameter.
    private static String productKey = "";
    // The DeviceName, a device certificate parameter.
    private static String deviceName = "";
    // The DeviceSecret, a device certificate parameter.
    private static String deviceSecret = "";
    // The topic for message communication. You must define this topic in the console and grant it publish and subscribe permissions.
    private static String checkStatusTopic = "/" + productKey + "/" + deviceName + "/user/checkstatus";
    // ===================Parameters to configure: end===========================

    // The received message.
    private static String subInfo = "";

    public static void main(String[] args) throws InterruptedException {

        CheckDeviceStatusOnDevice device = new CheckDeviceStatusOnDevice();

        // Initialize.
        device.init(productKey, deviceName, deviceSecret);

        // Listen for downstream data.
        device.registerNotifyListener();

        // Subscribe to the topic.
        device.subscribe(checkStatusTopic);

        // Test the device status.
        System.out.println("We will now check the device online status.");
        device.checkStatus();

        // Prepare to test the offline status. Unplug the network cable.
        System.out.println("Please disconnect the network. We will check the device offline status in 60 seconds.");
        for (int i = 0; i < 6; i++) {
            Thread.sleep(10000);
        }
        device.checkStatus();
    }

    /**
     * Tests the device status.
     * 
     * @throws InterruptedException
     */
    public void checkStatus() throws InterruptedException {

        // -------------------------------------------------------------------
        // The message to send. You can customize it, but the current format is recommended.
        // -------------------------------------------------------------------
        // Field   | Type   | Desc
        // -------------------------------------------------------------------
        // id      | Object | Used to verify that the sent and received messages are the same. Ensure uniqueness at the business layer.
        // -------------------------------------------------------------------
        // version | String | The version number. Must be 1.0.
        // -------------------------------------------------------------------
        // time    | Long   | The timestamp when the message is sent. You can use it to calculate the round-trip delay and assess communication quality.
        // -------------------------------------------------------------------
        String payload = "{\"id\":123, \"version\":\"1.0\",\"time\":" + System.currentTimeMillis() + "}";

        // Send the message.
        publish(checkStatusTopic, payload);

        // Strict offline logic: If the message is not received within 5 seconds of sending, the attempt fails. A single failure means the device is offline.
        boolean isTimeout = true;
        for (int i = 0; i < 5; i++) {
            Thread.sleep(1000);
            if (!subInfo.isEmpty()) {
                isTimeout = false;
                break;
            }
        }
        if (!isTimeout && payload.equals(subInfo)) {
            System.out.println("Device is online!!");
        } else {
            System.out.println("Device is offline!!");
        }

        // Clear the received message to prepare for the next test.
        subInfo = "";
    }

    /**
     * Initializes the client.
     * 
     * @param pk productKey
     * @param dn deviceName
     * @param ds deviceSecret
     * @throws InterruptedException
     */
    public void init(String pk, String dn, String ds) throws InterruptedException {

        LinkKitInitParams params = new LinkKitInitParams();

        // Set MQTT initialization parameters.
        IoTMqttClientConfig config = new IoTMqttClientConfig();
        config.productKey = pk;
        config.deviceName = dn;
        config.deviceSecret = ds;
        params.mqttClientConfig = config;

        // Set the device certificate information provided by the user.
        DeviceInfo deviceInfo = new DeviceInfo();
        deviceInfo.productKey = pk;
        deviceInfo.deviceName = dn;
        deviceInfo.deviceSecret = ds;

        params.deviceInfo = deviceInfo;

        LinkKit.getInstance().init(params, new ILinkKitConnectListener() {
            @Override
            public void onInitDone(InitResult initResult) {
                System.out.println("Initialization successful!!");
            }

            @Override
            public void onError(AError aError) {
                System.out.println("Initialization failed!! code=" + aError.getCode() + ",msg=" + aError.getMsg() + ",subCode="
                        + aError.getSubCode() + ",subMsg=" + aError.getSubMsg());
            }
        });

        // Proceed to the next steps only after a successful initialization. You can extend this delay if needed.
        Thread.sleep(2000);
    }

    /**
     * Listens for downstream data.
     */
    public void registerNotifyListener() {
        LinkKit.getInstance().registerOnNotifyListener(new IConnectNotifyListener() {
            @Override
            public boolean shouldHandle(String connectId, String topic) {
                // Process messages only from the specified topic.
                if (checkStatusTopic.equals(topic)) {
                    return true;
                } else {
                    return false;
                }
            }

            @Override
            public void onNotify(String connectId, String topic, AMessage aMessage) {
                // Receive the message.
                try {
                    subInfo = new String((byte[]) aMessage.getData(), "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onConnectStateChange(String connectId, ConnectState connectState) {
            }
        });
    }

    /**
     * Publishes a message.
     * 
     * @param topic The topic for publishing the message.
     * @param payload The message content.
     */
    public void publish(String topic, String payload) {
        MqttPublishRequest request = new MqttPublishRequest();
        request.topic = topic;
        request.payloadObj = payload;
        request.qos = 0;
        LinkKit.getInstance().getMqttClient().publish(request, new IConnectSendListener() {
            @Override
            public void onResponse(ARequest aRequest, AResponse aResponse) {
            }

            @Override
            public void onFailure(ARequest aRequest, AError aError) {
            }
        });
    }

    /**
     * Subscribes to a topic.
     * 
     * @param topic The topic to subscribe to.
     */
    public void subscribe(String topic) {
        MqttSubscribeRequest request = new MqttSubscribeRequest();
        request.topic = topic;
        LinkKit.getInstance().getMqttClient().subscribe(request, new IConnectSubscribeListener() {
            @Override
            public void onSuccess() {
            }

            @Override
            public void onFailure(AError aError) {
            }
        });
    }

}
Note If you detect that a device is offline, avoid making active reconnection attempts.

IoT Platform allows a device to make a maximum of five connection attempts per minute. If this limit is exceeded, throttling is triggered and device access is restricted. If this occurs, stop the connection attempts and wait for one minute for the restriction to be lifted.

The device must implement a backoff mechanism to prevent the throttling limit from being triggered.