Skip to content

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)

要点:

  1. mqc 必须全局;局部变量被 GC 后所有回调静默失效(不报错,就是不工作)。
  2. 订阅放 CONNECTED 回调里。
  3. mqtt.is_linked() 返回整数,必须写 == 1;写 if mqtt.is_linked(mqc) then 永远为真。
  4. 断线重发用 待重发 这个全局变量:只在 CONNECTED 里补发一次,避免重连期间刷屏。
  5. 上报用 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,下发/上报结构照套

LPPLC 工业物联网云平台 · 软硬件与云平台全栈自研