设备在车间,老板在北京——Modbus 数据怎么走出局域网?这一课讲云端数据采集的事实标准 MQTT:它为什么适合工控、Topic 怎么设计、一条 PUBLISH 报文到底长什么样。这也是整个系列的收官课。
>
看完这一课,数据就能从秤台一路走到手机 App。
上一课把数据搬到了网口,但 Modbus TCP 是"一问一答"的协议:云端想拿数据,就得穿过防火墙来问设备——这在现场网络里几乎不可行。
MQTT 反过来:设备主动把数据发出去。它不点对点,而是"发布-订阅"模式。先打个比方:

设备把消息投进快递柜(发布到 Broker 的某个主题)
平台凭取件码去取(订阅同一个主题)
收件人和寄件人从头到尾不用见面

三个角色:
| 角色 | 是谁 | 职责 |
|---|---|---|
| Publisher 发布者 | 设备 / 网关 | 只管发 |
| Broker 中转服务器 | EMQX / Mosquitto,端口 1883 | 只管转发 |
| Subscriber 订阅者 | 云平台 | 只管收 |
Broker 负责转发,两边都不用知道对方的 IP 和端口——这对隔着防火墙、NAT 的现场网络是决定性的优势:设备只需主动连出一条 TCP 就够了。
工控爱用它还有三个理由:
一条长连接:TCP 建一次连接反复用,不象 HTTP 每次握手
心跳只有 2 个字节(C0 00),对 4G/物联卡的流量极其友好
断线自动重连,MQTT 协议层面就定义了重连语义
MQTT 不需要配置"发给谁",只需要给消息贴一个 Topic(主题)。

factory/line1/scale01/weight │ │ │ └─ 数据点:重量 │ │ └─ 设备号 │ └─ 产线 └─ 工厂
主题用斜杠分层,一层一层往下排。订阅端可以精确订阅这一条,也可以用通配符批量订阅:

| 通配符 | 含义 | 示例 |
|---|---|---|
+ | 匹配一层 | factory/+/scale01/weight → 所有产线的 1 号秤 |
# | 匹配后面所有层 | factory/# → 这个工厂的一切数据 |
两个禁区:
# 只能放在最后一级,factory/#/weight 非法
发布消息时不允许带通配符,通配符只用于订阅
网络不可靠,"发过去了"和"收到了"是两回事。MQTT 定义三档服务质量:

| QoS | 名称 | 机制 | 适用 |
|---|---|---|---|
| 0 | 最多一次 | 发完就忘,丢了不补 | 高频温度等可覆盖数据 |
| 1 | 至少一次 | 等确认,可能重复 | 一般传感器数据 |
| 2 | 恰好一次 | 四次握手,绝不重不漏 | 报警、计费 |
传感器数据用 0 或 1 就够(下一个值马上会来,丢了无所谓);报警和计费才需要 2。QoS 越高开销越大,全用 2 反而把系统拖垮。
MQTT 报文非常精简,只有三块:

固定头第一个字节 30,拆成两半:
高 4 位 0011 = 3,即 PUBLISH(发布)。常用类型:连接 = 1、发布 = 3、订阅 = 8、心跳 = 12
低 4 位 = 标志位
固定头第二个字节 56 是剩余长度:十六进制 0x56 = 86,表示它后面还有 86 个字节。
我们发布主题 factory/line1/scale01/weight 和一段 JSON,完整报文:

| 段 | 字节 | 内容 |
|---|---|---|
| 固定头 | 2 | 30 56(发布 + 剩余长度 86) |
| 主题长度 | 2 | 00 1C(0x1C = 28) |
| 主题名 | 28 | factory/line1/scale01/weight 的 ASCII |
| 负载 | 56 | 一段 JSON |
总长 2 + 28 + 58 = 88 字节。可变头里最关键的就是"主题长度 + 主题名",后面跟着的才是真正要传的数据。
负载里放 JSON:
{"sn":"scale01","weight":255600,"dec":2,"ts":1699000000}sn:设备编号
weight:发整数 255600,不发 2556.00
dec:精度(小数点左移 2 位)
ts:时间戳
为什么不直接发 2556.00? 因为 JSON 里没法表达"保留两位小数"这个信息——2556.0、2556.00 在解析后都会变成同一个数。所以行业惯例是发整数 + 精度字段,显示端自己移小数点。这和 Modbus 时代的 255600 + dec=2 是同一套思路,链路上不用做任何换算。
别忘了时间戳:平台要靠它画曲线、算平均值、做报警。设备端和平台端时钟不一致时,曲线会整体错位。

保留消息(Retain):Broker 把该主题的最后一条消息存下来,新上线的订阅者一连接就能立刻拿到当前值,不用干等下一轮发布。适合"当前重量"这种状态型数据。
遗嘱消息(Will):设备连接时预先登记一条"遗嘱";一旦设备异常掉线(不是正常断开),Broker 替它把遗嘱发出去——scale01 offline。平台不用做任何探测逻辑,就能第一时间知道设备离线。
安全三件套,一样都不能少:
用户名 + 密码认证
TLS 加密:明文端口 1883,加密端口 8883
主题权限:设备只能发布自己的主题,防止串数据
最常见的坑是 clientId(客户端 ID)重复:MQTT 规定同一个 clientId 只能存在一条连接,两台设备配了相同的 ID,就会互相把对方踢下线,表现为"连接上了又莫名其妙断开、循环往复"。规则:每台设备的 clientId 必须全局唯一,比如用设备序列号。

一条数据走完的完整旅程:
秤台(应变片) → 变送器(SJ101CX) → RS485/Modbus RTU → PLC 轮询(ADPRW) → 网关拼 JSON → MQTT 发布 → Broker → 云平台 → 手机 App
整个系列用四个比喻串起来:
串口是手,Modbus 是语言,TCP 是公路,MQTT 是邮政系统。
| 课 | 你学会了 |
|---|---|
| 1 | 串口 / RS232 / RS485 / 协议 / ASCII 与十六进制 |
| 2 | Modbus RTU 主从、帧结构、CRC、RS485 组网 |
| 3 | 应变片、惠斯通电桥、两点标定、32 位重量 |
| 4 | 串口助手实战:读 255600 → 2556.00 kg、清零 |
| 5 | PLC 轮询:ADPRW、字序大坑、超时跳站 |
| 6 | Modbus TCP:MBAP、事务号、单元号 |
| 7 | MQTT:发布订阅、Topic、QoS、报文构造、上云 |
合集按顺序看,下个项目见!