ESP32 ESP8266 MicroPython MQTT Publish Subscribe DS18B20 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 DS18B20 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 Subscribe DS18B20 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 DS18B20 sensor which is used to measure temperature.

Prerequisites

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

We will be using the same Thonny IDE as we have done previously when we learned how to blink and chase LEDs in micropython. If you have not followed our previous tutorial, you check here:

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

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

ESP32 ESP8266 MicroPython MQTT DS18B20 Project Overview

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

ESP32 and ESP8266 MQTT Publish and Subscribe DS18B20 Sensor Readings Project Overview
  1. An ESP32 or ESP8266 board connected with DS18B20 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 ESP32/ESP8266 board publishes the DS18B20 temperature readings in Celsius on the MQTT topic: esp/ds18b20/temperatureC and publishes the DS18B20 temperature readings in Fahrenheit on the MQTT topic: esp/ds18b20/temperatureF.
  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 DS18B20 Publish and Subscribe 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 DS18B20 Publisher

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

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

A waterproof version of this sensor is also available in the market. The following figure show the pinout of the DS18B20 sensors.

ds18b20 waterproof pinout diagram
Pin out of waterproof DS18B20

The following diagram shows the pinout of normal DS18B20 temperature sensor.

ds18b20 pinout diagram

The table below lists the pin configurations:

PinDescription
VCCThis is the pin that powers up the sensor. 3.3V for ESP32/ESP8266 board.
DataThis pin gives the temperature value
GroundThis pin is connected with the ground

Interfacing DS18B20 sensor with ESP32/ESP8266

ESP32 with DS18B20 sensor

We will now learn how to connect the temperature sensor with ESP32/ESP8266 board. We will need the following components.

Required Components

  1. ESP32 or ESP8266 board
  2. DS18B20  sensor
  3. 4.7k ohm resistor
  4. Breadboard
  5. Connecting Wires

DS18B20 Parasite vs Normal Mode

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.

ESP32 Schematic Diagram

As you can see in the schematic diagram below, we have used DS10B20 in normal mode and powered the sensor with its Vcc pin from 3.3V pin of ESP32 board. Connect ESP32 device with DS18B20 as shown in the schematic diagram below:

ds18b20 ESP32 schematic diagram

ESP8266 Schematic Diagram

If you are using ESP8266 NodeMCU for this project, connect ESP8266 device with DS18B20 as shown in the schematic diagram below:

Interfacing DS18B20 with ESP8266 NodeMCU

As you can see above, we have powered the sensor using the normal mode. The DS18B20 sensor has three terminals which we saw above in the pinout. The first terminal is grounded with the ESP32 /ESP8266 board. The data line of the sensor, which is the middle terminal, is connected through GPIO14 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 or ESP8266 module. You can refer to these posts to know more about ESP boards GPIO pins:

Install MQTT and DS18B20 Libraries for 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 here save it 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.

For this project we will require two libraries: 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 ESP 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)

Now we are ready to program our ESP board with DS18B20 sensor for MQTT protocol in MicroPython.

ESP32 ESP8266 MQTT Publisher DS18B20 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
from machine import Pin
import micropython
import network
import esp
import onewire, ds18x20

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_C = b'esp/ds18b20/temperatureC'
TOPIC_PUB_TEMP_F = b'esp/ds18b20/temperatureF'

last_message = 0
message_interval = 5

ds_pin = Pin(14)
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, onewire, ds18x20 and Pin class from the machine module.

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

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

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

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

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/ESP8266 board will publish to. The temperature readings in Celsius will be published to esp/ds18b20/temperatureC. The temperature readings in Fahrenheit will be published to esp/ds18b20/temperatureF.

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

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 read_ds_sensor():
 try:
  roms = ds_sensor.scan()
  print('Found DS devices: ', roms)
  ds_sensor.convert_temp()
  for rom in roms:
    temp = ds_sensor.read_temp(rom)
    if isinstance(temp, float):
      msg = round(temp, 2)
      print(temp, end=' ')
      print('Valid temperature')
      return msg
  return b'0.0'
 except OSError as e:
     return('Failed to read sensor.')

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:
      tempC = read_ds_sensor()
      tempF = round(tempC * (9/5) + 32.0, 2)
      print('Temperature(°C): %s' %tempC, 'Temperature(°F): %s' %tempF)
      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?

Lets understand how the main.py file works.

read_ds_sensor()

First, we will define a read_ds_sensor() function which scans and reads 2 bytes of temperature value from the number of DS18B20 sensors connected to the GPIO pin. We have created an object ds_sensor object on the GPIO14 pin in the boot.py file. Additionally, we can use the same GPIO pin to read temperature values from multiple temperature sensors.

def read_ds_sensor():
 try:
  roms = ds_sensor.scan()
  print('Found DS devices: ', roms)
  ds_sensor.convert_temp()
  for rom in roms:
    temp = ds_sensor.read_temp(rom)
    if isinstance(temp, float):
      msg = round(temp, 2)
      print(temp, end=' ')
      print('Valid temperature')
      return msg
  return b'0.0'
 except OSError as e:
     return('Failed to read sensor.')

The scan() method scans all DS18B20 sensors connected to a ds_sensor pin and saves the 64-bit address of each sensor in a list variable that is “roms”. Later, we will use these addresses to read temperature from the sensor one by one. 

roms = ds_sensor.scan()

The print() function prints the unique addresses of each temperature sensor on MicroPython shell console:

print('Found DS devices: ', roms)

After that call an object on covert_temp() method before reading a temperature from a sensor using its unique address. 

ds_sensor.convert_temp()

To read a temperature, use read_temp() procedure on ds_sensor object and pass an address which is stored in roms list.

temp = ds_sensor.read_temp(rom)

The DS18B20 temperature sensor provides data output in float data type. We should validate the readings by checking sensor output using isinstance() method. The isinstance() routine takes a variable and its type as an argument. If both arguments have the same data type, it will return True. Otherwise, it returns False.

if isinstance(temp, float):

If the output of the sensor is validated, the round() method will round off the humidity reading up to 2 decimal points and return the message. 

msg = round(temp, 2)
print(temp, end=' ')
print('Valid temperature')     

 In this case, there will be an address of only one sensor in the roms list. Because we are using only one DS18B20 sensor. Therefore, we print the temperature on the shell console and return to the execution where this function was called. 

return msg
return b'0.0'

Connect with MQTT

Next, 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 DS18B20 sensor data and publishing it at the specific topics. After every 5 seconds, the ESP32/ESP8266 obtains the temperature reading in Celsius from the read_ds_sensor() and saves it in the variable ‘tempC’. To obtain the reading in Fahrenheit, we do a mathematical conversion on the tempC value and save the value in the variable ‘tempF’ after rounding it off to 2 decimal places.

The temperature reading in Celsius is published to the topic esp/ds18b20/temperatureC. The temperature reading in Fahrenheit is published to the topic esp/ds18b20/temperatureF.

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 DS18B20 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:
      tempC = read_ds_sensor()
      tempF = round(tempC * (9/5) + 32.0, 2)
      print('Temperature(°C): %s' %tempC, 'Temperature(°F): %s' %tempF)
      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()

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.

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 in Celsius to the esp/ds18b20/temperatureC topic and the temperature reading in Fahrenheit to the esp/ds18b20/temperatureF topic.

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

Setting up Node-Red Dashboard as an MQTT DS18B20 Subscriber

Now let us build the Node-Red Dashboard to view the DS18B20 temperature readings in an interactive manner. Our ESP32/ESP8266 publisher is continuously sending temperature readings to 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 ‘DS18B20 Readings.’

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

We will display both the temperature 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 DS18B20 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/ds18b20/temperatureC.’ Click the Done button.

ESP32 and ESP8266 MQTT DS18B20 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/ds18b20/temperatureF.’ Click the Done button.

ESP32 and ESP8266 MQTT DS18B20 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 in Celsius and the second one for temperature in Fahrenheit.

Finally, join the nodes as shown below and click the Deploy button found at the top. The status of the MQTT nodes changes to connected.

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

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 DS18B20 sensor.

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

You may also like to read:

Leave a Comment