物联网平台实用技巧:设备端检测自己是否在线

简介: 基于MQTT接入的设备靠心跳保活,但心跳是周期性的、且自动收发和超时重连,这些特性给主动检测设备端是否在线带来了一定难度。本文提供通过消息收发是否正常判定设备是否在线的原理、流程、实现方式。

产品推荐:阿里云物联网开发者工具(IoT Studio),立刻免费体验吧!  
 

原理

如果设备可以发送、接收消息,那么该设备的通信是没问题的,并且一定在线。
消息收发是物联网平台的核心能力。因此,这种判定方法不会因为物联网平台架构升级或业务变动而变化,也不会因为设备使用的客户端不同而不同。是设备端检测自己是否在线最通用的一种原理。
该原理的一种特殊实现就是设备端消息的自发自收。

流程

1、创建Topic=`js/yourProductKey/yourDeviceName/user/checkstatus

可以自定义,但权限必须为发布和订阅。
2、设备端订阅上一步创建的Topic 。
设备端发送消息```js{"id":123,"version":"1.0","time":1234567890123}

,请一定使用QoS=0 。
消息内容可自定义,但建议使用此格式。
参数说明:

字段 类型 说明
id Object 用于验证收发的消息是否是同一个,请自行业务层保证唯一
version String 版本号固定1.0
time Long 发送消息的时间戳,可以计算消息来回的延时,评估当前的通信质量

4、设备端收到消息上一步发送的消息。
离线判定逻辑

  • 严格的:发送消息后,5秒内没有收到消息算失败,出现1次失败,判定为离线
  • 普通的:发送消息后,5秒内没有收到消息算失败,连续2次失败,判定为离线
  • 宽松的:发送消息后,5秒内没有收到消息算失败,连续3次失败,判定为离线
    说明 您可以根据自己的情况,自定义离线判定逻辑。

实现

为方便体验,本例基于Java SDK Demo开发,实现设备端检测自己是否在线的严格判定逻辑。
Java SDK开发具体细节,请查看相关文档。
说明 您可以根据自己的喜好,选择不同的设备端SDK进行开发。
首先,下载Demo工程,添加本类,并填写设备证书信息。设备端代码如下:


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 {

    // ===================需要用户填写的参数,开始===========================
    // 产品productKey,设备证书参数之一
    private static String productKey = "";
    // 设备名字deviceName,设备证书参数之一
    private static String deviceName = "";
    // 设备密钥deviceSecret,设备证书参数之一
    private static String deviceSecret = "";
    // 消息通信的Topic,需要在控制台定义,权限必须为发布和订阅
    private static String checkStatusTopic = "/" + productKey + "/" + deviceName + "/user/checkstatus";
    // ===================需要用户填写的参数结束===========================

    // 接收到的消息
    private static String subInfo = "";

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

        CheckDeviceStatusOnDevice device = new CheckDeviceStatusOnDevice();

        // 初始化
        device.init(productKey, deviceName, deviceSecret);

        // 下行数据监听
        device.registerNotifyListener();

        // 订阅Topic
        device.subscribe(checkStatusTopic);

        // 测试设备状态
        System.out.println("we will check device online status now.");
        device.checkStatus();

        // 准备测试设备离线状态,请拔掉网线
        System.out.println("pls close network,we will check device offline status after 60 seconds.");
        for (int i = 0; i < 6; i++) {
            Thread.sleep(10000);
        }
        device.checkStatus();
    }

    /**
     * 测试设备状态
     * 
     * @throws InterruptedException
     */
    public void checkStatus() throws InterruptedException {

        // -------------------------------------------------------------------
        // 要发送的消息,可以自定义,建议使用当前格式
        // -------------------------------------------------------------------
        // Field   | Tyep   | Desc
        // -------------------------------------------------------------------
        // id      | Object | 用于验证收发的消息是否是同一个,请自行业务层保证唯一
        // -------------------------------------------------------------------
        // version | String | 版本号固定1.0
        // -------------------------------------------------------------------
        // time    | Long   | 发送消息的时间戳,可以计算消息来回的延时,评估当前的通信质量
        // -------------------------------------------------------------------
        String payload = "{\"id\":123, \"version\":\"1.0\",\"time\":" + System.currentTimeMillis() + "}";

        // 发送消息
        publish(checkStatusTopic, payload);

        // 严格的离线判定逻辑:发送消息后,5秒内没有收到消息算失败,出现1次失败,判定为离线
        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 !!");
        }

        // 置空接收到的消息,方便下一次测试
        subInfo = "";
    }

    /**
     * 初始化
     * 
     * @param pk productKey
     * @param dn devcieName
     * @param ds deviceSecret
     * @throws InterruptedException
     */
    public void init(String pk, String dn, String ds) throws InterruptedException {

        LinkKitInitParams params = new LinkKitInitParams();

        // 设置 MQTT 初始化参数
        IoTMqttClientConfig config = new IoTMqttClientConfig();
        config.productKey = pk;
        config.deviceName = dn;
        config.deviceSecret = ds;
        params.mqttClientConfig = config;

        // 设置初始化设备证书信息,用户传入
        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("init success !!");
            }

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

        // 确保初始化成功后才执行后面的步骤,可以根据实际情况适当延长这里的延时
        Thread.sleep(2000);
    }

    /**
     * 监听下行数据
     */
    public void registerNotifyListener() {
        LinkKit.getInstance().registerOnNotifyListener(new IConnectNotifyListener() {
            @Override
            public boolean shouldHandle(String connectId, String topic) {
                // 只处理特定Topic的消息
                if (checkStatusTopic.equals(topic)) {
                    return true;
                } else {
                    return false;
                }
            }

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

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

    /**
     * 发布消息
     * 
     * @param topic 发送消息的Topic
     * @param payload 发送的消息内容
     */
    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) {
            }
        });
    }

    /**
     * 订阅消息
     * 
     * @param topic 订阅消息的Topic
     */
    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) {
            }
        });
    }

}

说明 检测到设备离线后,尽量不要选择主动重连。
物联网平台规定一个设备1分钟只允许尝试接入平台5次,超过会触发限流,限制设备接入。此时停止接入,等待1分钟即可解除限制。
设备端应注意退避,切勿触发限流。```  
相关实践学习
钉钉群中如何接收IoT温控器数据告警通知
本实验主要介绍如何将温控器设备以MQTT协议接入IoT物联网平台,通过云产品流转到函数计算FC,调用钉钉群机器人API,实时推送温湿度消息到钉钉群。
阿里云AIoT物联网开发实战
本课程将由物联网专家带你熟悉阿里云AIoT物联网领域全套云产品,7天轻松搭建基于Arduino的端到端物联网场景应用。 开始学习前,请先开通下方两个云产品,让学习更流畅: IoT物联网平台:https://iot.console.aliyun.com/ LinkWAN物联网络管理平台:https://linkwan.console.aliyun.com/service-open
目录
相关文章
|
13天前
|
物联网
DC/AC电源模块:为物联网设备提供可靠的电力支持
DC/AC电源模块:为物联网设备提供可靠的电力支持
DC/AC电源模块:为物联网设备提供可靠的电力支持
|
13天前
|
安全 物联网
BOSHIDA DC/AC电源模块:为物联网设备提供可靠的电力支持
BOSHIDA DC/AC电源模块:为物联网设备提供可靠的电力支持
BOSHIDA DC/AC电源模块:为物联网设备提供可靠的电力支持
|
1月前
|
监控 安全 Java
Java与物联网:连接与控制设备
Java与物联网:连接与控制设备
83 0
|
7天前
|
安全 物联网 网络安全
智能家居安全:物联网设备的风险与防护
【6月更文挑战第18天】随着科技的飞速发展,智能家居设备已经深入人们的日常生活。然而,这些便捷的智能设备背后隐藏着不容忽视的安全风险。本文将探讨智能家居中常见的安全威胁,并提供有效的预防措施,帮助用户构建一个更加安全的智能家居环境。
|
22天前
|
传感器 物联网 Linux
物联网设备的操作系统之争:Linux vs RTOS
【6月更文挑战第4天】在遥远的数码星球,物联网城中的Linux先生与RTOS小姐展开激烈角逐,分别在操作系统领域各显神通。Linux先生以其开源、兼容性强、功能丰富占据服务器、桌面及嵌入式设备市场,适合处理复杂任务和需要强大计算能力的设备。而RTOS小姐以实时性、高效响应和低资源占用见长,适用于资源有限、强调实时性的物联网设备。设备制造商在两者间抉择,引发物联网设备操作系统的选择大战。通过Python与FreeRTOS示例,展现了两者在智能家居和生产线控制等场景的应用。在物联网世界,Linux与RTOS共同推动设备智能化,为生活带来更多便捷。
65 3
|
24天前
|
物联网 传感器 安全
物联网设备的能效管理与优化
【6月更文挑战第2天】在物联网时代,能效管理成为关键议题。有效管理能避免设备过度消耗能源,减少浪费并保护环境。通过动态调整能源供应、选用低能耗硬件、智能算法优化及高效数据处理,可实现能效优化。示例代码展示了软件控制节能模式。能效管理应用于农业和城市,实现精准操作和节能。这是科技与环保的融合,助力可持续未来,让物联网世界更美好。
29 2
|
7天前
|
边缘计算 监控 物联网
边缘计算在物联网设备中的应用与优化
边缘计算在物联网设备中的应用与优化
12 0
|
1月前
|
传感器 存储 监控
物联网设备
【5月更文挑战第16天】物联网设备
28 5
|
1月前
|
安全 网络协议 物联网
如何提高物联网设备的安全性以应对不断变化的安全威胁?
【5月更文挑战第15天】如何提高物联网设备的安全性以应对不断变化的安全威胁?
33 2
|
26天前
|
存储 安全 物联网
物联网设备的安全挑战与解决方案
【5月更文挑战第31天】随着物联网技术的发展,大量设备联网带来严重安全挑战。设备计算能力有限,易受黑客攻击;多样性和复杂性增加管理难度;环境暴露及用户安全意识薄弱也是问题。解决方法包括:增强设备身份认证、定期更新软件、实施网络隔离和访问控制、加密数据以及建立安全监测机制。Python 示例展示了数据加密方法。提升用户安全意识同样关键。综合施策,强化安全研究,保障物联网设备安全,促进行业健康发展。重视物联网安全,打造安全可靠的数字环境。
29 0

热门文章

最新文章

相关产品

  • 物联网平台