4、MQTT客户端库实战(一):使用Eclipse Paho Java客户端连接ThingsBoard,发送遥测与属性数据。

好,咱们进入实战环节了。前面几章我们把MQTT协议和ThingsBoard的通信机制讲了个透,现在该动手写代码了。这一章,我带你用Eclipse Paho Java客户端库,把ThingsBoard连起来,然后发点遥测数据和属性数据上去。

说实话,Paho这个库是我用得最多的Java MQTT客户端。它成熟、稳定,社区活跃。我在好几个物联网项目里都用它,从智能路灯到工业设备监控,从来没出过幺蛾子。你想想看,一个库能让你这么放心,那肯定是有道理的。

4.1 环境准备与依赖引入

先搞定开发环境。我假设你已经有了Java开发环境,JDK 8以上就行。我用的是Maven项目,如果你用Gradle,原理一样,改一下依赖坐标就行。

pom.xml里加入这个依赖:

<dependency>
    <groupId>org.eclipse.paho</groupId>
    <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
    <version>1.2.5</version>
</dependency>

嗯,版本号我建议用1.2.5,这是目前最稳定的版本。我曾经试过用最新的1.2.6,结果发现有个小bug,在重连逻辑上有点问题,后来还是退回了1.2.5。所以,别追新,稳定为王。

4.2 创建MQTT客户端并连接ThingsBoard

接下来,我们创建一个MQTT客户端,连接到ThingsBoard。这里有几个关键参数:

  • Broker地址:ThingsBoard的MQTT Broker地址,通常是tcp://your-thingsboard-host:1883。如果你用了SSL,那就是ssl://...,端口是8883。
  • Client ID:每个设备必须有唯一的Client ID。在ThingsBoard里,这个ID就是设备的访问令牌(Access Token)
  • Clean Session:我建议设为true,这样每次连接都是全新会话,避免旧消息干扰。

看代码:

import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

public class ThingsBoardMqttClient {

    private static final String BROKER = "tcp://demo.thingsboard.io:1883";
    private static final String ACCESS_TOKEN = "YOUR_DEVICE_ACCESS_TOKEN";
    private static final String CLIENT_ID = ACCESS_TOKEN; // 注意:Client ID就是Access Token

    private MqttClient client;

    public void connect() throws MqttException {
        // 使用内存持久化,不存盘
        MemoryPersistence persistence = new MemoryPersistence();
        client = new MqttClient(BROKER, CLIENT_ID, persistence);

        MqttConnectOptions options = new MqttConnectOptions();
        options.setCleanSession(true);
        options.setConnectionTimeout(10);
        options.setKeepAliveInterval(60);

        System.out.println("正在连接 ThingsBoard...");
        client.connect(options);
        System.out.println("连接成功!");
    }
}

关键点:Client ID必须等于Access Token。这是ThingsBoard的硬性要求,少一个字符都不行。我刚开始做的时候,以为Client ID可以随便起,结果连了半天连不上,查日志才发现是这个问题。

4.3 发送遥测数据

连接上了,就该干活了。遥测数据,说白了就是设备上报的实时数据,比如温度、湿度、电压这些。ThingsBoard要求遥测数据以JSON格式发送,并且要发布到特定的Topic上。

Topic格式:v1/devices/me/telemetry

来看一个完整的发送示例:

public void sendTelemetry() throws MqttException {
    // 构造遥测数据JSON
    String telemetryJson = "{"
            + "\"temperature\": 25.6,"
            + "\"humidity\": 68.2,"
            + "\"voltage\": 220.5"
            + "}";

    MqttMessage message = new MqttMessage(telemetryJson.getBytes());
    message.setQos(1); // QoS 1,确保至少送达一次

    client.publish("v1/devices/me/telemetry", message);
    System.out.println("遥测数据已发送: " + telemetryJson);
}

这里我用了QoS 1。为什么不用QoS 0?因为遥测数据虽然允许丢失,但在工业场景下,丢一个数据点可能就意味着错过一次告警。QoS 2又太重了,没必要。所以QoS 1是折中的好选择。我在项目里一直这么用,没出过问题。

小技巧:如果你要批量发送多个数据点,可以把它们放在一个JSON数组里。比如:[{"ts": 1630000000000, "values": {"temperature": 25.6}}, ...]。这样一次发布就能上报多条记录,效率更高。

4.4 发送属性数据

属性数据和遥测数据不同。属性通常是不怎么变化的数据,比如设备型号、固件版本、安装位置等。ThingsBoard把属性分为两类:客户端属性服务端属性。我们这里发的是客户端属性,也就是设备主动上报的属性。

Topic格式:v1/devices/me/attributes

代码示例:

public void sendAttributes() throws MqttException {
    String attributesJson = "{"
            + "\"firmwareVersion\": \"1.2.3\","
            + "\"deviceModel\": \"TB-100\","
            + "\"installationDate\": \"2024-01-15\""
            + "}";

    MqttMessage message = new MqttMessage(attributesJson.getBytes());
    message.setQos(1);

    client.publish("v1/devices/me/attributes", message);
    System.out.println("属性数据已发送: " + attributesJson);
}

你看,代码结构和发送遥测几乎一样,只是Topic不同。这就是ThingsBoard设计得好的地方——统一了通信模式,降低了学习成本。

4.5 完整示例:连接、发送、断开

把上面几段代码串起来,就是一个完整的客户端程序了。我习惯在main方法里按顺序调用:

public static void main(String[] args) {
    ThingsBoardMqttClient tbClient = new ThingsBoardMqttClient();
    try {
        tbClient.connect();
        tbClient.sendTelemetry();
        tbClient.sendAttributes();
        // 记得断开连接,释放资源
        tbClient.client.disconnect();
        System.out.println("已断开连接。");
    } catch (MqttException e) {
        System.err.println("发生错误: " + e.getMessage());
        e.printStackTrace();
    }
}

注意:一定要在程序结束时调用disconnect()。我曾经在一个长期运行的服务里忘了断开连接,结果导致连接池被占满,新设备连不上来。排查了半天才发现是这个问题。嗯,血的教训。

4.6 验证数据是否到达ThingsBoard

代码跑起来了,怎么知道数据真的到了ThingsBoard呢?最简单的方法:登录ThingsBoard的Web界面,找到你的设备,查看"最新遥测"和"属性"页面。

如果数据正确显示,恭喜你,你已经成功迈出了第一步。如果没显示,别急,先检查这几个地方:

  • Access Token是否正确? 复制粘贴的时候容易多一个空格。
  • Broker地址和端口是否可达? 用telnet测试一下。
  • JSON格式是否合法? 可以用在线JSON校验工具检查。

我记得有一次,我怎么发数据ThingsBoard都不收,最后发现是JSON里多了一个逗号。这种小错误最坑人,所以我现在每次发数据前都会先打印一下JSON字符串,肉眼检查一遍。

4.7 小结

这一章我们做了三件事:创建Paho客户端连接ThingsBoard、发送遥测数据、发送属性数据。代码量不大,但每一步都是基础中的基础。你想想看,后面所有的复杂功能——比如设备固件升级、远程控制、告警触发——都是建立在这几个简单操作之上的。

下一章,我们会深入Paho客户端的回调机制,实现消息订阅和实时响应。到时候,你的设备就能"听"到ThingsBoard下发的指令了。做好准备,咱们继续。