第七课 · Modbus 数据上云:MQTT 协议与报文构造

2026-11-04
  • 4

第七课 · Modbus 数据上云:MQTT 协议与报文构造

设备在车间,老板在北京——Modbus 数据怎么走出局域网?这一课讲云端数据采集的事实标准 MQTT:它为什么适合工控、Topic 怎么设计、一条 PUBLISH 报文到底长什么样。这也是整个系列的收官课。

>

看完这一课,数据就能从秤台一路走到手机 App。

1. 为什么上云要用 MQTT

上一课把数据搬到了网口,但 Modbus TCP 是"一问一答"的协议:云端想拿数据,就得穿过防火墙来问设备——这在现场网络里几乎不可行。

MQTT 反过来:设备主动把数据发出去。它不点对点,而是"发布-订阅"模式。先打个比方:

快递柜比喻

  • 设备把消息投进快递柜(发布到 Broker 的某个主题)

  • 平台凭取件码去取(订阅同一个主题)

  • 收件人和寄件人从头到尾不用见面

三个角色

三个角色:

角色是谁职责
Publisher 发布者设备 / 网关只管发
Broker 中转服务器EMQX / Mosquitto,端口 1883只管转发
Subscriber 订阅者云平台只管收

Broker 负责转发,两边都不用知道对方的 IP 和端口——这对隔着防火墙、NAT 的现场网络是决定性的优势:设备只需主动连出一条 TCP 就够了。

工控爱用它还有三个理由:

  1. 一条长连接:TCP 建一次连接反复用,不象 HTTP 每次握手

  2. 心跳只有 2 个字节(C0 00),对 4G/物联卡的流量极其友好

  3. 断线自动重连,MQTT 协议层面就定义了重连语义


2. Topic:消息发到哪

MQTT 不需要配置"发给谁",只需要给消息贴一个 Topic(主题)。

主题分层

factory/line1/scale01/weight
  │      │      │      └─ 数据点:重量
  │      │      └─ 设备号
  │      └─ 产线
  └─ 工厂

主题用斜杠分层,一层一层往下排。订阅端可以精确订阅这一条,也可以用通配符批量订阅:

通配符

通配符含义示例
+匹配一层factory/+/scale01/weight → 所有产线的 1 号秤
#匹配后面所有层factory/# → 这个工厂的一切数据

两个禁区:

  1. # 只能放在最后一级,factory/#/weight 非法

  2. 发布消息时不允许带通配符,通配符只用于订阅


3. QoS:消息质量三档

网络不可靠,"发过去了"和"收到了"是两回事。MQTT 定义三档服务质量:

QoS 三档

QoS名称机制适用
0最多一次发完就忘,丢了不补高频温度等可覆盖数据
1至少一次等确认,可能重复一般传感器数据
2恰好一次四次握手,绝不重不漏报警、计费

传感器数据用 0 或 1 就够(下一个值马上会来,丢了无所谓);报警和计费才需要 2。QoS 越高开销越大,全用 2 反而把系统拖垮。


4. 报文结构:固定头 / 可变头 / 负载

MQTT 报文非常精简,只有三块:

报文三段

固定头第一个字节 30,拆成两半:

  • 高 4 位 0011 = 3,即 PUBLISH(发布)。常用类型:连接 = 1、发布 = 3、订阅 = 8、心跳 = 12

  • 低 4 位 = 标志位

固定头第二个字节 56 是剩余长度:十六进制 0x56 = 86,表示它后面还有 86 个字节。


5. 一条完整的 PUBLISH:88 字节逐段拆

我们发布主题 factory/line1/scale01/weight 和一段 JSON,完整报文:

主题 28 字节 + 负载

段字节内容
固定头230 56(发布 + 剩余长度 86)
主题长度200 1C(0x1C = 28)
主题名28factory/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 是同一套思路,链路上不用做任何换算。

别忘了时间戳:平台要靠它画曲线、算平均值、做报警。设备端和平台端时钟不一致时,曲线会整体错位。


6. 两个好用的开关

保留与遗嘱

保留消息(Retain):Broker 把该主题的最后一条消息存下来,新上线的订阅者一连接就能立刻拿到当前值,不用干等下一轮发布。适合"当前重量"这种状态型数据。

遗嘱消息(Will):设备连接时预先登记一条"遗嘱";一旦设备异常掉线(不是正常断开),Broker 替它把遗嘱发出去——scale01 offline。平台不用做任何探测逻辑,就能第一时间知道设备离线。


7. 安全与一个最常见的坑

安全三件套,一样都不能少:

  1. 用户名 + 密码认证

  2. TLS 加密:明文端口 1883,加密端口 8883

  3. 主题权限:设备只能发布自己的主题,防止串数据

最常见的坑是 clientId(客户端 ID)重复:MQTT 规定同一个 clientId 只能存在一条连接,两台设备配了相同的 ID,就会互相把对方踢下线,表现为"连接上了又莫名其妙断开、循环往复"。规则:每台设备的 clientId 必须全局唯一,比如用设备序列号。


8. 全链路收官

全链路

一条数据走完的完整旅程:

秤台(应变片) → 变送器(SJ101CX) → RS485/Modbus RTU
  → PLC 轮询(ADPRW) → 网关拼 JSON → MQTT 发布
  → Broker → 云平台 → 手机 App

整个系列用四个比喻串起来:

串口是手,Modbus 是语言,TCP 是公路,MQTT 是邮政系统。
课你学会了
1串口 / RS232 / RS485 / 协议 / ASCII 与十六进制
2Modbus RTU 主从、帧结构、CRC、RS485 组网
3应变片、惠斯通电桥、两点标定、32 位重量
4串口助手实战:读 255600 → 2556.00 kg、清零
5PLC 轮询:ADPRW、字序大坑、超时跳站
6Modbus TCP:MBAP、事务号、单元号
7MQTT:发布订阅、Topic、QoS、报文构造、上云

合集按顺序看,下个项目见!

微信二维码

扫码添加微信