NodeMCU publishing multiple messages over MQTT

Recently I started playing around with openHAB. And after having had enough fun with switches and led’s turning on and off, I wanted to experiment with temperature and humidity logging.

Getting it all set up in openHAB was quite easy, but messages from the ESP8266 running NodeMCU was very unstable. It should update once every minute, but often it could be 5 to 10 minutes before a new message arrived.

After a bit digging around, I nailed it down to the example I had used from another site, and decide to make my own, based on the switch code I already had and knew was working reliably.

In the original example I used, one function was calling another function, nested inside the previous, so it would post when the previous had succeeded. To me it was difficult to get a good overview of, and it was difficult for me to add new values to post too.

My solution was to put the destination of the message into a table, and the value into another table, and then read from that, based on a timer.

mqttPublish

This function is the one preparing the messages, which will later be send via MQTT to the server.

In this example, it puts temperature and humidity into the end of the two tables, along with their values.

By simply copying the two lines which insert the messages into the table, you can easily make it post more messages without having to worry about it triggering correctly after the previous message is delivered. Only important thing is that you put something into both tables. The two tables mqtt_dest and mqtt_valu MUST ALWAYS be edited the same, to keep the destination and value at the same index in both tables.

mqttHandle

Here is where the magic happens. Instead of having the same function publish the messages, I ended up with this function. It is triggered by a timer running at 1000ms, and every time it is triggered, it will check the content of the first value in the mqtt_dest table. If the value isn’t nil, it will go ahead and publish it to the server.

After successfully publishing it, instead of triggering the next publish, it will remove the values it just posted, from the two tables.

Optimization

In my case, I just needed it to post the temperature and humidity once every minute. But if you need it to do it faster, and with more messages, you might need to tweak the timer triggering mqttHandle. With two messages, it will take two seconds to send them, because it sends the first in the tables, removes them from the tables, waits for the timer to run out, and repeats the cycle again.

I haven’t tested how fast it can be done, I might at another time, but for now I just need something working reliably, and is easy to configure and modify.

Some testing I did do a bit of, was to see how many messages it could hold in the tables before running out of memory. However, it was holding so many messages I aborted the test again. In the test I disabled the timer for publishing and cleaning out the tables, and just let it fill messages into the tables. After each table was holding 100 entrys, it was still running without problems, and I doubt I will ever need to publish that many things. 🙂

LUA

And here is the code I ended up with.

Notice, to work as it is, I needs the DHT22 module, with the DHT22 sensor connected to pin 3.

wifi.setmode(wifi.STATION)
wifi.sta.config ("SSID" , "KEY")

broker="192.168.1.30"
seconds = 60
deviceID = "esp03"
roomID = "2"

humi="XX"
temp="XX"

mqtt_dest = {}
mqtt_valu = {}

PIN = 3 -- data pin, GPIO0
--PIN = 4 -- data pin, GPIO2

--load DHT22 module and read sensor
function ReadDHT22()
dht22 = require("dht22")
dht22.read(PIN)
t = dht22.getTemperature()
h = dht22.getHumidity()
humi=(h/10).."."..(h%10)
temp=(t/10).."."..(t%10)
dht22 = nil
package.loaded["dht22"]=nil
end

function mqttHandle(n)
if mqtt_dest[1] ~= nil then
print("MQTT publish " .. mqtt_dest[1] .. " payload " .. mqtt_valu[1])
m:publish("/home/".. roomID .."/" .. deviceID .. "/" .. mqtt_dest[1], mqtt_valu[1], 0, 0, function()
table.remove(mqtt_dest, 1)
table.remove(mqtt_valu, 1)
end)
end
end

tmr.alarm(2, 1000, 1, function() mqttHandle(0); end)

m = mqtt.Client("ESP8266".. deviceID, 180, "user", "pass")
m:lwt("/lwt", "ESP8266", 0, 0)
m:on("offline", function(con)
ip = wifi.sta.getip()
print ("Mqtt Reconnecting to " .. broker .. " from " .. ip)
tmr.alarm(1, 10000, 0, function()
m:connect(broker, 1883, 0, function(conn)
print("Mqtt Connected to:" .. broker)
end)
end)
end)

function mqttPublish(level)
ReadDHT22()
print("MQTT update")

table.insert(mqtt_dest, "temperature")
table.insert(mqtt_valu, temp)

table.insert(mqtt_dest, "humidity")
table.insert(mqtt_valu, humi)

--local volt = node.readvdd33();
--table.insert(mqtt_dest, "voltage")
--table.insert(mqtt_valu, volt/1000) .. "." .. (volt%1000))

mqttHandle(0)
end

tmr.alarm(0, 1000, 1, function()
if wifi.sta.status() == 5 and wifi.sta.getip() ~= nil then
tmr.stop(0)
m:connect(broker, 1883, 0, function(conn)
print("Mqtt Connected to:" .. broker)
tmr.alarm(0, seconds*1000, 1, function() mqttPublish(0); end)
end)
end
end)

2 comments

Leave a Reply