【问题标题】:With Lua/NodeMCU, how can I wait until >1 mqtt publish calls have been made before running a block of code?使用 Lua/NodeMCU,我如何才能等到 >1 mqtt 发布调用之后再运行代码块?
【发布时间】:2017-05-22 06:57:27
【问题描述】:

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

为了节省电池寿命,我想在完成工作后立即致电dsleep()。这项工作可能涉及对mqqt.Client.publish() 的多次调用。这给我们带来了我面临的问题。

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

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

在像上面这样的简单情况下,效果很好 - 即使 MQTT 消息的实际发送与 publish() 调用是异步的(请参阅对此 here 的良好讨论),callback_func() in上面的例子只有在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:

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

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

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

我以为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)

【问题讨论】:

    标签: lua mqtt nodemcu


    【解决方案1】:

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

    项目已发布

    项目已发布

    项目已发布

    项目已发布

    项目已发布

    我说完了,晚安!

    【讨论】:

    • 谢谢!不过,我认为这些选项都不能真正满足我的需求。选项 1 迫使我围绕客户端的限制扭曲系统行为。具体来说,如果我的消息本质上不同(它们是不同的),因此需要单独处理(甚至可能由不同的订阅者),我将在订阅者中(有效地)重新实现主题逻辑。 (例如,假设我正在发布临时数据和房间状态数据,我希望完全不同的订阅方逻辑来处理这两个事情。)
    • 选项 2 听起来不错,但鉴于回调在 publish() 上的行为方式,我不知道如何实现它。从字面上看,每次调用都会运行完全相同的逻辑,那么我如何检查第一次调用是否完成?你能举个例子来说明它是如何工作的吗?
    • 您使用 if 语句运行相同的逻辑来检查您是否完成了。你已经用过 if 所以我假设你熟悉条件语句......你不必知道第一次调用是否完成。您只需要知道所有通话都已完成,您就可以睡觉了。
    • 啊,是的!谢谢-出于某种原因,我并没有像计算对回调的调用那样简单。效果很好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-16
    • 2023-03-25
    相关资源
    最近更新 更多