主题
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。
验证方法:
- 运行后每 5 秒向
data/lpplc_001推一条含temp/y0的 JSON。 - 向
cmd/lpplc_001发{"y0":1}:Y0 吸合,且下一轮上报里y0变成 1(证明两个方向都通)。 - 停掉 Broker 再启动:不用重启设备,上报自行恢复(
start之后的重连由内部线程负责)。
注意事项:
- 不要在脚本末尾补
while true do ... system.sleep(...) ... end:它会掐掉消息泵,表现为「MQTT 显示已连接,但收不到任何下发、也不上报」。 - 别用
mqtt.connect()/mqtt.on_message()这类凭其它平台习惯起的名字 —— 本模块只有mqtt.creat/mqtt.reg/mqtt.start。