Publishing MQTT messages from a NodeMCU – Soil Moisture Sensor








In this article I’d like to share the experience of building a soil moisture sensor built on NodeMCU which publishes MQTT messages (payload) to a cloud service.
This article is also a soft launch of a preview of cloud.iotalot.com service. We use it for our DIY projects to register json payload and visualise it is a very simple way. This allows us to “debug” our sensors. If you wish to get access to cloud.iotalot.com let us know by sending an email to admin@iotalot.com. We will provide you with the user account and instruct on how to start your project.
DISCLAIMER: We are not responsible for any data privacy or availability of the service at the moment.
Now back to the “experience” …
The set up
The sensor is mounted on the flower pot
Every 10 minutes is sends MQTT payload to cloud.iotalot.com
I can check the sensor data records using the telegram bot
This is how the components look like
The code
I assume you have a nodeMCU with the firmware burnt like explained in the post earlier. The firmware version that is used in this example is the following: nodemcu_float_0.9.6-dev_20150704.bin
This is the code for init.lua and moisturesensor.lua .
As a best practice, I don’t put the main code to int.lua file so that I can play with the NodeMCU module when the main code has some loops or timers. Before going to “production” I just put dofile("moisture sensor.lua")
to init.lua to make sure the main code runs when the device is powered.
init.lua
1 | dofile("moisturesensor.lua") |
moisturesensor.lua
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | local WIFI_SSID = "{WIFI_SSID}" local WIFI_PASS = "{WIFI_PASS}" local MAINTIMER_ID = 0 -- every 10 minutes = 600000ms local MAINTIMER_INTERVAL_MS = {INTERVAL} local mqtt_client = {} local MQTT_CLIENTID = "{MQTT CLINT_ID STRING}" local MQTT_USER = "{cloud.iotalot.com USERNAME}" local MQTT_PASS = "{cloud.iotalot.com PASSWORD}" local MQTT_TOPIC = "iotalot/sensors/{SENSOR_APIKEY}" local MQTT_ADDR = "cloud.iotalot.com" local MQTT_PORT = {MQTT_PORT} print("Moisture Sensor Initialized") function send_moisture_mqtt() moisture = adc.read(0) r = mqtt_client:publish(MQTT_TOPIC, string.format("{\"moisture\": %d}", moisture), 1, 1) end function register_main_timer() -- sending once the moisture wnen devices initialized send_moisture_mqtt() -- registering a timer tmr.register(MAINTIMER_ID, MAINTIMER_INTERVAL_MS, tmr.ALARM_AUTO, send_moisture_mqtt) tmr.start(MAINTIMER_ID) end function init_mqtt_client() -- init mqtt client with keepalive timer 120sec mqtt_client = mqtt.Client(MQTT_CLIENTID, 120, MQTT_USER, MQTT_PASS) mqtt_client:connect(MQTT_ADDR, MQTT_PORT, register_main_timer, function(client, reason) print("failed reason: "..reason) end) end function wait_for_wifi_conn ( ) tmr.alarm (1, 1000, 1, function ( ) if wifi.sta.getip ( ) == nil then print ("Waiting for Wifi connection") else tmr.stop (1) print ("The mode is: " .. wifi.getmode ( )) print ("The module MAC address is: " .. wifi.ap.getmac ( )) print ("Config done, IP is " .. wifi.sta.getip ( )) -- creating the mqqt client init_mqtt_client() end end) end -- Configure the ESP as a station (client) wifi.setmode (wifi.STATION) wifi.sta.config (WIFI_SSID, WIFI_PASS) wifi.sta.autoconnect (1) -- Hang out until we get a wifi connection before the httpd server is started. wait_for_wifi_conn ( ) |
To configure the code for your project you need to specify the following:
- {WIFI_SSID}, {WIFI_PASS} – the name of your WiFi access point and a password to it;
- {INTERVAL} – the interval in milliseconds for the timer. The code will send MQTT messages every {INTERVAL} milliseconds;
- {MQTT_CLIENTID} – any unique string to identify the MQTT client;
- {cloud.iotalot.com USERNAME}, {cloud.iotalot.com PASSWORD} – the MQTT server username and a password;
- {SENSOR_APIKEY} – the sensor apikey on cloud.iotalot.com. Feel free to modify the topic to any other when not using cloud.iotalot.com;
- {MQTT_PORT} – a port of MQTT service.
The code above explained …
Configuring the NodeMCU as a WiFi station trying to connect to WIFI_SSID access point using WIFI_PASS password.
1 2 3 4 5 6 7 | -- Configure the ESP as a station (client) wifi.setmode (wifi.STATION) wifi.sta.config (WIFI_SSID, WIFI_PASS) wifi.sta.autoconnect (1) -- Hang out until we get a wifi connection before the httpd server is started. wait_for_wifi_conn ( ) |
Every 1 second we check that the IP is provided by access point DHCP. When given we create the MQTT client and connect to MQTT server.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | function wait_for_wifi_conn ( ) tmr.alarm (1, 1000, 1, function ( ) if wifi.sta.getip ( ) == nil then print ("Waiting for Wifi connection") else tmr.stop (1) print ("The mode is: " .. wifi.getmode ( )) print ("The module MAC address is: " .. wifi.ap.getmac ( )) print ("Config done, IP is " .. wifi.sta.getip ( )) -- creating the mqqt client init_mqtt_client() end end) end |
In the following code the call back function register_main_timer() is provided for the case when connected successfully to MQTT server:
1 2 3 4 5 | function init_mqtt_client() -- init mqtt client with keepalive timer 120sec mqtt_client = mqtt.Client(MQTT_CLIENTID, 120, MQTT_USER, MQTT_PASS) mqtt_client:connect(MQTT_ADDR, MQTT_PORT, register_main_timer, function(client, reason) print("failed reason: "..reason) end) end |
In the following part tmr.ALARM_AUTO means that timer doesn’t stop when once triggered. send_moisture_mqtt is called when timer is triggered.
1 2 3 4 5 6 7 | function register_main_timer() -- sending once the moisture wnen devices initialized send_moisture_mqtt() -- registering a timer tmr.register(MAINTIMER_ID, MAINTIMER_INTERVAL_MS, tmr.ALARM_AUTO, send_moisture_mqtt) tmr.start(MAINTIMER_ID) end |
And at last, we read the analogue input on NodeMCU to which a moisture sensor is attached. The MQTT json message is published to a defined topic with QoS = 1.
1 2 3 4 | function send_moisture_mqtt() moisture = adc.read(0) r = mqtt_client:publish(MQTT_TOPIC, string.format("{\"moisture\": %d}", moisture), 1, 1) end |
Referenced articles and documentation:
NEWBIE BUILDS A SOIL MOISTURE SENSOR ON NODEMCU (ESP8266)
Official NodeMCU documentation on MQTT Module
Official NodeMCU documentation on Wifi Module
Official NodeMCU documentation on Timer Module
Lua – Strings Tutorial







