使用 Lua/NodeMCU,在运行代码块之前如何等待超过1个 MQTT 发布调用?

我的应用程序涉及使用电池供电的 ESP8266 运行 NodeMCU,目的是通过 MQTT 定期更新传感器值。

为了节省电池寿命,我希望在完成工作后立即调用 dsleep()。这项工作可能涉及多次调用 mqqt.Client.publish()。这就是我面临的问题。

我是 Lua 的新手,但是在我理解的方式中,运行某些代码的正确方法是在 publish() 完成后给它一个 PUBACK 回调:

m = mqtt.Client(...)
m.publish("/my/topic", "some message", 1, 0, callback_func)

在上面的简单案例中,即使实际发送 MQTT 消息与 publish() 调用异步(请参见这里的很好的讨论[https://stackoverflow.com/questions/33414441/nodemcu-and-esp8266-slow-mqtt-publish]),`callback_func()` 在上面的例子中仅在 publish() 完成后才会被调用。

但是,当我有多个调用 publish() 并且希望我的回调函数 a)在它们全部完成后被调用,并且 b)仅被调用一次,我就卡住了。

这个问题的天真方法是仅在第N个 publish() 调用上放置回调(这是可选的):

m = mqtt.Client(...)
m.publish("/my/topic", "some message", 1, 0)
m.publish("/another/topic", "unrelated message", 1, 0, callback_func)

但这将不能达到预期的结果。正如文档所述:

注意:在调用 publish() 多次时,最后一个定义的回调函数将为所有 publish 命令调用。

因此,在上面的示例中,callback_func() 最终会被调用两次(分别用于每个成功的 publish())。

我可以将多个 publish() 调用合并为单个调用,但这感觉像一个丑陋的 hack,并且会有其他不良影响。如果我的两条消息在概念上是不同的,这种方法将把逻辑推向将它们分离成订阅者 - yuck。如果它们需要进入不同的主题,那将更糟糕。必须有更好的方法。

我曾经以为 mqqt.Client.close() 会等待我的不同的 publish() 调用完成,但它没有。

我没有什么想法,希望有更多的 Lua 和/或 NodeMCU+mqqt 经验的人可以为我提供正确方向的指导。

这是我的实际代码,如果有帮助:

-- prior to this, we've gotten on the wifi network and acquired an IP
dofile("temp.lua") -- provides get_temp()

m = mqtt.Client("clientid", 120, "8266test", "password")

function mainloop(client)
    print("connected - at top of loop")
    m:publish("uptime",tmr.time(),1,0, function(client) print("sent uptime") end)
    temp, humi = get_temp()
    if (temp ~= nil) then
        print(string.format("temp: %d", temp))
        print(string.format("humi: %d", humi))
        m:publish("temp",temp,1,0)
        m:publish("humi",humi,1,0, function(client) -- note: this callback will be used for all publish() calls
            rtctime.dsleep(SLEEP_USEC)
            end)
    end
end

m:on("connect", mainloop)
m:on("offline", function(client) is_connected = false print ("offline") end)
m:connect(MQQT_SVR, 1883, 0, mainloop,
    function(client, reason) print("failed reason: "..reason) end)
点赞
用户2858170
用户2858170

选项 1: 一次性发布所有数据,然后进入睡眠状态。

选项 2: 将回调分成两部分。第一部分检查是否完成,第二部分在完成后进入睡眠状态。 当然,您也可以采用其他方法解决这个问题,例如计算还剩多少,计算已经发送了多少,发送并从列表中删除项目,直到列表为空等等...

当然还有更多选项,但这些选项已经足够简单。

编辑:根据要求提供示例

local totalItemCount = 5

function publishCallback()
  itemsPublished = (itemsPublished or 0) + 1
  print("item published")
  if itemsPublished == totalItemCount then
    print("I'm done, good night!")
  end
end

for i = 1, totalItemCount do
  publishCallback()
end

item published

item published

item published

item published

item published

I'm done, good night!

2017-01-07 17:30:33