ESP32 ESP8266 MicroPython MQTT Publish DHT22 Sensor Readings

In this ESP32 ESP8266 tutorial, we will learn to use ESP MQTT as a Publisher using MicroPython and subscribe through Node-Red. There will be one ESP32/ESP8266 MQTT publisher and Node-Red subscriber. We will publish DHT11 and DHT22 sensor readings to MQTT with ESP32/ESP8266 and Node-Red Dashboard will subscribe to the MQTT topics and display sensor readings on the Dashboard in an interactive manner.

ESP32 ESP8266 MicroPython MQTT Publish DHT22 Sensor Readings

We will use the Mosquitto broker that is installed on the Raspberry Pi. But if you do not have Raspberry Pi, you can also install it on your Windows or Linux Ubuntu machine.

The Publisher ESP board and Node-RED will make connections with the MQTT broker installed on Raspberry Pi. After that, the ESP32/ESP8266 will publish sensor data to the Node-Red dashboard on the specified topics. Any appropriate sensor can be used but for this article, we will use a DHT22 and DHT11 sensor which are used to measure temperature and humidity.

Prerequisites

Before continuing with this project, make sure you have the latest version of MicroPython and Thonny IDE installed on your PC. Your ESP32/ESP8266 board should be already flashed with the firmware. Additionally, we will be using the Mosquitto broker to send and receive messages. So make sure your broker is installed on the Raspberry Pi.

ESP32 ESP8266 MicroPython MQTT DHT11/DHT22 Project Overview

The diagram below illustrates the process that we will follow in our ESP32/ESP8266 MQTT project with DHT sensor.

ESP32 and ESP8266 MQTT Publish and Subscribe DHT Sensor Readings Project Overview
  1. An ESP32 or ESP8266 board connected with DHT22 sensor will connect to the MQTT broker. We will use Mosquitto broker on Raspberry Pi. Refer to the following article (Install Mosquitto MQTT Broker on Raspberry Pi) to successfully install it in Raspberry Pi before moving forward.
  2. This ESP board publishes the DHT22 temperature readings on the MQTT topic: esp/dht/temperature and publishes the DHT22 humidity readings on the MQTT topic: esp/dht/humidity.
  3. Node-Red will subscribe to these two topics. Node-Red receives the sensor data and displays them in an interactive manner in its dashboard.
ESP32 and ESP8266 MQTT Publish and Subscribe DHT Sensor Readings Project

MQTT Protocol Introduction

  • MQTT is known as Message Queuing Telemetry Transport protocol.
  • It is a lightweight messaging protocol and helps resource constrained network clients with a simple communication mechanism.
  • Unlike, most messaging system, we don’t have to assign addresses to MQTT clients.
  • MQTT uses simple publish/subscribe communication based on a topic.
  • This protocol runs on top of TCP / IP in order to provide reliable data delivery.
MQTT Protocol Introdution

For a detailed tutorial regarding MQTT, its main components, MQTT broker and working follow the link: 

MicroPython ESP32 ESP8266 as an MQTT Publisher

Our ESP32/ESP8266 MQTT Publisher is connected with a DHT22 sensor.

DHT22 is a sensor which measures relative humidity and temperature. It provides a calibrated digital output with a 1-wire protocol. DHT sensors are pre-calibrated. We can directly connect them with ESP32/ESP8266 to obtain sensor output reading. They are internally composed of a humidity sensing sensor and a thermistor. These two components measure humidity and temperature. 

DHT22 Pinout 

The following figure shows the pinout diagram of DHT sensors. DHT sensor consists of four pins. But on DHT modules only three pins are exposed to the pinout of the module and10k ohm pull-up resistor is internally connected to pin 2. 

dht11 dht22 pinout diagram

Pin Description

The following lists the pinout of the DHT sensor and their brief description. Pin number starts from left to right when you hold the sensor from the front end. 

DHT22 PinESP32/ESP8266
1 (Vcc)3.3V 
2 (Data Out)Any GPIO pins of the ESP board along with a 10k ohm pull-up resistor
3 (NC)Not used
4 (GND)Ground
  • Vcc is the power supply pin. Apply voltage in a range of 3.3 V to 5.0 V to this pin
  • Data Out is the digital output pin. It sends out the value of measured temperature and humidity in the form of serial data
  • N/C is not connected
  • GND: Connect the GND pin 

Circuit Diagram of ESP32/ESP8266 with DHT22 sensor

DHT22 with ESP32

We will need the following components.

Required Components

  1. ESP32 or ESP8266 board
  2. DHT22
  3. 10k ohm resistor (not required if using the DHT22 sensor module)
  4. Bread Board
  5. Jumper wires
  • The first pin the DHT22 sensor is a power supply(VCC) pin. Connect it with the 3.3 volt or Vin pin of ESP32/ESP8266.
  • Data out is the pin through which we get temperature and humidity samples from the DHT sensor. Connect this pin with GPIO14 of ESP32/ESP8266 and also connect the data pin with a 10k pull-up resistor. But you can also use any digital pin of ESP32/ESP8266. A pull-up resistor is used to keep the data pin high for proper communication between the microcontroller and sensor. You can check the datasheet of DHT22 to get more information about it. DHT22 is also known by the name of AM2302. If you are using a DHT22 sensor module then you do not need to add the resistor.
  • Third pin is not used.
  • Connect the fourth pin (GND) to the ground pin of the ESP32/ESP8266 board.

If you are using ESP32, connect it with DHT22 as shown in the schematic diagram below:

DHT11 DHT22 Interfacing with ESP32 MicroPython

If you are using ESP8266, connect it with DHT22 sensor as shown below:

ESP8266 with DHT sensor circuit diagram

You can read an in-depth guide on DHT11/DHT22 interfacing with ESP32:

Setting up MQTT with MicroPython

The first step is to install the umqttsimple library in MicroPython because we have to use the MQTT with our ESP32/ESP8266 board.

Open Thonny IDE and create a new file. Copy the code given from here save it to your MicroPython device.

try:
    import usocket as socket
except:
    import socket
import ustruct as struct
from ubinascii import hexlify

class MQTTException(Exception):
    pass

class MQTTClient:

    def __init__(self, client_id, server, port=0, user=None, password=None, keepalive=0,
                 ssl=False, ssl_params={}):
        if port == 0:
            port = 8883 if ssl else 1883
        self.client_id = client_id
        self.sock = None
        self.server = server
        self.port = port
        self.ssl = ssl
        self.ssl_params = ssl_params
        self.pid = 0
        self.cb = None
        self.user = user
        self.pswd = password
        self.keepalive = keepalive
        self.lw_topic = None
        self.lw_msg = None
        self.lw_qos = 0
        self.lw_retain = False

    def _send_str(self, s):
        self.sock.write(struct.pack("!H", len(s)))
        self.sock.write(s)

    def _recv_len(self):
        n = 0
        sh = 0
        while 1:
            b = self.sock.read(1)[0]
            n |= (b & 0x7f) << sh
            if not b & 0x80:
                return n
            sh += 7

    def set_callback(self, f):
        self.cb = f

    def set_last_will(self, topic, msg, retain=False, qos=0):
        assert 0 <= qos <= 2
        assert topic
        self.lw_topic = topic
        self.lw_msg = msg
        self.lw_qos = qos
        self.lw_retain = retain

    def connect(self, clean_session=True):
        self.sock = socket.socket()
        addr = socket.getaddrinfo(self.server, self.port)[0][-1]
        self.sock.connect(addr)
        if self.ssl:
            import ussl
            self.sock = ussl.wrap_socket(self.sock, **self.ssl_params)
        premsg = bytearray(b"\x10\0\0\0\0\0")
        msg = bytearray(b"\x04MQTT\x04\x02\0\0")

        sz = 10 + 2 + len(self.client_id)
        msg[6] = clean_session << 1
        if self.user is not None:
            sz += 2 + len(self.user) + 2 + len(self.pswd)
            msg[6] |= 0xC0
        if self.keepalive:
            assert self.keepalive < 65536
            msg[7] |= self.keepalive >> 8
            msg[8] |= self.keepalive & 0x00FF
        if self.lw_topic:
            sz += 2 + len(self.lw_topic) + 2 + len(self.lw_msg)
            msg[6] |= 0x4 | (self.lw_qos & 0x1) << 3 | (self.lw_qos & 0x2) << 3
            msg[6] |= self.lw_retain << 5

        i = 1
        while sz > 0x7f:
            premsg[i] = (sz & 0x7f) | 0x80
            sz >>= 7
            i += 1
        premsg[i] = sz

        self.sock.write(premsg, i + 2)
        self.sock.write(msg)
        #print(hex(len(msg)), hexlify(msg, ":"))
        self._send_str(self.client_id)
        if self.lw_topic:
            self._send_str(self.lw_topic)
            self._send_str(self.lw_msg)
        if self.user is not None:
            self._send_str(self.user)
            self._send_str(self.pswd)
        resp = self.sock.read(4)
        assert resp[0] == 0x20 and resp[1] == 0x02
        if resp[3] != 0:
            raise MQTTException(resp[3])
        return resp[2] & 1

    def disconnect(self):
        self.sock.write(b"\xe0\0")
        self.sock.close()

    def ping(self):
        self.sock.write(b"\xc0\0")

    def publish(self, topic, msg, retain=False, qos=0):
        pkt = bytearray(b"\x30\0\0\0")
        pkt[0] |= qos << 1 | retain
        sz = 2 + len(topic) + len(msg)
        if qos > 0:
            sz += 2
        assert sz < 2097152
        i = 1
        while sz > 0x7f:
            pkt[i] = (sz & 0x7f) | 0x80
            sz >>= 7
            i += 1
        pkt[i] = sz
        #print(hex(len(pkt)), hexlify(pkt, ":"))
        self.sock.write(pkt, i + 1)
        self._send_str(topic)
        if qos > 0:
            self.pid += 1
            pid = self.pid
            struct.pack_into("!H", pkt, 0, pid)
            self.sock.write(pkt, 2)
        self.sock.write(msg)
        if qos == 1:
            while 1:
                op = self.wait_msg()
                if op == 0x40:
                    sz = self.sock.read(1)
                    assert sz == b"\x02"
                    rcv_pid = self.sock.read(2)
                    rcv_pid = rcv_pid[0] << 8 | rcv_pid[1]
                    if pid == rcv_pid:
                        return
        elif qos == 2:
            assert 0

    def subscribe(self, topic, qos=0):
        assert self.cb is not None, "Subscribe callback is not set"
        pkt = bytearray(b"\x82\0\0\0")
        self.pid += 1
        struct.pack_into("!BH", pkt, 1, 2 + 2 + len(topic) + 1, self.pid)
        #print(hex(len(pkt)), hexlify(pkt, ":"))
        self.sock.write(pkt)
        self._send_str(topic)
        self.sock.write(qos.to_bytes(1, "little"))
        while 1:
            op = self.wait_msg()
            if op == 0x90:
                resp = self.sock.read(4)
                #print(resp)
                assert resp[1] == pkt[2] and resp[2] == pkt[3]
                if resp[3] == 0x80:
                    raise MQTTException(resp[3])
                return

    # Wait for a single incoming MQTT message and process it.
    # Subscribed messages are delivered to a callback previously
    # set by .set_callback() method. Other (internal) MQTT
    # messages processed internally.
    def wait_msg(self):
        res = self.sock.read(1)
        self.sock.setblocking(True)
        if res is None:
            return None
        if res == b"":
            raise OSError(-1)
        if res == b"\xd0":  # PINGRESP
            sz = self.sock.read(1)[0]
            assert sz == 0
            return None
        op = res[0]
        if op & 0xf0 != 0x30:
            return op
        sz = self._recv_len()
        topic_len = self.sock.read(2)
        topic_len = (topic_len[0] << 8) | topic_len[1]
        topic = self.sock.read(topic_len)
        sz -= topic_len + 2
        if op & 6:
            pid = self.sock.read(2)
            pid = pid[0] << 8 | pid[1]
            sz -= 2
        msg = self.sock.read(sz)
        self.cb(topic, msg)
        if op & 6 == 2:
            pkt = bytearray(b"\x40\x02\0\0")
            struct.pack_into("!H", pkt, 2, pid)
            self.sock.write(pkt)
        elif op & 6 == 4:
            assert 0

    # Checks whether a pending message from server is available.
    # If not, returns immediately with None. Otherwise, does
    # the same processing as wait_msg.
    def check_msg(self):
        self.sock.setblocking(False)
        return self.wait_msg()
ESP32 MQTT using Micro-Python install umqttsimple.py library

Save the new file by the name umqttsimple.py. This library will be used while programming the ESP MQTT publisher.

ESP32 ESP8266 MQTT Publisher DHT Sensor Readings MicroPython Script

After installing the umqttsimple library, click File > Open and open the boot.py file from the MicroPython device. If the file is not present, then create one by going to File > New to open a new file. Copy the code given below in that file and save it as boot.py in your MicroPython device. You need to enter your network credentials and your Raspberry Pi IP address.

boot.py

import time
from umqttsimple import MQTTClient
import ubinascii
import machine
import micropython
import network
import esp
from machine import Pin
import dht
esp.osdebug(None)
import gc
gc.collect()

ssid = 'YOUR_SSID'
password = 'YOUR_PASSWORD'
mqtt_server = '192.168.10.8' #Replace with your MQTT Broker IP

client_id = ubinascii.hexlify(machine.unique_id())
TOPIC_PUB_TEMP = b'esp/dht/temperature'
TOPIC_PUB_HUM = b'esp/dht/humidity'

last_message = 0
message_interval = 5

sensor = dht.DHT22(Pin(14))
#sensor = dht.DHT11(Pin(14))  #if using DHT11, comment the above line and uncomment this line.

station = network.WLAN(network.STA_IF)
station.active(True)
station.connect(ssid, password)

while station.isconnected() == False:
  pass

print('Connection successful')
print(station.ifconfig())

How the Code Works?

We start off by importing all the necessary libraries which would be required including time ,umqttsimple, MQTTClient, ubinascii, machine, micropython, network, esp, dht and Pin class from machine module.

import time
from umqttsimple import MQTTClient
import ubinascii
import machine
import micropython
import network
import esp
from machine import Pin
import dht

Then we activate the garbage collector and set the debug to ‘None.’

esp.osdebug(None)
import gc
gc.collect()


Next, we will enter our network Wi-fi, its password and the IP of our MQTT broker. You will have to specify your own figures in order for the network to connect with the MQTT and ESP32/ESP8266 board.

ssid = 'YOUR_SSID' #write your own wi-fi name
password = 'YOUR_PASSWORD' #write your own password
mqtt_server = '192.168.10.8'  #Replace with your MQTT Broker IP

To initialize DHT11/DHT22 sensors, create an instance of the DHT class on GPIO14  by creating an object with the name of sensor. 

sensor = dht.DHT22(Pin(14))
#sensor = dht.DHT11(Pin(14))  #if using DHT11, comment the above line and uncomment this line.

Then we will create a client_id variable which saves the ESP unique ID. This is required to form a MQTT client.

client_id = ubinascii.hexlify(machine.unique_id())

Next we will define two topics which the ESP32 board will publish to. The temperature readings will be published to esp/dht/temperature and the humidity readings will be published to esp/dht/humidity.

TOPIC_PUB_TEMP = b'esp/dht/temperature'
TOPIC_PUB_HUM = b'esp/dht/humidity'

We will obtain the sensor readings after every 5 seconds. The variables below will monitor the time interval between the readings.

last_message = 0
message_interval = 5

MicroPython: Connecting to a Wi-Fi Network

Next, we will connect the ESP32/ESP8266 board to the Wi-Fi network. The network.WLAN() is used to create a WLAN network interface object. 

Supported interfaces are:

  • network.STA_IF (station mode)
  • network.AP_IF (Soft access point mode)

After that, activate the station by passing the “True” argument to the sta_if.active() method. The connect() method is used to connect to the specified wireless network using the specified Wi-Fi name (SSID) and password. 

station = network.WLAN(network.STA_IF)
station.active(True)
station.connect(ssid, password)

In station mode, isconnected() method returns “True” if ESP32/ESP8266 successfully connects to a Wi-Fi network and a device also assigned a valid IP address. Otherwise, it returns “False”. This statement checks if the ESP device connects to the Wi-Fi or not. The code does not move to the next step till the ESP board is not connected to the Wi-Fi network.

while station.isconnected() == False:
  pass

After a Wi-Fi connection is established on the ESP board, an IP address gets assigned to the ESP device. The ifconfig() method provides an IP address assigned to the ESP32/ESP8266 device. In this statement, we print the IP address using the ifconfig() method on the station object which we created previously.

print('Connection successful')
print(station.ifconfig())

main.py

The next step is to create the main.py file for the ESP32/ESP8266 Publisher. Create a new file and name it as main.py. Save it to the MicroPython device. Copy the code given below in that file.

def connect_mqtt():
  global client_id, mqtt_server
  client = MQTTClient(client_id, mqtt_server)

  client.connect()
  print('Connected to %s MQTT broker' % (mqtt_server))
  return client

def restart_and_reconnect():
  print('Failed to connect to MQTT broker. Reconnecting...')
  time.sleep(10)
  machine.reset()

try:
  client = connect_mqtt()
except OSError as e:
  restart_and_reconnect()

while True:
  try:
    if (time.time() - last_message) > message_interval:
      sensor.measure()
      temp = sensor.temperature()
      hum = sensor.humidity()
      temp = (b'{0:3.1f}'.format(temp))
      hum =  (b'{0:3.1f}'.format(hum))
      print('Temperature: %s' %temp, 'Humidity: %s' %hum)
      client.publish(TOPIC_PUB_TEMP, temp)
      print('Published message %s to topic %s' %(temp,TOPIC_PUB_TEMP))
      client.publish(TOPIC_PUB_HUM, hum)
      print('Published message %s to topic %s' %(hum,TOPIC_PUB_HUM))
      print()
      last_message = time.time()
  except OSError as e:
    restart_and_reconnect()

How the Code Works?

First, we will define the connect_mqtt() function. This is used to connect the ESP32/ESP8266 board with the MQTT broker. We will initialize global variables for client_id and mqtt_server. This way we will be able to use them anywhere in the program code.

The next step is to initialize the MQTTClient() method with two parameters. The first parameter is the ID of the client named as: ‘client_id’ and the second parameter is the mqtt_server which is the IP address of the broker which was already configured in the boot.py file. This method will be assigned to the object ‘client.’ Then we will connecting the client with the broker. This would be done by using the connect() method on the object which we created previously: ‘client.’

def connect_mqtt():
  global client_id, mqtt_server
  client = MQTTClient(client_id, mqtt_server)
  client.connect()
  print('Connected to %s MQTT broker' % (mqtt_server))
  return client

In case of failure of connecting with the ESP32/ESP8266 board, we will also create a restart_and_reconnect() function which would display a failure message on the screen. The ESP development board will restart after 10 seconds.

def restart_and_reconnect():
  print('Failed to connect to MQTT broker. Reconnecting...')
  time.sleep(10)
  machine.reset()

The next step is to learn how to connect our ESP32/ESP8266 board to the MQTT broker. This is accomplished by forming a client. We will create a client through the connect() function which we defined previously. Moreover, we have to keep track of a failure in connection as well. In case it happens, the ESP32/ESP8266 board will restart.

try:
  client = connect_mqtt()
except OSError as e:
  restart_and_reconnect()

Publish MQTT Message to Topic

After that, we will set up a while loop which continues endlessly by acquiring the DHT sensor data and publishing it at the specific topics. After every 5 seconds, the ESP32/ESP8266 obtains the temperature and humidity readings and save them in the variables ‘temp’ and ‘hum’ respectively

The temperature reading is published to the topic esp/dht/temperature and the humidity reading is published to the topic esp/dht/humidity.

To publish the messages we use the publish() method. This method has two arguments. The first argument is the publishing topic which we already configured in our boot.py file. The second argument is the MQTT message which is the DHT sensor reading in our case.

In case of a failure during the process, we will call the restart_and _reconnect() function which restarts the ESP32/ESP8266 board.

while True:
  try:
    if (time.time() - last_message) > message_interval:
      sensor.measure()
      temp = sensor.temperature()
      hum = sensor.humidity()
      temp = (b'{0:3.1f}'.format(temp))
      hum =  (b'{0:3.1f}'.format(hum))
      print('Temperature: %s' %temp, 'Humidity: %s' %hum)
      client.publish(TOPIC_PUB_TEMP, temp)
      print('Published message %s to topic %s' %(temp,TOPIC_PUB_TEMP))
      client.publish(TOPIC_PUB_HUM, hum)
      print('Published message %s to topic %s' %(hum,TOPIC_PUB_HUM))
      print()
      last_message = time.time()
  except OSError as e:
    restart_and_reconnect()

Uploading MicroPython Script

To upload this MicroPython script to your ESP32/ESP8266 publisher device, go to Files and click on ‘Save as’ or click the Save icon. Save the file to the MicroPython device as main.py and click ok.

ESP32 MQTT using Micro-Python Control Output Publisher main.py file

Now press the Run current script icon.

This would upload the code onto our ESP32/ESP8266 board. After the code is uploaded, press the Enable/RESET button on your ESP32/ESP8266.

ESP32 enable button
ESP8266 NodeMCU reset button

Open the Thonny Shell. The ESP board will first connect to the Wi-Fi and the MQTT broker. After every 5 seconds, it will publish temperature reading to the esp/dht/temperature topic and the humidity reading to the esp/dht/humidity topic.

ESP32 and ESP8266 MQTT Publish and Subscribe DHT Sensor Readings Shell Terminal demo
Thonny Shell Terminal

Setting up Node-Red Dashboard as an MQTT Subscriber

Now let us build the Node-Red Dashboard to view the DHT sensor readings in an interactive manner. Our ESP32/SP8266 publisher is continuously sending temperature and humidity readings to the specific topics. Let us use Node-Red to subscribe to those topics and view them on our laptop.

We will be using Node-Red to subscribe to the topics. However, you can use any other MQTT subscription service as well. In order to get started with Node-Red on Raspberry Pi, refer to the guide:

To access Node-RED, we need the IP address of our Raspberry Pi and the port number on which Node-RED is accessible. By default, it starts on port 1880. Open any web browser and enter the RPI IP address followed by the port number.

192.168.18.8:1880

Creating Flow

This will open the Node-RED interface. You can start creating the flow.

access Node-RED

Make sure the dashboard is already installed. Head over to the extreme right side and find dashboard.

Node-Red Dashboard UI Overview pic1

After you click it, the Dashboard tab appears. The first step is to create the tab. Head over to the Dashboard tab and click +tab to create a new tab. Specify the name and icon and click Update for the changes to take place. Here we are using icons from Angular Material Icons.

We will create one tab named Home.

Node-Red Dashboard UI Overview pic4

Note: You can use any name according to your preference but for the icon you have to use one available at these three links found below:

Add Widgets

The next step is to add the widgets. We will add one group to the tab. Click +group in the tab and create the group. We have named it ‘DHT Readings.’

ESP32 MQTT Publish and Subscribe Project Set up Node-Red Dashboard 1

We will display the temperature and humidity readings on gauges. Therefore we add four nodes to the flow. Head over to the Nodes section found at the far left and scroll down to view the nodes under Dashboard. Drag and drop two gauges and two mqtt in nodes to the flow as shown below:

ESP32 MQTT Publish and Subscribe Project Set up Node-Red Dashboard 2

Now double click the first mqtt node to edit its properties as shown below.

Here we have set the sever (MQTT Broker) to localhost:1883 as we are using Mosquitto Broker on our Raspberry Pi. Specify the topic to be subscribed. This node is being subscribed to the topic ‘esp/dht/temperature.’ Click the Done button.

ESP32 and ESP8266 DHT MQTT Publish and Subscribe Project Set up Node-Red Dashboard 1

Similarly, double click the second mqtt node and edit its properties as shown below. Notice that this particular node is subscribed to the topic ‘esp/dht/humidity.’ Click the Done button.

ESP32 and ESP8266 DHT MQTT Publish and Subscribe Project Set up Node-Red Dashboard 2

Now double click the gauges and edit their properties as well. Set the first one for temperature and the second one for humidity.

Finally connect the nodes as shown below and then click the Deploy button found at the top.

ESP32 and ESP8266 DHT MQTT Publish and Subscribe Project Set up Node-Red Dashboard 3

Now you may see that the status of the mqtt nodes have been changed to connected.

ESP32 and ESP8266 DHT MQTT Publish and Subscribe Project Set up Node-Red Dashboard 4

To view the UI, open a new web browser and type: http://Your_RPI_IP_address:1880/ui

The user interface will open up once you press enter.

Here you can view the dashboard consisting of the two gauges with readings from the DHT22 sensor. These readings get updated to new ones after every 5 seconds.

ESP32 and ESP8266 DHT MQTT Publish and Subscribe Project Set up Node-Red Dashboard 5

You may also like to read:

Leave a Comment