ESP32 MQTT – Publish and Subscribe with Arduino IDE

This project shows how to use MQTT communication protocol with the ESP32 to publish messages and subscribe to topics. As an example, we’ll publish BME280 sensor readings to the Node-RED Dashboard, and control an ESP32 output. The ESP32 we’ll be programmed using Arduino IDE.

Project Overview

In this example, there’s a Node-RED application that controls ESP32 outputs and receives sensor readings from the ESP32 using MQTT communication protocol. The Node-RED application is running on a Raspberry Pi.

We’ll use the Mosquitto broker installed on the same Raspberry Pi. The broker is responsible for receiving all messages, filtering the messages, decide who is interested in them and publishing the messages to all subscribed clients.

The following figure shows an overview of what we’re going to do in this tutorial.

  • The Node-RED application publishes messages (“on” or “off“) in the topic esp32/output. The ESP32 is subscribed to that topic. So, it receives the message with “on” or “off” to turn the LED on or off.
  • The ESP32 publishes temperature on the esp32/temperature topic and the humidity on the esp32/humidity topic. The Node-RED application is subscribed to those topics. So, it receives temperature and humidity readings that can be displayed on a chart or gauge, for example.

Note: there’s also a similar tutorial on how to use the ESP8266 and Node-RED with MQTT.

Prerequisites

If you like home automation and you want to learn more about Node-RED, Raspberry Pi, ESP8266 and Arduino, we recommend trying our course: Build a Home Automation System with Node-RED, ESP8266 and Arduino. We also have a course dedicated to the ESP32: Enroll in Learn ESP32 with Arduino IDE course.

Parts Required

These are the parts required to build the circuit (click the links below to find the best price at Maker Advisor):

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Introducing the BME280 Sensor Module

The BME280 sensor module reads temperature, humidity, and pressure. Because pressure changes with altitude, you can also estimate altitude. However, in this tutorial we’ll just read temperature and humidity. There are several versions of this sensor module, but we’re using the one shown in the figure below.

The sensor can communicate using either SPI or I2C communication protocols (there are modules of this sensor that just communicate with I2C, these just come with four pins).

To use SPI communication protocol, use the following pins:

  • SCK – this is the SPI Clock pin
  • SDO – MISO
  • SDI – MOSI
  • CS – Chip Select

To use I2C communication protocol, the sensor uses the following pins:

  • SCK – SCL pin
  • SDI – SDA pin

Schematic

We’re going to use I2C communication with the BME280 sensor module. For that, wire the sensor to the ESP32 SDA and SCL pins, as shown in the following schematic diagram.

We’ll also control an ESP32 output, an LED connected to GPIO 4.

Here’s how your circuit should look:

Preparing the Arduino IDE

There’s an add-on for the Arduino IDE that allows you to program the ESP32 using the Arduino IDE and its programming language. Follow one of the next tutorials to prepare your Arduino IDE to work with the ESP32, if you haven’t already.

After making sure you have the ESP32 add-on installed, you can continue with this tutorial.

Installing the PubSubClient Library

The PubSubClient library provides a client for doing simple publish/subscribe messaging with a server that supports MQTT (basically allows your ESP32 to talk with Node-RED).

  1. Click here to download the PubSubClient library. You should have a .zip folder in your Downloads folder
  2. Unzip the .zip folder and you should get pubsubclient-master folder
  3. Rename your folder from pubsubclient-master to pubsubclient
  4. Move the pubsubclient folder to your Arduino IDE installation libraries folder
  5. Then, re-open your Arduino IDE

The library comes with a number of example sketches. See File >Examples > PubSubClient within the Arduino IDE software.

Important: PubSubClient is not fully compatible with the ESP32, but the example provided in this tutorial is working very reliably during our tests.

Installing the BME280 library

To take readings from the BME280 sensor module we’ll use the Adafruit_BME280 library. Follow the next steps to install the library in your Arduino IDE:

  1. Click here to download the Adafruit-BME280 library. You should have a .zip folder in your Downloads folder
  2. Unzip the .zip folder and you should get Adafruit-BME280-Library-master folder
  3. Rename your folder from Adafruit-BME280-Library-master to Adafruit_BME280_Library
  4. Move the Adafruit_BMPE280_Library folder to your Arduino IDE installation libraries folder
  5. Finally, re-open your Arduino IDE

Alternatively, you can go to Sketch > Include Library > Manage Libraries and type “adafruit bme280” to search for the library. Then, click install.

Installing the Adafruit_Sensor library

To use the BME280 library, you also need to install the Adafruit_Sensor library. Follow the next steps to install the library:

  1. Click here to download the Adafruit_Sensor library. You should have a .zip folder in your Downloads folder
  2. Unzip the .zip folder and you should get Adafruit_Sensor-master folder
  3. Rename your folder from Adafruit_Sensor-master to Adafruit_Sensor
  4. Move the Adafruit_Sensor folder to your Arduino IDE installation libraries folder
  5. Finally, re-open your Arduino IDE

Uploading code

Now, you can upload the following code to your ESP32. The code is commented on where you need to make changes. You need to edit the code with your own SSID, password and Raspberry Pi IP address.

/*********
  Rui Santos
  Complete project details at https://randomnerdtutorials.com  
*********/

#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Adafruit_Sensor.h>

// Replace the next variables with your SSID/Password combination
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Add your MQTT Broker IP address, example:
//const char* mqtt_server = "192.168.1.144";
const char* mqtt_server = "YOUR_MQTT_BROKER_IP_ADDRESS";

WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;

//uncomment the following lines if you're using SPI
/*#include <SPI.h>
#define BME_SCK 18
#define BME_MISO 19
#define BME_MOSI 23
#define BME_CS 5*/

Adafruit_BME280 bme; // I2C
//Adafruit_BME280 bme(BME_CS); // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI
float temperature = 0;
float humidity = 0;

// LED Pin
const int ledPin = 4;

void setup() {
  Serial.begin(115200);
  // default settings
  // (you can also pass in a Wire library object like &Wire2)
  //status = bme.begin();  
  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);

  pinMode(ledPin, OUTPUT);
}

void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;
  
  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();

  // Feel free to add more if statements to control more GPIOs with MQTT

  // If a message is received on the topic esp32/output, you check if the message is either "on" or "off". 
  // Changes the output state according to the message
  if (String(topic) == "esp32/output") {
    Serial.print("Changing output to ");
    if(messageTemp == "on"){
      Serial.println("on");
      digitalWrite(ledPin, HIGH);
    }
    else if(messageTemp == "off"){
      Serial.println("off");
      digitalWrite(ledPin, LOW);
    }
  }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP8266Client")) {
      Serial.println("connected");
      // Subscribe
      client.subscribe("esp32/output");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}
void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  long now = millis();
  if (now - lastMsg > 5000) {
    lastMsg = now;
    
    // Temperature in Celsius
    temperature = bme.readTemperature();   
    // Uncomment the next line to set temperature in Fahrenheit 
    // (and comment the previous temperature line)
    //temperature = 1.8 * bme.readTemperature() + 32; // Temperature in Fahrenheit
    
    // Convert the value to a char array
    char tempString[8];
    dtostrf(temperature, 1, 2, tempString);
    Serial.print("Temperature: ");
    Serial.println(tempString);
    client.publish("esp32/temperature", tempString);

    humidity = bme.readHumidity();
    
    // Convert the value to a char array
    char humString[8];
    dtostrf(humidity, 1, 2, humString);
    Serial.print("Humidity: ");
    Serial.println(humString);
    client.publish("esp32/humidity", humString);
  }
}

View raw code

This code publishes temperature and humidity readings on the esp32/temperature and esp32/humidity topics trough MQTT protocol.

The ESP32 is subscribed to the esp32/output topic to receive the messages published on that topic by the Node-RED application. Then, accordingly to the received message, it turns the LED on or off.

Subscribing to MQTT topics

In the reconnect() function, you can subscribe to MQTT topics. In this case, the ESP32 is only subscribed to the esp32/output:

client.subscribe("esp32/output");

In the callback() function, the ESP32 receives the MQTT messages of the subscribed topics. According to the MQTT topic and message, it turns the LED on or off:

// If a message is received on the topic esp32/output, you check if the message is either "on" or "off". 
// Changes the output state according to the message
if (String(topic) == "esp32/output") {
  Serial.print("Changing output to ");
  if (messageTemp == "on") {
    Serial.println("on");
    digitalWrite(ledPin, HIGH);
  }
  else if (messageTemp == "off") {
    Serial.println("off");
    digitalWrite(ledPin, LOW);
  }
}

Publishing MQTT messages

In the loop(), new readings are being published every 5 seconds:

if (now - lastMsg > 5000) { ... }

By default the ESP32 is sending the temperature in Celsius, but you can uncomment the last line to send the temperature in Fahrenheit:

// Temperature in Celsius
temperature = bme.readTemperature(); 
// Uncomment the next line to set temperature in Fahrenheit 
// (and comment the previous temperature line)
//temperature = 1.8 * bme.readTemperature() + 32; // Temperature in Fahrenheit

You need to convert the temperature float variable to a char array, so that you can publish the temperature reading in the esp32/temperature topic:

// Convert the value to a char array
char tempString[8];
dtostrf(temperature, 1, 2, tempString);
Serial.print("Temperature: ");
Serial.println(tempString);
client.publish("esp32/temperature", tempString);

The same process is repeated to publish the humidity reading in the esp32/humidity topic:

humidity = bme.readHumidity();
// Convert the value to a char array
char humString[8];
dtostrf(humidity, 1, 2, humString);
Serial.print("Humidity: ");
Serial.println(humString);
client.publish("esp32/humidity", humString);

Creating the Node-RED flow

Before creating the flow, you need to have installed in your Raspberry Pi:

After that, import the Node-RED flow provided. Go to the GitHub repository or click the figure below to see the raw file, and copy the code provided.

Next, in the Node-RED window, at the top right corner, select the menu, and go to Import  > Clipboard.

Then, paste the code provided and click Import.

The following nodes should load:

After making any changes, click the Deploy button to save all the changes.

Node-RED UI

Now, your Node-RED application is ready. To access Node-RED UI and see how your application looks, access any browser in your local network and type:

http://Your_RPi_IP_address:1880/ui

Your application should look as shown in the following figure. You can control the LED on and off with the switch or you can view temperature readings in a chart and the humidity values in a gauge.

Demonstration

Watch the next video for a live demonstration:

Open the Arduino IDE serial monitor to take a look at the MQTT messages being received and published.

Wrapping Up

In summary, we’ve shown you the basic concepts that allow you to turn on lights and monitor sensors with your ESP32 using Node-RED and the MQTT communication protocol. You can use this example to integrate in your own home automation system, control more outputs, or monitor other sensors.

You might also like reading:

We hope you’ve found this tutorial useful.

Thanks for reading.


Learn how to program and build projects with the ESP32 and ESP8266 using MicroPython firmware DOWNLOAD »

Learn how to program and build projects with the ESP32 and ESP8266 using MicroPython firmware DOWNLOAD »


Enjoyed this project? Stay updated by subscribing our weekly newsletter!

28 thoughts on “ESP32 MQTT – Publish and Subscribe with Arduino IDE”

  1. After entering Node-RED code the the graph and gauge node show up as unknown, is there some other “Libraries” that have to be installed?

  2. Great guide as always!!

    One question though, where is the sensor data stored?

    I assume on the RPi somewhere – maybe in the node.red folder?

    Many thanks!

  3. Many thanks – so no data is ‘stored’ for later use, its only kept in the current deployed Node Red instance for the duration of the charts settings e.g. 1 hour?

    Simon

  4. Hi Rui & Sara,

    I’ve been following your blog since long time, Here I would like to suggest one thing, If you add “search box” in your website, it’ll be great. Searching for coarses, tutorials will become more easy.
    then.
    Here in this post I replicate setup you given with multiple “esp32’s” at my desk, but I encountered strange problem -> one of the ESP32 reboots again and again each time it tries to publish. But I figure out the what was the problem with help of your tutorial (for esp8266) solution – In case of MQTT multiple connections we have to assign different device ID’s to each ESP.

    Thank you for all your tutorials, very useful to hobbyist like me. I always recommends your blog and Coarses to my friends.

    Thanks and Regards

    • Hi.
      Thank you so much for supporting our work.
      Indeed a “search box” is something that will be very useful. However, adding a search box that actually works well, it’s not as straightforward as it seems. But, yes, we want to improve and we’re working on it. We have lots of tutorials that sometimes are not easy to “find”. Thank you for your suggestion.
      I’m glad you got your MQTTT project working. Now, with an ID for each ESP32, is everything working fine?
      Regards,
      Sara

  5. Hi Rui,
    great work and thanks for your great video courses.

    I try to adapt your code to my private mqtt project and add the following after lines:
    const char* mqtt_server = “192.168.2.122”;
    const char* mqttUser = “quito”;
    const char* mqttPassword = “XXXXXXXX“;

    WLAN connect ok, but no connection to the mqtt server is possible.

    How can i fix this?

    Best Regards
    Guido

  6. Hello my friend.
    What is the best library for used MQTT on ESP32?

    Pubsubclient or Async?

    In the your book ESP32 you used Async.

    Best regards.
    Thank you for your tutorial

  7. HI, I followed your instructions and got to the point where I got a error message on the Node Red when i was trying to deploy the nodes. Trouble shooting brought me to a point where I don’t get any feedback from the ESP32 in the Serial monitor. any further advice?
    the sketch is uploading OK, no error messages.

  8. Hie I wanted to do plant monitoring with IoT.I just started following your website, up to WiFi scanning it is working perfectly fine after that after that when I try compiling your code I am getting two errors.one main error is….

    Multiple libraries were found for “WiFi.h”
    Used: C:\Users\admin\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.1\libraries\WiFi
    compilation terminated.

    Not used: C:\Program Files (x86)\Arduino\libraries\WiFi
    exit status 1

    other error is related to DHT sensors library which is from other post of yours where DHT.h file is missing in that folder..

    Please help me I am struggling from two days……

  9. I did all the things accordingly but still showing this this message again and again. Please help me, please see my code below.

    Attempting MQTT connection…failed, rc=5 try again in 5 seconds

    /*********
    Rui Santos
    Complete project details at https://randomnerdtutorials.com
    *********/

    #include
    #include
    #include
    #include
    #include

    // Replace the next variables with your SSID/Password combination
    const char* ssid = “”;
    const char* password = “”;

    // Add your MQTT Broker IP address, example:
    //const char* mqtt_server = “192.168.1.144”;
    const char* mqtt_server = “192.168.1.101”;

    WiFiClient espClient;
    PubSubClient client(espClient);
    long lastMsg = 0;
    char msg[50];
    int value = 0;

    //uncomment the following lines if you’re using SPI
    /*#include
    #define BME_SCK 18
    #define BME_MISO 19
    #define BME_MOSI 23
    #define BME_CS 5*/

    Adafruit_BME280 bme; // I2C
    //Adafruit_BME280 bme(BME_CS); // hardware SPI
    //Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI
    float temperature = 0;
    float humidity = 0;

    // LED Pin
    const int ledPin = 5;

    void setup() {
    Serial.begin(115200);
    // default settings
    // (you can also pass in a Wire library object like &Wire2)
    //status = bme.begin();
    if (!bme.begin(0x76)) {
    Serial.println(“Could not find a valid BME280 sensor, check wiring!”);
    while (1);
    }
    setup_wifi();
    client.setServer(mqtt_server, 1883);
    client.setCallback(callback);

    pinMode(ledPin, OUTPUT);
    }

    void setup_wifi() {
    delay(10);
    // We start by connecting to a WiFi network
    Serial.println();
    Serial.print(“Connecting to “);
    Serial.println(ssid);

    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(“.”);
    }

    Serial.println(“”);
    Serial.println(“WiFi connected”);
    Serial.println(“IP address: “);
    Serial.println(WiFi.localIP());
    }

    void callback(char* topic, byte* message, unsigned int length) {
    Serial.print(“Message arrived on topic: “);
    Serial.print(topic);
    Serial.print(“. Message: “);
    String messageTemp;

    for (int i = 0; i 5000) {
    lastMsg = now;

    // Temperature in Celsius
    temperature = bme.readTemperature();
    // Uncomment the next line to set temperature in Fahrenheit
    // (and comment the previous temperature line)
    //temperature = 1.8 * bme.readTemperature() + 32; // Temperature in Fahrenheit

    // Convert the value to a char array
    char tempString[8];
    dtostrf(temperature, 1, 2, tempString);
    Serial.print(“Temperature: “);
    Serial.println(tempString);
    client.publish(“esp32/temperature”, tempString);

    humidity = bme.readHumidity();

    // Convert the value to a char array
    char humString[8];
    dtostrf(humidity, 1, 2, humString);
    Serial.print(“Humidity: “);
    Serial.println(humString);
    client.publish(“esp32/humidity”, humString);
    }
    }

  10. Hi,

    In your code that you provided, at the top section, you have these 2 lines
    —————————-
    WiFiClient espClient;
    PubSubClient client(espClient);
    ——————————-

    under the reconnect () section…..
    —————————-
    void reconnect() {
    // Loop until we’re reconnected
    while (!client.connected()) {
    Serial.print(“Attempting MQTT connection…”);
    // Attempt to connect
    if (client.connect(“ESP8266Client”)) {
    Serial.println(“connected”);
    —————————

    What is this “ESP8266Client” ? Is this an error ?

    Regards,
    Ong Kheok Chin

  11. Hi!
    Great doc!

    Using PlatformIO I did try this code, but when uploading to Arduino I get a warning and that warning is causing the same issue as ONG KHEOK CHIN.

    Probably related.
    Error is the following:

    ————————————————————————————————–
    test.cpp: In function ‘void callback(char*, byte*, unsigned int)’:
    test.cpp:274:23: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    for (int i = 0; i < length; i++) {
    ^
    ————————————————————————————————–

    There seems to be a problem with the FOR condition…

    Any ideas?
    Thx alot!

  12. Hi Rui/Sara,

    I followed your tutorial for “ESP32 with Multiple DS18B20 Temperature Sensors” which is exactly what i need for a project for the factory that i work in. I managed to alter it slightly to display 5 sensors. This is working well.

    The second thing that I wanted to do is display these readings on a webpage, and thirdly i wanted to store the readings in a database.

    So for the second part of my project I followed the link from the first tutorial, which took me to this tutorial “ESP32 MQTT – Publish and Subscribe with Arduino IDE”.

    Unfortunately, you have now jumped to using a different sensor (the BME280) and you are only using one sensor.

    I am really struggling to make the jump from one to the other and i don’t think that learning about the BME280 is really going to help.
    I looked at the forum to see if anyone else has asked a similar question. There was one person, but he seems to be following a python tutorial.

    Do you have a link to any other tutorial which links the “ESP32 with Multiple DS18B20 Temperature Sensors” to allow me to Publish and Subscribe the data to MQTT?

Leave a Comment

Download our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.