0

I'm building a battery powered IoT device based on ESP8266 with NodeMCU. I use mqtt to periodically perform measurements and publish results. I know, that to allow network stack running, I should avoid tight loops and rely on callback functions. Therefore it seemed to me that the right organization of my measurement code should be:

interval=60000000

function sleep_till_next_sample()
 node.dsleep(interval)
end

function close_after_sending()
  m:close()
  sleep_till_next_sample()
end

function publish_meas()
   m:publish("/test",result,1,0,close_after_sending)
   print("published:"..result)
end

function measurement()
   -- The omitted part of the function accesses
   -- the hardware and places results
   -- in the "result" variable
   m = mqtt.Client("clientid", 120, "user", "password")
   m:connect("172.19.1.254",1883,0, publish_meas)
end

The init.lua ensures, that the node has connected to the WiFi AP (if not, it retries up to 20 times, and if no connection is established, it puts the node on sleep until the next measurement time). After WiFi connection is done, it calls the measurement function.

The interesting thing is, that the above code doesn't work. There are no errors displayed in the console, but the mqtt broker does not receive published messages. To make it working, i had to add additional idle time, by adding timers in the callback functions.

The finally working code looks like below:

interval=60000000

function sleep_till_next_sample()
 node.dsleep(interval)
end

function close_after_sending()
  m:close()
  tmr.alarm(1,500,0,function() sleep_till_next_sample() end)
end

function publish_meas()
   m:publish("/test",result,1,0,function() tmr.alarm(1,500,0,close_after_sending) end)
   print("published:"..result)
end

function measurement()
   -- The omitted part of the function accesses
   -- the hardware and places results
   -- in the "result" variable
   m = mqtt.Client("clientid", 120, "user", "password")
   m:connect("172.19.1.254",1883,0, function() tmr.alarm(1,500,0, publish_meas) end)
end

The above works, but I'm not sure if it is optimal. To conserve the battery power I'd like to minimize the time before the node is put on sleep after the measurement is completed and results published.

Is there any better way to chain the necessary calls to m:connect, m:publish, m:close and finally node.dsleep so, that the results are correctly published in the minimal time?

greatwolf
  • 20,287
  • 13
  • 71
  • 105
wzab
  • 788
  • 7
  • 24
  • I was looking through the [open NodeMCU MQTT issues](https://github.com/nodemcu/nodemcu-firmware/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+mqtt) but none seems to relate to this here. I'm tempted to consider this to be a firmware bug. [The API documentation](https://github.com/nodemcu/nodemcu-firmware/wiki/nodemcu_api_en#mqttclientpublish) claims that the publish callback be "fired when PUBACK received." Since you publish with QoS 1 you're supposed to get a proper PUBACK (http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718043). – Marcel Stör Dec 20 '15 at 19:52
  • I'm not sure about optimal but the second set of code looks better to me. I will always rely on timers rather than the node.sleep. That's one way to do it. If you ask about performance then you shouldn't be using LUA to begin with but that's a different story. – ProgrammerV5 Dec 22 '15 at 19:04
  • Did you try recently with a build from the `dev` branch? We've added a number of MQTT fixes over the last few months. – Marcel Stör May 15 '16 at 09:52

1 Answers1

1

Perhaps this was solved by more recent firmware. I am working through a problem that I thought might be somewhat explained by this issue, so tried to reproduce the problem as described.

My simplified test code is substantially similar; it calls dsleep() from the PUBACK callback of mqtt.Client.publish():

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

m:lwt("/lwt", "offline", 0, 0) 

function main(client) 
    print("connected - at top of main")

m:publish("someval",12345,1,0, function(client)  
    rtctime.dsleep(SLEEP_USEC)      
    end)
end

m:on("connect", main)
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)

and when run, does successfully publish to my MQTT broker.

I am using:

    NodeMCU custom build by frightanic.com
            branch: master
            commit: 81ec3665cb5fe68eb8596612485cc206b65659c9
            SSL: false
            modules: dht,file,gpio,http,mdns,mqtt,net,node,rtctime,sntp,tmr,uart,wifi
     build  built on: 2017-01-01 20:51
     powered by Lua 5.1.4 on SDK 1.5.4.1(39cb9a32)
jrheling
  • 153
  • 1
  • 8