主题
B10. 云端双向通信框架(订阅下发 + 周期上报 + 断线重发)
综合例程。MQTT 原语见
05-02-mqtt;完整骨架见05-02-mqtt-3-full-skeleton。 这一页把"远程开关 + 状态上报 + 断线不丢数据"拼成可直接照搬的框架。
适用:带网口的机型 | 依赖模块:mqtt timer plc | 难度:★★☆
场景:远端下发明令控制现场(开/关/设定值),设备周期上报状态;网络抖断时,最后一条状态要在重连后补发,不能丢。
完整框架(可直接下载运行)
lua
-- ① MQTT 对象必须全局持有,否则回调会被 GC 静默失效
mqc = mqtt.creat("broker.emqx.io", 1883, "lpplc_001", "", "")
-- ② 订阅必须放在 CONNECTED 回调里(脚本跑到这里时多半还没连上)
mqtt.reg(mqc, mqtt.CONNECTED, function()
mqtt.subscribe(mqc, "cmd", 0)
if 待重发 then -- 重连后补发最后状态
mqtt.publish(mqc, "state", 待重发, 1)
待重发 = nil
end
end)
mqtt.reg(mqc, mqtt.DISCONNECTED, function()
print("网络断开")
end)
-- ③ 收到下发:按 topic/payload 分支,写 M 去控现场
mqtt.reg(mqc, mqtt.RECVDATA, function(topic, payload)
if topic == "cmd" then
if payload == "on" then plc.m(50, 1) -- M50 = 开
elseif payload == "off" then plc.m(50, 0) -- M50 = 关
end
end
end)
mqc:start()
-- ④ 周期上报状态:先判在线再发;变化或重连才需要补发
timer.start(2000, function()
if mqtt.is_linked(mqc) == 1 then -- 返回整数,必须 == 1
local s = plc.m(50) == 1 and "on" or "off"
mqtt.publish(mqc, "state", s, 1)
待重发 = s -- 记下,断线时用于重连补发
end
end)要点:
mqc必须全局;局部变量被 GC 后所有回调静默失效(不报错,就是不工作)。- 订阅放
CONNECTED回调里。 mqtt.is_linked()返回整数,必须写== 1;写if mqtt.is_linked(mqc) then永远为真。- 断线重发用
待重发这个全局变量:只在CONNECTED里补发一次,避免重连期间刷屏。 - 上报用 QoS 1(
publish最后一参1)保证云端至少收到一次。
验证方法:
- 云侧下发
on→ 看 M50 置 1、现场继电器动作;下发off→ M50 清 0。 - 手动改 M50 → 2 秒内云侧收到
state。 - 拔网线 3 秒再插回 → 重连后应立即收到一条
state(验证断线重发)。
注意事项:
- 把
mqc写成local mqc = ...→ 过会儿所有回调失效且无报错。 - 订阅写在外层(未连上就执行)→ 订阅丢失,表现"能发不能收"。
is_linked()不写== 1→ 断线时还在发,消息全丢。
什么时候用本框架
| 需求 | 本框架覆盖 |
|---|---|
| 远程开关 / 启停 / 设定值 | ✅ 下发分支 |
| 周期上报状态 / 工程量 | ✅ timer 上报 |
| 网络抖动不丢最后状态 | ✅ 断线重发 |
| 要对接你们自己的服务器协议 | 把 mqtt 换成 tcp_client 或 http,下发/上报结构照套 |