使用 Lua/NodeMCU,我如何才能等到在 运行 一个代码块之前进行了 >1 个 mqtt 发布调用?

With Lua/NodeMCU, how can I wait until >1 mqtt publish calls have been made before running a block of code?

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

为了节省电池寿命,我想在完成工作后立即致电 dsleep()。这项工作可能涉及对 mqqt.Client.publish() 的 1 次以上调用。这就引出了我面临的问题。

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

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

在像上面这样的简单情况下,效果很好——即使 MQTT 消息的实际发送相对于 publish() 调用是异步的(参见对此 的很好的讨论), 上例中的 callback_func() 只会在 publish() 完成时调用。

但是当我对 publish() 进行了 1 次以上的调用并希望我的回调 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)

但这不会达到预期的效果。作为 documented:

NOTE: When calling publish() more than once, the last callback function defined will be called for ALL publish commands.

所以在上面的例子中,callback_func() 最终被调用了两次(每次成功调用一次 publish()

我可以将多个 publish() 调用组合成一个调用,但这感觉就像一个丑陋的 hack,并且会产生其他不利影响。如果我的两条消息在概念上是不同的,这种方法将推动逻辑将它们分离到订阅者中 - 糟糕。如果他们需要讨论不同的主题,情况会更糟。一定有更好的办法。

我想也许 mqqt.Client.close() 会等待我不同的 publish() 调用完成,但事实并非如此。

我没有想法,希望有更多 Lua and/or 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)

选项 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!