主题
5.4 发布订阅示例
下面的示例都遵循 5.5 消息格式规范:主题名即数据点名,payload 用裸值。
5.4.1 命令行(mosquitto)
bash
# 上报单个值
mosquitto_pub -h 服务器IP -p 1883 -u 账号 -P 密码 -i dev01 \
-t "车间1/1号机/温度" -m "25.6" -q 1
# 一条消息上报多个值(得到 车间1/1号机/温度、车间1/1号机/湿度)
mosquitto_pub -h 服务器IP -p 1883 -u 账号 -P 密码 -i dev01 \
-t "车间1/1号机" -m '{"温度":25.6,"湿度":60}' -q 1
# 订阅云端下发
mosquitto_sub -h 服务器IP -p 1883 -u 账号 -P 密码 -i dev01 \
-t "车间1/1号机/启停" -q 15.4.2 Python(paho-mqtt)
python
import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, rc):
print("connected", rc)
client.subscribe("车间1/1号机/启停", qos=1)
def on_message(client, userdata, msg):
# 服务器下发默认格式为 {"value":新值}
print(msg.topic, msg.payload.decode())
c = mqtt.Client(client_id="dev01", protocol=mqtt.MQTTv311)
c.username_pw_set("账号", "密码")
c.on_connect = on_connect
c.on_message = on_message
c.connect("服务器IP", 1883, keepalive=60)
c.loop_start()
# 上报裸值
c.publish("车间1/1号机/温度", "25.6", qos=1)
# 批量上报(服务器会拆成 车间1/1号机/温度、车间1/1号机/湿度)
import json
c.publish("车间1/1号机", json.dumps({"温度": 25.6, "湿度": 60}, ensure_ascii=False), qos=1)5.4.3 Node.js(mqtt.js)
javascript
const mqtt = require('mqtt')
const client = mqtt.connect('mqtt://服务器IP:1883', {
clientId: 'dev01',
username: '账号',
password: '密码',
protocolVersion: 4, // 4 = MQTT 3.1.1
keepalive: 60,
})
client.on('connect', () => {
client.subscribe('车间1/1号机/启停', { qos: 1 })
client.publish('车间1/1号机/温度', '25.6', { qos: 1 })
})
client.on('message', (topic, payload) => {
console.log(topic, payload.toString()) // 下发默认为 {"value":新值}
})5.4.4 LP 设备 Lua 脚本
LP 设备接自家服务器一般用云交互绑定(3.2)就够了,这里给的是用标准 MQTT 接入的写法,适合要自定义主题和 payload 的场合。
lua
-- 客户端对象必须用全局变量长期持有,否则会被 GC 回收
mqttc = mqtt.creat("服务器IP", 1883, "dev01", "账号", "密码")
mqtt.set_version(mqttc, 4) -- 4 = MQTT 3.1.1
mqtt.set_keepalive(mqttc, 60)
-- 连接成功后再订阅最稳
mqtt.reg(mqttc, mqtt.CONNECTED, function()
mqtt.subscribe(mqttc, "车间1/1号机/启停", 1) -- topic, qos
end)
-- 接收回调签名固定为 (topic, payload)
mqtt.reg(mqttc, mqtt.RECVDATA, function(topic, payload)
if topic == "车间1/1号机/启停" then
-- 服务器下发 {"value":1},外部数据一律 pcall 保护
local ok, obj = pcall(json.decode, payload)
if ok and type(obj) == "table" and obj.value ~= nil then
plc.y(0, tonumber(obj.value) == 1) -- 写 Y0
end
end
end)
mqtt.start(mqttc) -- 内部线程自动重连
-- 定时上报:payload 使用裸值
t = timer.start(5000, function()
if mqtt.is_linked(mqttc) == 1 then -- 返回整数,必须 == 1
mqtt.publish(mqttc, "车间1/1号机/温度", tostring(plc.d(0) / 10), 1, 0)
end
end)三点需要注意:
mqtt.函数(客户端, ...)与客户端:函数(...)两种写法等价,creat只能用库表形式mqtt.is_linked()返回整数 0/1,Lua 里0为真,判断必须写== 1mqtt.publish()未连接时返回 nil(不是 false)
5.4.5 双向联动的完整例子
目标:温度上报到云端画面显示;画面上的按钮能远程启停设备。
设备侧
publish 车间1/1号机/温度 → 25.6 (上行)
subscribe 车间1/1号机/启停 ← {"value":1} (下行)云端
- 两个数据点在实时数据库里自动出现(
车间1/1号机/启停首次需要云端写一次才会创建,可在实时数据库页手动新建或用 HTTP APIset_real写一次) - 云组态里用项目变量(无前缀)绑定:显示控件绑
车间1/1号机/温度,按钮的点击事件用「设定数据」写车间1/1号机/启停 - 要历史曲线,在实时数据库里把温度点的存盘方式设为「瞬时型每分钟存盘」
- 要超温报警,在事件触发里做「数据变化 → 数值比较 → 限频 → 发送邮件」
5.4.6 通配符订阅
订阅支持 MQTT 标准通配符,适合做网关或调试监听:
| 通配符 | 含义 | 例子 |
|---|---|---|
+ | 匹配单层 | 车间1/+/温度 匹配 车间1/1号机/温度、车间1/2号机/温度 |
# | 匹配多层(只能放末尾) | 车间1/# 匹配 车间1 下所有层级 |
$ 开头的主题是 MQTT 系统保留主题,不会被通配符匹配到,所以 # 订阅不到它们。