MicroPython ESP32 MQTT Publish Multiple Sensor Readings to Node-Red

In this tutorial, we will learn to publish multiple sensor readings to Node-Red with ESP32 MQTT using MicroPython. We will perform MQTT communication with ESP32 and Node-Red by publishing and subscribing to MQTT topics. using mosquito broker. There will be one ESP32 MQTT publisher and Node-Red as a subscriber. We will publish DS18B20, DHT, and BME280 sensor readings to MQTT with ESP32, and Node-Red Dashboard will subscribe to the MQTT topics and display all the sensor readings on the Dashboard.

MicroPython ESP32 MQTT Publish Multiple Sensor Readings to Node-Red

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.

Both Publisher and Subscriber will make connections with the MQTT broker installed on Raspberry Pi. After that, the ESP32 will publish sensor data to the Node-Red dashboard on specified topics.

We have a similar guide for ESP8266 NodeMCU:

Prerequisites

Before continuing with this project, make sure you have the latest version of MicroPython and Thonny IDE installed on your PC. Your ESP32 board should be already flashed with the firmware.

If you are using uPyCraft IDE, you can check this getting started guide:

Additionally, we will be using the Mosquitto MQTT broker to send and receive messages. So make sure your broker is installed on the Raspberry Pi.

MicroPython ESP32 MQTT Publish Multiple Sensors to Node-Red

The diagram below illustrates the process that we will follow in our ESP32 MQTT Publisher Subscriber project involving multiple sensors.

ESP32 MQTT Publish Sensor Readings to Node-Red using Micro-Python
  1. An ESP32 board connected with DHT22, BME280, and DS18B20 sensors 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 ESP32 board publishes the DHT22 temperature readings on the MQTT topic: esp32/dht/temperature and publishes the DHT22 humidity readings on the MQTT topic: esp32/dht/humidity.
  3. It also publishes the BME280 temperature readings on the topic: esp32/bme280/temperature. It publishes the BME280 humidity readings on the topic: esp32/bme280/humidity. Likewise, it publishes the BME280 pressure readings on the topic: esp32/bme280/pressure.
  4. Similarly, this board publishes the DS18B20 temperature readings in Celsius on the MQTT topic: esp32/ds18b20/temperatureC and publishes the DS18B20 temperature readings in Fahrenheit on the MQTT topic: esp32/ds18b20/temperatureF.
  5. We have Node-Red as a subscriber to these seven topics. Node-Red receives the sensor data and displays them in an interactive manner in its dashboard.

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 Publish Multiple Sensor Readings on MQTT Topics

Our ESP32 MQTT Publisher is connected with a DHT22 sensor, a BME280 sensor, and a DS18B20 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 to obtain sensor output reading. They are internally composed of a humidity sensing sensor and a thermistor. These two components measure humidity and temperature.

The BME280 sensor is used to measure readings regarding ambient temperature, barometric pressure, and relative humidity. It is mostly used in web and mobile applications where low power consumption is key. This sensor uses I2C or SPI to communicate data with the micro-controllers. Although there are several different versions of BME280 available in the market, the one we will be studying uses the I2C communication protocol.

DS18B20 is a temperature sensor which is single wire programmable in nature. It is widely used to measure temperature of chemical solutions and substances which are present in a hard environment. One of the advantages of using this sensor is that we only require a single pin of our ESP32 board to transfer data. Thus, it is extremely convenient to use with the micro-controller as we can measure multiple temperatures by using the least number of pins on our development board. 

Interfacing DHT22, BME280, and DS18B20 sensors with ESP32

We will now learn how to connect the sensors with the ESP32 board. We will need the following components.

ESP32 MQTT Publish BME280 DS18B20 DHT22 sensors readings

Required Components

  1. ESP32 board
  2. DHT22 sensor
  3. 10k ohm resistor (not required if using the DHT22 sensor module)
  4. BME280 sensor
  5. DS18B20  sensor
  6. 4.7k ohm resistor
  7. Breadboard
  8. Connecting Wires

DHT22 Connections

  • The first pin the DHT22 sensor is a power supply(VCC) pin. Connect it with the 3.3 volt or Vin pin of ESP32.
  • Data out is the pin through which we get temperature and humidity samples from the DHT sensor. Connect this pin with GPIO15 of ESP32 and also connect the data pin with a 10k pull-up resistor. But you can also use any digital pin of ESP32. 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 board.

BME280 Connections

The connection of BME280 with the ESP32 boards is very easy. We have to connect the VCC terminal with 3.3V, ground with the ground (common ground), SCL of the sensor with SCL of the module, and SDA of the sensor with the SDA pin of the ESP32 module.

The I2C pin in ESP32 for SDA is GPIO21 and for SCL is GPIO22.

DS18B20 Connections

The DS18B20 sensor can be powered in two different modes.

Normal Mode: The sensor is powered through an external source through the VDD pin and 4.7K ohm pull-up resistor.

Parasite Mode: The sensor obtains power from its own data line. Hence, no external power supply is required.

We will power DS10B20 in normal mode hence it is powered with its Vcc pin from 3.3V pin of ESP32 board.

The DS18B20 sensor has three terminals. The first terminal is grounded with the ESP32 board. The data line of the sensor, which is the middle terminal, is connected through GPIO4 through a pull-up resistor of 4.7k-ohm. We can choose any other GPIO pin as well. The third terminal is powered by 3.3V from the ESP board.

You can use any other GPIO pin of ESP32 module. You can refer to the post below, to know more about ESP32 GPIO pins:

Connect ESP32 with the sensors as shown in the schematic diagram below:

ESP32 with DHT22, BME280 and DS18B20 schematic diagram

We have used the same connections as described above.

You may like to read:

Install MicroPython Libraries

For this project, we will have to install libraries for the BME280 sensor, DS18B20 sensor, and MQTT. The DHT library already comes with the MicroPython firmware so there is no need to install it ourselves.

Install BME280 Library

We will have to install the BME280 library for MicroPython to continue with our project.

To successfully do that, open your Thonny IDE with your ESP32 board plugged in your system. Go to Tools > Manage Packages. This will open up the Thonny Package Manager.

Raspberry Pi Pico Installing ssd1306 OLED library MicoPython 1

Search for “bme280” in the search bar by typing its name and clicking the button ‘Search on PyPI.’ From the following search results click on the one highlighted below: micropython-bme280. Install this library.

Installing BME280 Micropython library Thonny

After a few moments this library will get successfully installed.

Install DS18B20 Libraries

For this project we will require two libraries to work with the DS18B20 sensor: ds18x20.py and onewire.py. Copy both of these libraries and save them in your MicroPython device with the respective file names. Open a new file in Thonny. Copy the libraries given below. Save them to your ESP32 board with names ds18x20.py and onewire.py under the lib folder.

ds18x20.py

# DS18x20 temperature sensor driver for MicroPython.
# MIT license; Copyright (c) 2016 Damien P. George

from micropython import const

_CONVERT = const(0x44)
_RD_SCRATCH = const(0xBE)
_WR_SCRATCH = const(0x4E)


class DS18X20:
    def __init__(self, onewire):
        self.ow = onewire
        self.buf = bytearray(9)

    def scan(self):
        return [rom for rom in self.ow.scan() if rom[0] in (0x10, 0x22, 0x28)]

    def convert_temp(self):
        self.ow.reset(True)
        self.ow.writebyte(self.ow.SKIP_ROM)
        self.ow.writebyte(_CONVERT)

    def read_scratch(self, rom):
        self.ow.reset(True)
        self.ow.select_rom(rom)
        self.ow.writebyte(_RD_SCRATCH)
        self.ow.readinto(self.buf)
        if self.ow.crc8(self.buf):
            raise Exception("CRC error")
        return self.buf

    def write_scratch(self, rom, buf):
        self.ow.reset(True)
        self.ow.select_rom(rom)
        self.ow.writebyte(_WR_SCRATCH)
        self.ow.write(buf)

    def read_temp(self, rom):
        buf = self.read_scratch(rom)
        if rom[0] == 0x10:
            if buf[1]:
                t = buf[0] >> 1 | 0x80
                t = -((~t + 1) & 0xFF)
            else:
                t = buf[0] >> 1
            return t - 0.25 + (buf[7] - buf[6]) / buf[7]
        else:
            t = buf[1] << 8 | buf[0]
            if t & 0x8000:  # sign bit set
                t = -((t ^ 0xFFFF) + 1)
            return t / 16

onewire.py

# 1-Wire driver for MicroPython
# MIT license; Copyright (c) 2016 Damien P. George

import _onewire as _ow


class OneWireError(Exception):
    pass


class OneWire:
    SEARCH_ROM = 0xF0
    MATCH_ROM = 0x55
    SKIP_ROM = 0xCC

    def __init__(self, pin):
        self.pin = pin
        self.pin.init(pin.OPEN_DRAIN, pin.PULL_UP)

    def reset(self, required=False):
        reset = _ow.reset(self.pin)
        if required and not reset:
            raise OneWireError
        return reset

    def readbit(self):
        return _ow.readbit(self.pin)

    def readbyte(self):
        return _ow.readbyte(self.pin)

    def readinto(self, buf):
        for i in range(len(buf)):
            buf[i] = _ow.readbyte(self.pin)

    def writebit(self, value):
        return _ow.writebit(self.pin, value)

    def writebyte(self, value):
        return _ow.writebyte(self.pin, value)

    def write(self, buf):
        for b in buf:
            _ow.writebyte(self.pin, b)

    def select_rom(self, rom):
        self.reset()
        self.writebyte(self.MATCH_ROM)
        self.write(rom)

    def scan(self):
        devices = []
        diff = 65
        rom = False
        for i in range(0xFF):
            rom, diff = self._search_rom(rom, diff)
            if rom:
                devices += [rom]
            if diff == 0:
                break
        return devices

    def _search_rom(self, l_rom, diff):
        if not self.reset():
            return None, 0
        self.writebyte(self.SEARCH_ROM)
        if not l_rom:
            l_rom = bytearray(8)
        rom = bytearray(8)
        next_diff = 0
        i = 64
        for byte in range(8):
            r_b = 0
            for bit in range(8):
                b = self.readbit()
                if self.readbit():
                    if b:  # there are no devices or there is an error on the bus
                        return None, 0
                else:
                    if not b:  # collision, two devices with different bit meaning
                        if diff > i or ((l_rom[byte] & (1 << bit)) and diff != i):
                            b = 1
                            next_diff = i
                self.writebit(b)
                if b:
                    r_b |= 1 << bit
                i -= 1
            rom[byte] = r_b
        return rom, next_diff

    def crc8(self, data):
        return _ow.crc8(data)

Install MQTT Library

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

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()

Create a new file in Thonny IDE. Copy the code given here and save it to your MicroPython device.

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.

Now we are ready to program our ESP32 board with DHT, BME280, and DS18B20 sensors for MQTT protocol in MicroPython.

MicroPython Script: ESP32 MQTT Publisher for Multiple Sensor Readings

After installing all the necessary libraries, 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
from machine import Pin,I2C  
import micropython
import network
import esp
import onewire, ds18x20
import dht
import bme280

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

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

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

#MQTT Topics
TOPIC_PUB_TEMP_DHT = b'esp32/dht/temperature'
TOPIC_PUB_HUM_DHT = b'esp32/dht/humidity'

TOPIC_PUB_TEMP_BME280 = b'esp32/bme280/temperature'
TOPIC_PUB_HUM_BME280 = b'esp32/bme280/humidity'
TOPIC_PUB_PRES_BME280 = b'esp32/bme280/pressure'

TOPIC_PUB_TEMP_C = b'esp32/ds18b20/temperatureC'
TOPIC_PUB_TEMP_F = b'esp32/ds18b20/temperatureF'

last_message = 0
message_interval = 10

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

i2c = I2C(scl=Pin(22), sda=Pin(21), freq=10000)     #initializing the I2C method for ESP32

ds_pin = Pin(4)
ds_sensor = ds18x20.DS18X20(onewire.OneWire(ds_pin))

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, bme280, onewire, ds18x20 and Pin and I2C classes from the machine module.

import time
from umqttsimple import MQTTClient
import ubinascii
import machine
from machine import Pin,I2C  
import micropython
import network
import esp
import onewire, ds18x20
import dht
import bme280

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 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

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())

Defining Topics

Next, we will define the seven topics which the ESP32 board will publish to.

The temperature readings for DHT22 will be published to esp32/dht/temperature and the humidity readings for DHT22 will be published to esp32/dht/humidity.

The temperature readings of BME280 will be published to esp32/bme280/temperature. The humidity readings of BME280 will be published to esp32/bme280/humidity. Likewise, the pressure readings of BME280 will be published to esp32/bme280/pressure.

The temperature readings in degree Celsius for DS18B20 will be published to esp32/ds18b20/temperatureC and the temperature readings in degree Fahrenheit for DS18B20 sensor will be published to esp32/ds18b20/temperatureF.

#MQTT Topics
TOPIC_PUB_TEMP_DHT = b'esp32/dht/temperature'
TOPIC_PUB_HUM_DHT = b'esp32/dht/humidity'

TOPIC_PUB_TEMP_BME280 = b'esp32/bme280/temperature'
TOPIC_PUB_HUM_BME280 = b'esp32/bme280/humidity'
TOPIC_PUB_PRES_BME280 = b'esp32/bme280/pressure'

TOPIC_PUB_TEMP_C = b'esp32/ds18b20/temperatureC'
TOPIC_PUB_TEMP_F = b'esp32/ds18b20/temperatureF'

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

last_message = 0
message_interval = 10

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

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

We will initialize the I2C method for the BME280 sensor by giving it three arguments. The first argument specifies the GPIO pin for SCL. This is given as GPIO22 for ESP32. The second parameter specifies the GPIO pin for the SDA. This is given as GPIO21 for ESP32. Keep in mind, these are the default I2C pins for SCL and SDA which we have used for the ESP32 board. The third parameter specifies the maximum frequency for SCL to be used.

i2c = I2C(scl=Pin(22), sda=Pin(21), freq=10000)     #initializing the I2C method for ESP32

Next, we will create an instance of Pin class with an object name of ds_pin and set GPIO4 as the DS18B20 data line pin.

ds_pin = Pin(4)
ds_sensor = ds18x20.DS18X20(onewire.OneWire(ds_pin))

MicroPython: Connecting to a Wi-Fi Network

Next, we will connect the ESP32 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 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 ESP32 device connects to the Wi-Fi or not. The code does not move to the next step till the ESP32 board is not connected to the Wi-Fi network.

while station.isconnected() == False:
  pass

After a Wi-Fi connection is established on the ESP32 board, an IP address gets assigned to the ESP32 device. The ifconfig() method provides an IP address assigned to the ESP32 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 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()
      temperature_DHT = sensor.temperature()
      humidity_DHT = sensor.humidity()
      temperature_DHT = (b'{0:3.1f}'.format(temperature_DHT))
      humidity_DHT =  (b'{0:3.1f}'.format(humidity_DHT))

      bme = bme280.BME280(i2c=i2c)         
      temperature_BME280 = bme.values[0]         
      humidity_BME280 = bme.values[2]                     
      pressure_BME280 = bme.values[1]  
 
      roms = ds_sensor.scan()
      ds_sensor.convert_temp()
      for rom in roms:
        msg = ds_sensor.read_temp(rom)
        tempC = round(msg,2)
        tempF = round(tempC * (9/5) + 32.0, 2)
  
      client.publish(TOPIC_PUB_TEMP_DHT, temperature_DHT)
      print('Published message %s to topic %s' %(temperature_DHT,TOPIC_PUB_TEMP_DHT))
      client.publish(TOPIC_PUB_HUM_DHT, humidity_DHT)
      print('Published message %s to topic %s' %(humidity_DHT,TOPIC_PUB_HUM_DHT))
      
      client.publish(TOPIC_PUB_TEMP_BME280, temperature_BME280)
      print('Published message %s to topic %s' %(temperature_BME280,TOPIC_PUB_TEMP_BME280))
      client.publish(TOPIC_PUB_HUM_BME280, humidity_BME280)
      print('Published message %s to topic %s' %(humidity_BME280,TOPIC_PUB_HUM_BME280))
      client.publish(TOPIC_PUB_PRES_BME280, pressure_BME280)
      print('Published message %s to topic %s' %(pressure_BME280,TOPIC_PUB_PRES_BME280))
      
      client.publish(TOPIC_PUB_TEMP_C, str(tempC))
      print('Published message %s to topic %s' %(tempC,TOPIC_PUB_TEMP_C))
      client.publish(TOPIC_PUB_TEMP_F, str(tempF))
      print('Published message %s to topic %s' %(tempF,TOPIC_PUB_TEMP_F))
      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 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 board, we will also create a restart_and_reconnect() function which would display a failure message on the screen. The ESP32 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 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 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 in which after every 10 seconds the ESP32 obtains the DHT, BME280 and DS18B20 sensor readings and stores them in their appropriate variables.

try:
    if (time.time() - last_message) > message_interval:
        
      sensor.measure()
      temperature_DHT = sensor.temperature()
      humidity_DHT = sensor.humidity()
      temperature_DHT = (b'{0:3.1f}'.format(temperature_DHT))
      humidity_DHT =  (b'{0:3.1f}'.format(humidity_DHT))

      bme = bme280.BME280(i2c=i2c)         
      temperature_BME280 = bme.values[0]         
      humidity_BME280 = bme.values[2]                     
      pressure_BME280 = bme.values[1]  
 
      roms = ds_sensor.scan()
      ds_sensor.convert_temp()
      for rom in roms:
        msg = ds_sensor.read_temp(rom)
        tempC = round(msg,2)
        tempF = round(tempC * (9/5) + 32.0, 2)

Publishing MQTT Message to Topics

Then it publishes the sensor readings on their respective topics. 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 sensor reading in our case.

      client.publish(TOPIC_PUB_TEMP_DHT, temperature_DHT)
      print('Published message %s to topic %s' %(temperature_DHT,TOPIC_PUB_TEMP_DHT))
      client.publish(TOPIC_PUB_HUM_DHT, humidity_DHT)
      print('Published message %s to topic %s' %(humidity_DHT,TOPIC_PUB_HUM_DHT))
      
      client.publish(TOPIC_PUB_TEMP_BME280, temperature_BME280)
      print('Published message %s to topic %s' %(temperature_BME280,TOPIC_PUB_TEMP_BME280))
      client.publish(TOPIC_PUB_HUM_BME280, humidity_BME280)
      print('Published message %s to topic %s' %(humidity_BME280,TOPIC_PUB_HUM_BME280))
      client.publish(TOPIC_PUB_PRES_BME280, pressure_BME280)
      print('Published message %s to topic %s' %(pressure_BME280,TOPIC_PUB_PRES_BME280))
      
      client.publish(TOPIC_PUB_TEMP_C, str(tempC))
      print('Published message %s to topic %s' %(tempC,TOPIC_PUB_TEMP_C))
      client.publish(TOPIC_PUB_TEMP_F, str(tempF))
      print('Published message %s to topic %s' %(tempF,TOPIC_PUB_TEMP_F))
      print()
      last_message = time.time()

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

  except OSError as e:
    restart_and_reconnect()

Uploading MicroPython Script

To upload this MicroPython script to your ESP32 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.

Now press the Run current script icon.

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

ESP32 enable button

Open the Thonny Shell. The ESP32 board will first connect to the Wi-Fi and the MQTT broker. After every 10 seconds, it will publish the sensor readings on the respective topics.

ESP32 MQTT Publish Sensor Readings to Node-Red using Micro-Python Shell Terminal
Thonny Shell Terminal

Setting up Node-Red Dashboard as an MQTT Sensors Readings Subscriber

Now let us build the Node-Red Dashboard to view the DHT, BME280, and DS18B20 sensor readings in an interactive manner. Our ESP32 publisher is continuously sending the readings to the specific topics after every 10 seconds. Let us use Node-Red to subscribe to those topics and view them on our laptops.

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:

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 will create three groups, one for each sensor.

DHT Readings

The first group is named ‘DHT Readings.’

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

We will display the temperature and humidity readings obtained from the DHT sensor on gauges. Therefore we add four nodes to the flow. Head to the Nodes section 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 ‘esp32/dht/temperature.’ Click the Done button.

ESP32 MQTT Publish and Subsribe Multiple Sensor 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 ‘esp32/dht/humidity.’ Click the Done button.

ESP32 MQTT Publish and Subsribe Multiple Sensor 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:

ESP32 MQTT Publish and Subsribe Multiple Sensor Set up Node-Red Dashboard 3

BME280 Readings

Now let us create the group for BME280 readings. Click +group in the tab and create a new group as shown below:

ESP32 MQTT Publish and Subsribe Multiple Sensor Set up Node-Red Dashboard 4

We have named it ‘BME280 Readings.’

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

We will display the temperature and humidity readings of BME280 on gauges and the pressure readings of BME280 on a line chart. Therefore we add six nodes to the flow. Head to the Nodes section at the far left and scroll down to view the nodes under Dashboard. Drag and drop two gauges, one chart, and two MQTT in nodes to the flow as shown below:

ESP32 MQTT Publish and Subsribe Multiple Sensor Set up Node-Red Dashboard 5

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 ‘esp32/bme280/temperature.’ Click the Done button.

ESP32 MQTT Publish and Subsribe Multiple Sensor Set up Node-Red Dashboard 6

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

ESP32 MQTT Publish and Subsribe Multiple Sensor Set up Node-Red Dashboard 7

Similarly, double click the third mqtt node and edit its properties as shown below. Notice that this particular node is subscribed to the topic ‘esp32/bme280/pressure.’ Click the Done button.

ESP32 MQTT Publish and Subsribe Multiple Sensor Set up Node-Red Dashboard 8

Now double click the gauges and edit their properties as well. Set the first one for temperature and the second one for humidity. Remember to choose the correct Group.

For the chart, edit the properties as shown below. Remember to choose the correct Group. The line chart will plot pressure readings.

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

Finally, join the nodes as shown below:

ESP32 MQTT Publish and Subsribe Multiple Sensor Set up Node-Red Dashboard 9

DS18B20 Readings

Finally create the group for DS18B20 readings. Click +group in the tab and create a new group. We have named it ‘DS18B20 Readings.’

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

We will display both the temperature readings from DS18B20 sensor on gauges. Therefore we add four nodes to the flow. Head to the Nodes section 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 Subsribe Multiple Sensor Set up Node-Red Dashboard 10

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 ‘esp32/ds18b20/temperatureC.’ Click the Done button.

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

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

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

Now double click the gauges and edit their properties as well. Set the first one for temperature in Celsius and the second one for temperature in Fahrenheit. Remember to choose the correct Group.

Finally wire the nodes as shown below:

ESP32 MQTT Publish and Subsribe Multiple Sensor Set up Node-Red Dashboard 11

You can also change the theme of the dashboard. Now deploy the changes by clicking the Deploy button found at the top. The status of the mqtt nodes changes to connected.

ESP32 MQTT Publish and Subsribe Multiple Sensor Set up Node-Red Dashboard 12

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, two gauges and a line chart with readings from the BME280 sensor and two gauges with readings from the DS18B20 sensor.

ESP32 MQTT Publish and Subsribe Multiple Sensor Set up Node-Red Dashboard 13

You may also like to read:

Leave a Comment