Skip to content

MQTT 客户端例程 3:采集上报 + 云端下发的完整骨架(推荐直接照抄) ​

这一页只讲这一个例程。同组其余例程、以及这个库的函数说明都在 05-02-mqtt。

适用:LP3201 / LP3202 / LP3203 / LP3222 / LP3232 / LP3288 / 软PLC模拟器 | 依赖模块:mqtt json timer plc | 难度:★★☆

场景:最常被问也最常被写错的一种 —— 既要定时上报,又要接受云端下发控制。下面这段把两个方向的骨架都给全了:上报由定时器驱动、下发由回调驱动,脚本注册完即结束。要加采集逻辑就补其它模块(如 com_modbus),不要改这段骨架的结构。

完整代码(可直接下载运行):

lua
mqttc = mqtt.creat("broker.emqx.io", 1883, "lpplc_001", "", "")
mqtt.set_keepalive(mqttc, 60)

mqtt.reg(mqttc, mqtt.CONNECTED, function()
    mqtt.subscribe(mqttc, "cmd/lpplc_001", 0)
end)

mqtt.reg(mqttc, mqtt.RECVDATA, function(topic, payload)
    local ok, cmd = pcall(json.decode, payload)
    if ok and cmd.y0 ~= nil then
        plc.y(0, cmd.y0)
    end
end)

mqtt.start(mqttc)

timer.start(5000, function()
    if mqtt.is_linked(mqttc) == 1 then
        mqtt.publish(mqttc, "data/lpplc_001",
            json.encode({ temp = plc.df(100), y0 = plc.y(0) }), 0, 0)
    end
end)

不要在脚本末尾再补 while 主循环

上面这个骨架已经是完整脚本:注册完回调和定时器后脚本就结束,固件自动接管消息泵(C 侧,不需要脚本自己转圈)。

不要"为了让脚本跑起来"再补一段主循环,更不能用 sleep / system.sleep 等待 —— 那会把消息泵堵死,mqtt.reg 注册的 RECVDATA / CONNECTED 回调和 timer.start 全都永远不触发,典型表现是「MQTT 显示已连接,但收不到任何下发、也不上报」。

周期上报就用 timer.start;要"一直跑、中间还要等"就用 task.start 起任务。脚本侧的 system.recv 已于 2026-10-22 删除,老工程里的 while true do system.recv(10) end 直接删掉即可。

要点:

  • mqtt.is_linked(mqttc) == 1 是唯一正确的连接判断写法(返回值是整数,不是 boolean)。
  • 上报与下发彼此独立:定时器只管上报、回调只管执行,不需要同步也不会互相干扰。
  • 采集到的数据先落到 D 寄存器,再在这里 json.encode 打包 —— MQTT 这一段不要自己去拼外设报文。
  • 更贴近现场的端到端版本(Modbus 采集 + ACK + 错误写法对照)见 综合例程 B1。

验证方法:

  1. 运行后每 5 秒向 data/lpplc_001 推一条含 temp / y0 的 JSON。
  2. 向 cmd/lpplc_001 发 {"y0":1}:Y0 吸合,且下一轮上报里 y0 变成 1(证明两个方向都通)。
  3. 停掉 Broker 再启动:不用重启设备,上报自行恢复(start 之后的重连由内部线程负责)。

注意事项:

  • 不要在脚本末尾补 while true do ... system.sleep(...) ... end:它会掐掉消息泵,表现为「MQTT 显示已连接,但收不到任何下发、也不上报」。
  • 别用 mqtt.connect() / mqtt.on_message() 这类凭其它平台习惯起的名字 —— 本模块只有 mqtt.creat / mqtt.reg / mqtt.start。

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