ESP8266 Lua Tutorial, Part 3

We use NodeMCU's implementation of MQTT to create our first IoT device!

Public Chat
Similar projects worth following
MQTT is a lightweight IoT protocol designed to be easy to use and implement. Here we use the NodeMCU MQTT library to log data to the cloud!

Take a look at Part 1 of this tutorial series:

ESP8266 SDK TutorialLearn how to use the ESP8266 SDK. This is what the pros use!
ESP8266 Lua/NodeMCU TutorialA look at the NodeMCU Lua interpreter for the ESP8266.  Learn how to get to Blinky!
ESP8266 Arduino TutorialUse the Arduino IDE to simplify development and get up to speed very quickly!

And here's the links to Part 2:

ESP8266 SDK Tutorial Looking at using the linker to get PWM, and the included I2C libraries
ESP8266 Lua/NodeMCU TutorialUsing PWM and I2C with Lua!
ESP8266 Arduino Tutorial Using the Wire library for I2C, and AnalogWrite for fading!

Links to the other tutorials in Part 3:

ESP8266 SDK TutorialUsing MQTT to develop an IoT device
ESP8266 Lua/NodeMCU Tutorial (You are here)Using the NodeMCU MQTT module to communicate with a cloud data service
ESP8266 Arduino TutorialWe use the simpler, more widely available HTTP protocol to log data to the cloud

Getting Help

If you run into trouble while following these tutorials, you have a few different options:

  • Ask in the discussion area below the article
  • Join the ##esp8266 channel on Freenode IRC and ping me (MrAureliusR) or ask anyone who is in there
  • Post on the ESP8266 Community Forums (note it can take a while to get a response!)
  • Send me a private message here on Hackaday

What is MQTT?

MQTT, pronounced "em-cute" and short for Message Queuing Telemetry Protocol, is a lightweight communication protocol that was specifically designed to be easy to implement in environments with limited resources, like a microcontroller. It has found widespread use in the IoT arena. It is easy to understand, and it is perfectly suited for applications where data logging or remote control are required. In this tutorial, we will be implementing a basic MQTT library for the ESP8266. In order to do this, we will need to take a look at the MQTT standard document, which is freely available. A huge kudos to OASIS and for making the specification freely available. This is one of the factors that has led to its widespread use.

MQTT is based on a subscribe/publish model. Think of it like a newspaper or magazine subscription. There are many different newspapers and magazines available, each containing different information that you may or may not be interested in. Your local magazine supplier, or broker, has a listing of all the publications that are available. You can choose to subscribe to those you are interested in, and from that point forward, you will receive all the subsequent releases as long as you maintain your subscription. 

MQTT works in the same way, with a server running the MQTT broker software. This broker can be queried by clients looking to subscribe to the data feeds (known as topics in MQTT lingo) it has available. One of these topics might be the temperature in Toronto, Ontario, at the Pearson International Airport. You send a message to the server that you would like to subscribe to that topic. When a new reading is taken from the thermometer, the server sends you a message with the new data. This continues until you cancel your subscription, stop responding, or the broker goes offline.

A diagram of the publish process. Notice the separation between Publisher and Subscriber

On the other side of the broker is the publisher. This is the computer at Pearson Airport which is actually taking the readings from the thermometer. Whenever it has a new piece of data, it publishes this data to the MQTT broker software, running on the publicly accessible server. The publisher doesn't know who is subscribed, and it doesn't care. Its only job is publishing new data to the feeds it is responsible for. This separation between the publisher and subscriber allows for easy scaling and security. It is also highly reliable, with features in the protocol that can ensure delivery of messages...

Read more »


This is the full code file used in this project

x-lua - 1.99 kB - 02/28/2019 at 04:37



NodeMCU Firmware for this tutorial

x-sega-cd-rom - 716.00 kB - 01/12/2019 at 21:42


  • 1
    MQTT Library

    NodeMCU comes fully equipped with an MQTT library, assuming you followed the build instructions in Part 1 of this series. However, this example also uses the DHT and DS18B20 libraries, so go back to the NodeMCU custom firmware build website and build a new firmware with these options:

    Or, you can download a pre-built version from the Files section. Flash it to your connected Feather or other module using:

    #!/bin/bash --port /dev/feather0 erase_flash --port /dev/feather0 write_flash -fm=dio -fs=32m 0x00000 nodemcu-master-19-modules-2019-01-12-20-51-40-float.bin

    Remember to change the port name if you don't have udev rules for your boards (they will show us as /dev/ttyUSBx normally). Also, change the name of the nodemcu binary if you built your own version. The above snippet can be saved to a file named and run when needed. This can make it very quick to flash fresh firmware if something goes wrong.

    For this example, we will be reading from a DS18B20 temperature sensor and a DHT22 Humidity Sensor. The code can easily be modified to use other sensors if you don't have these available. If you need help, check the Getting Help section in Details!

    The DS18B20 uses 1-Wire, while the DHT22 uses a proprietary single wire serial protocol. Luckily, both of these sensors already have modules as part of the NodeMCU firmware. Just make sure you choose the DS18B20, DHT, and 1-Wire modules when building your firmware.

    Our code will first connect to Wi-Fi, and establish an MQTT connection to Adafruit IO. We will create functions to get the humidity value from the DHT22 and to get the temperature from the DS18B20. These will in turn call functions which use the MQTT connection to log the most recent values.

    Here's a quick look at the circuit layout:

    The full init.lua file can be downloaded here. We'll go through it in later steps.

  • 2
    Sign up for Adafruit IO

    We will need an Adafruit IO account for this to work. If you use a different datalogging service that supports MQTT, feel free to use that instead, but I've found Adafruit IO to be very reliable and easy to use. They support the MQTT standard fully which makes IO widely compatible with MQTT libraries for many different devices.

    Signing up is a breeze. Head over to and sign up. If you already have an Adafruit account, you can use the same credentials to log into IO. Once logged in, we need to create feeds to send data to. Navigate to Feeds in the left hand toolbar. Click on Actions, and create a feed. You can optionally put them into a group, but it's easier to just leave them in the default group. After creating two feeds, click on View AIO Key and copy this value.

    After creating the feed, we can view it by clicking on its name. Click Feed Info on the right hand toolbar, and copy the MQTT topic name.

    Copy the MQTT topic name

    You can paste these values into a text file for now, or you can download the init.lua file from the downloads section above and paste these values directly into the code.

  • 3
    Establishing Connection

    The official documentation is located in the NodeMCU ReadTheDocs. The simplicity of the library reflects how elegantly simple MQTT itself is. All we need to do is create an MQTT Client object, connect, and then publish as needed.

    Creating the MQTT Client object is as simple as calling mqtt.Client() with the correct parameters. It requires a client ID, which can be any alphanumeric string you want; a timeout value for keeping the TCP connection open; and the username and password. In our case the password will be our Adafruit IO key. The call will look something like this:

    mClient = mqtt.Client("esp8266", 120, IO_USER, IO_KEY)

    Once the object is created, we use the functions built into that object to do everything else. The next thing to do is establish the connection to Adafruit IO. We use the :connect method of our object to do so:

    mClient:connect(IO_HOST, IO_PORT, IO_SSL, 0, 
        function(client, reason) 
            print("Connection failed for " .. reason) 

    One of the neat features of the Lua language is the ability to define anonymous (nameless) functions in the arguments to a function. In the :connect method, the callbacks for successful connection and failed connection are defined right in the call. This makes the code more compact and efficient, one of the hallmarks of Lua in general. The arguments to :connect are host (IP address), port (typically 1883), whether or not we are using SSL, a deprecated option which must be 0, and then callbacks for successful and failed connections.

    Now that we are connected, we can call any of the methods in the MQTT object. For the purpose of this project, we're just going to use our tried-and-tested timers to control everything. We create four functions: two to get the data from the humidity and temperature sensors respectively, and two to publish those pieces of data to the MQTT broker. We use timers to call the functions to get the data (named getHumid() and getTemp()) which in turn call the publish functions (logHumid() and logTemp()).

    function logTemp(ftemp)
       mClient:publish(IO_TEMP_TOPIC, ftemp, 0, 0, function(client) print("Temperature sent!\n") end)
    function logHumid(fhumid)
       mClient:publish(IO_HUMIDITY_TOPIC, fhumid, 0, 0, function(client) print("Humidity sent!\n") end)
    function getHumid()
       local fHumid = 0
       gpio.write(led2Pin, gpio.LOW)
       _, _, fHumid =
       gpio.write(led2Pin, gpio.HIGH)
    function getTemp()
       gpio.write(ledPin, gpio.LOW)
       gpio.write(ledPin, gpio.HIGH)

    Most of the code is very self-explanatory. One little trick in Lua is that a function can return multiple values. For example, returns up to five variables. We only need the third variable, so we can use the underscore to tell Lua that we don't want the first two return variables. Also, as mentioned previously, we can give a very simple function to instead of creating another named function. We only need the temp variable, so we send this off to logTemp(). Notice that the second argument to this function is an empty Lua table {}. By passing an empty table for the ROM address, the library will instead use the skip ROM command and simply talk to the first device on the bus. This will only work if there is only one device on the bus! In our case this is fine.

    The DHT22 Humidity Sensor

    In the log functions, we simply call mClient:publish(). :publish is another method of the MQTT object. Its arguments are the name of the topic to publish to, the data to publish, the QoS level to use (we will always be using 0 in our projects), whether this is a retained message (a special type of MQTT message that we won't worry about now), and a function to call once we receive confirmation from the server that it has received our data. In this example, I create an anonymous function which simply prints out a confirmation that the data was sent.

View all 4 instructions

Enjoy this project?



nodemcu12ecanada wrote 08/22/2020 at 15:57 point

I use many very inexpensive NodeMCU devices to save $1,000+/year at home. I only use Arduino IDE to program them not Arduino devices any more. Monitor and control from a web page instead of an app. Send data to a cloud spreadsheet available from anywhere. Monitor and control from anywhere using MQTT. Upload programs wirelessly to remote devices.

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates