In this ESP32 tutorial, we will learn to use ESP32 MQTT as a Publisher and Subscriber using Arduino IDE. There will be one ESP32 MQTT publisher and two subscribers. We will publish BME280 sensor readings to MQTT with ESP32 and one ESP32 will act as a subscriber and Node-Red Dashboard will also subscribe to the MQTT topics and display sensor readings on the Dashboard.
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 ESP32 and Node-RED will make connections with the MQTT broker installed on Raspberry Pi. After that, One ESP32 will publish sensor data to the Node-Red dashboard, and to the ESP32 subscriber on specified topics. Any appropriate sensor can be used but for this article, we will use a BME280 sensor which is used to measure ambient temperature, barometric pressure, and relative humidity.
ESP32 MQTT BME280 Project Overview
The diagram below illustrates the process that we will follow in our ESP32 MQTT project.
- An ESP32 board connected with a BME280 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.
- This ESP32 board publishes the BME280 temperature readings on the topic: esp32/temperature. It publishes the BME280 humidity readings on the topic: esp32/humidity. Likewise, it publishes the BME280 pressure readings on the topic: esp32/pressure.
- We have Node-Red and an another ESP32 as subscribers to these two topics. Node-Red receives the sensor data and displays them in an interactive manner in its dashboard. Whereas, the subscriber ESP32 board, is connected with an OLED and hence displays the readings on the display when it receives them.
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.
For a detailed tutorial regarding MQTT, its main components, MQTT broker and working follow the link: What is MQTT and How MQTT Works?
ESP32 as an MQTT BME280 Publisher
Our ESP32 MQTT Publisher is connected with a BME280 sensor.
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.
I2C means Inter-Integrated Circuit and works on the principle of the synchronous, multi-master multi-slave system. With BME280 and the ESP boards, the ESP32 acts as a master, and the BME280 sensor as a slave because it is an external device, acts as a slave. The ESP development boards communicate with the BME280 sensor through the I2C protocol to get temperature, barometric pressure, and relative humidity.
The figure below shows the BME280 sensor and its pinout.
- VCC: connected with 3.3V
- SCL: used to generate the clock signal
- SDA: used in sending and receiving data
Interfacing BME280 sensor with ESP32
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.
We will need the following components to connect our ESP32 board with the BME280 sensor.
- ESP32 board
- BME280 Sensor
- Connecting Wires
- Breadboard
Follow the schematic diagram below for the ESP32 module and connect them accordingly.
Install MQTT and BME280 Libraries
Before we proceed further, you should make sure that you have the latest version of Arduino IDE installed on your system. Moreover, you should also install an ESP32 add-on in Arduino IDE. If your IDE does not have the plugin installed you can visit the link below:
Installing ESP32 library in Arduino IDE and upload code
For this project, we will have to install libraries for the BME280 sensor and MQTT.
Installing BME280 Arduino Library
As we are connecting the BME280 sensor with ESP32 so we will have to install BME280 libraries to our module. We will require two libraries for this project:
We will use the Library Manager in our Arduino IDE to install the latest versions of the libraries. Open your Arduino IDE and go to Sketch > Include Libraries > Manage Libraries. Type Adafruit BME280 library name in the search bar and install them both.
Open your Arduino IDE and go to Sketch > Include Libraries > Manage Libraries. Type Adafruit unified sensor library name in the search bar and install it.
After installation of the libraries, restart your IDE.
Install Async MQTT Client Library and Async TCP Library
We will use Async MQTT Client Library by Marvin Roger to use MQTT with ESP32. AsyncTCP is another library that we will be incorporating as it is required for our ESP32 MQT project. Both of these libraries are not available in the Arduino library manager. Therefore, we will have to download and install them on our ESP32 board ourselves.
- To install Async MQTT Client library, click here to download. You will download the library as a .zip folder which you will extract and rename as ‘ESPAsyncWebServer.’ Then, transfer this folder to the library folder in your Arduino IDE.
- To install the Async TCP library, click here to download. You will download the library as a .zip folder which you will extract and rename as ‘AsyncTCP’. Then, copy the ‘AsyncTCP’ folder to the library folder in your Arduino IDE.
Similarly, you can also go to Sketch > Include Library > Add .zip Library inside the IDE to add the libraries. After installation of the libraries, restart your IDE.
ESP32 MQTT BME280 Publisher Arduino Sketch
Open your Arduino IDE and go to File > New to open a new file. Copy the code given below in that file and save it. You need to enter your network credentials and your Raspberry Pi IP address. This sketch will develop an ESP32 MQTT BME280 publisher by following the steps given below:
- Connecting the ESP32 board with the local network
- Setting up the BME280 sensor
- Connecting the ESP32 to the MQTT broker
- Publishing the sensor readings to the MQTT topics
#include <WiFi.h>
extern "C" {
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
}
#include <AsyncMqttClient.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
Adafruit_BME280 bme;
//replace with your network credentials
#define WIFI_SSID "YOUR_SSID"
#define WIFI_PASSWORD "YOUR_PASSWORD"
// Raspberry Pi Mosquitto MQTT Broker
#define MQTT_HOST IPAddress(192, 168, 1, XXX)
#define MQTT_PORT 1883
//MQTT Topics
#define MQTT_PUB_TEMP "esp32/temperature"
#define MQTT_PUB_HUM "esp32/humidity"
#define MQTT_PUB_PRES "esp32/pressure"
float temperature, humidity, pressure;
AsyncMqttClient mqttClient;
TimerHandle_t mqttReconnectTimer;
TimerHandle_t wifiReconnectTimer;
unsigned long previousMillis = 0;
const long interval = 5000;
void connectToWifi() {
Serial.println("Connecting to Wi-Fi...");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}
void connectToMqtt() {
Serial.println("Connecting to MQTT...");
mqttClient.connect();
}
void WiFiEvent(WiFiEvent_t event) {
Serial.printf("[WiFi-event] event: %d\n", event);
switch(event) {
case SYSTEM_EVENT_STA_GOT_IP:
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
connectToMqtt();
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
Serial.println("WiFi lost connection");
xTimerStop(mqttReconnectTimer, 0);
xTimerStart(wifiReconnectTimer, 0);
break;
}
}
void onMqttConnect(bool sessionPresent) {
Serial.println("Connected to MQTT.");
Serial.print("Session present: ");
Serial.println(sessionPresent);
}
void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
Serial.println("Disconnected from MQTT.");
if (WiFi.isConnected()) {
xTimerStart(mqttReconnectTimer, 0);
}
}
void onMqttPublish(uint16_t packetId) {
Serial.print("Publish acknowledged.");
Serial.print(" packetId: ");
Serial.println(packetId);
}
void setup() {
Serial.begin(115200);
Serial.println();
if (!bme.begin(0x76)) {
Serial.println("Could not detect a BME280 sensor, Fix wiring connections!");
while (1);
}
mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToMqtt));
wifiReconnectTimer = xTimerCreate("wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToWifi));
WiFi.onEvent(WiFiEvent);
mqttClient.onConnect(onMqttConnect);
mqttClient.onDisconnect(onMqttDisconnect);
mqttClient.onPublish(onMqttPublish);
mqttClient.setServer(MQTT_HOST, MQTT_PORT);
connectToWifi();
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
temperature = bme.readTemperature();
humidity = bme.readHumidity();
pressure = bme.readPressure() / 100.0F;
// Publish an MQTT message on topic esp32/temperature
uint16_t packetIdPub1 = mqttClient.publish(MQTT_PUB_TEMP, 1, true, String(temperature).c_str());
Serial.printf("Publishing on topic %s at QoS 1, packetId: %i", MQTT_PUB_TEMP, packetIdPub1);
Serial.printf("Message: %.2f \n", temperature);
// Publish an MQTT message on topic esp32/humidity
uint16_t packetIdPub2 = mqttClient.publish(MQTT_PUB_HUM, 1, true, String(humidity).c_str());
Serial.printf("Publishing on topic %s at QoS 1, packetId %i: ", MQTT_PUB_HUM, packetIdPub2);
Serial.printf("Message: %.2f \n", humidity);
// Publish an MQTT message on topic esp32/pressure
uint16_t packetIdPub3 = mqttClient.publish(MQTT_PUB_PRES, 1, true, String(pressure).c_str());
Serial.printf("Publishing on topic %s at QoS 1, packetId: %i", MQTT_PUB_PRES, packetIdPub3);
Serial.printf("Message: %.2f \n", pressure);
}
}
How does Code Works?
We will start off by including the necessary libraries for our project. These include the libraries that we previously installed for the sensor as well as the Wi-Fi library as the ESP32 connects with the internet and FreeRTOS libraries to generate timers.
#include <WiFi.h>
extern "C" {
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
}
#include <AsyncMqttClient.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
Then, define the Adafruit_BME280 object named bme by setting it on the default I2C GPIO pins of ESP32.
Adafruit_BME280 bme;
Next, define your network credentials in the WIFI_SSID and WIFI_PASSWORD variables. The ESP32 will connect to this network.
//replace with your network credentials
#define WIFI_SSID "YOUR_SSID"
#define WIFI_PASSWORD "YOUR_PASSWORD"
Define your Raspberry Pi IP address. This will be used by ESP32 to connect to the Mosquitto Broker.
#define MQTT_HOST IPAddress(192, 168, 1, XXX) //specify your Raspberry Pi IP Address
Also, specify the MQTT port which is 1883 (default).
#define MQTT_PORT 1883
Next we will define three topics which the ESP32 board will publish to. The temperature readings will be published to esp32/temperature. The humidity readings will be published to esp32/humidity. Likewise, the pressure readings will be published to esp32/pressure.
//MQTT Topics
#define MQTT_PUB_TEMP "esp32/temperature"
#define MQTT_PUB_HUM "esp32/humidity"
#define MQTT_PUB_PRES "esp32/pressure"
Also create three float variables called temperature, humidity and pressure to store the sensor readings acquired from BME280.
float temperature, humidity, pressure;
We will obtain the sensor readings after every 5 seconds. The variables below will monitor the time interval between the readings.
unsigned long previousMillis = 0;
const long interval = 5000;
To manage the MQTT client, we will create an AsyncMqttClient object named ‘mqttClient.’
AsyncMqttClient mqttClient;
Moreover, we will also be using FreeRTOS timers to reconnect to the Wi-Fi and the broker in case of disconnection. Hence, we will create two task handles, one for the MQTT and another for the Wi-Fi.
TimerHandle_t mqttReconnectTimer;
TimerHandle_t wifiReconnectTimer;
MQTT Functions
Next we have a series of MQTT callback functions that come with the library.
The connectToWifi() function is responsible for connecting the ESP32 board to our Wi-Fi network. The connectToMqtt() function is responsible for connecting the ESP32 board to the MQTT broker. The WiFiEvent() function is responsible for operating the Wi-Fi events. This includes printing the ESP32 IP address when it connects with the Wi-Fi and incase of disconnection, it starts the the Wi-Fi reconnection timer and stops the MQTT reconnection timer.
void connectToWifi() {
Serial.println("Connecting to Wi-Fi...");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}
void connectToMqtt() {
Serial.println("Connecting to MQTT...");
mqttClient.connect();
}
void WiFiEvent(WiFiEvent_t event) {
Serial.printf("[WiFi-event] event: %d\n", event);
switch(event) {
case SYSTEM_EVENT_STA_GOT_IP:
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
connectToMqtt();
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
Serial.println("WiFi lost connection");
xTimerStop(mqttReconnectTimer, 0);
xTimerStart(wifiReconnectTimer, 0);
break;
}
}
The onMqttConnect() function is called when the ESP32 successfully connects with the broker. On the other hand, the onMqttDisconect() function is called when the ESP32 disconnects from the broker and if it is connected with Wi-Fi then it starts the MQTT reconnection timer. Relevant messages are printed in the serial monitor in each case.
void onMqttConnect(bool sessionPresent) {
Serial.println("Connected to MQTT.");
Serial.print("Session present: ");
Serial.println(sessionPresent);
}
void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
Serial.println("Disconnected from MQTT.");
if (WiFi.isConnected()) {
xTimerStart(mqttReconnectTimer, 0);
}
}
The onMqttPublish() is responsible for printing the packet id in the serial monitor when the message is published to your topic.
void onMqttPublish(uint16_t packetId) {
Serial.print("Publish acknowledged.");
Serial.print(" packetId: ");
Serial.println(packetId);
}
setup()
Inside the setup() function, we start the serial communication at a baud rate of 115200 and then initialize the BME280 sensor.
Serial.begin(115200);
if (!bme.begin(0x76)) {
Serial.println("Could not detect a BME280 sensor, Fix wiring connections!");
while (1);
}
Next we create the timers for the MQTT and Wi-Fi reconnection.
mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToMqtt));
wifiReconnectTimer = xTimerCreate("wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToWifi));
Call the WiFiEvent() function, to display the Wi-Fi status in the serial monitor.
WiFi.onEvent(WiFiEvent);
Moreover, call the rest of the callback functions that we previously described as well. These include onConnect(), onDisconnect() and onPublish(). Additionally, set the MQTT server by calling the setServer() function on the AsyncMqttClient object. Specify the MQTT_HOST and MQTT_PORT as parameters inside it. Connect ESP32 to the Wi-Fi network by calling connectToWifi() function.
mqttClient.onConnect(onMqttConnect);
mqttClient.onDisconnect(onMqttDisconnect);
mqttClient.onPublish(onMqttPublish);
mqttClient.setServer(MQTT_HOST, MQTT_PORT);
connectToWifi();
loop()
Inside the loop() function, the ESP32 board first obtains the BME280 sensor readings after every 5 seconds and stores them in their respective variables. Temperature readings are acquired through bme.readTemperature(). Pressure and humidity readings are accessed using the bme.readPressure()/ 100.0F and bme.readHumidity() functions respectively.
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
temperature = bme.readTemperature();
humidity = bme.readHumidity();
pressure = bme.readPressure() / 100.0F;
Then it publishes humidity on the topic esp32/humidity, temperature on the topic esp32/temperature and pressure on the topic esp32/pressure. To publish an MQTT message, we use the publish() method on the AsyncMqttClient object. It takes in four arguments. The first argument is the MQTT topic. The second argument is the quality of service (QoS). It can take values of 0, 1 or 2 and is a mechanism to monitor the deliverance of the message. The third argument is the retain flag. Lastly, the fourth argument is the payload which we want to publish.
// Publish an MQTT message on topic esp32/temperature
uint16_t packetIdPub1 = mqttClient.publish(MQTT_PUB_TEMP, 1, true, String(temperature).c_str());
Serial.printf("Publishing on topic %s at QoS 1, packetId: %i", MQTT_PUB_TEMP, packetIdPub1);
Serial.printf("Message: %.2f \n", temperature);
// Publish an MQTT message on topic esp32/humidity
uint16_t packetIdPub2 = mqttClient.publish(MQTT_PUB_HUM, 1, true, String(humidity).c_str());
Serial.printf("Publishing on topic %s at QoS 1, packetId %i: ", MQTT_PUB_HUM, packetIdPub2);
Serial.printf("Message: %.2f \n", humidity);
// Publish an MQTT message on topic esp32/pressure
uint16_t packetIdPub3 = mqttClient.publish(MQTT_PUB_PRES, 1, true, String(pressure).c_str());
Serial.printf("Publishing on topic %s at QoS 1, packetId: %i", MQTT_PUB_PRES, packetIdPub3);
Serial.printf("Message: %.2f \n", pressure);
Demonstration
Before uploading the code to our ESP32 board we will choose the correct board and COM port. Go to Tools > Board and select ESP32 Dev Module.
Next, go to Tools > Port and select the appropriate port through which your board is connected.
Click on the upload button to upload the code into the ESP32 board. After you have uploaded your code to the board press its ENABLE button.
Open your Serial Monitor to view the progress of the project. You will be able to view if the ESP32 board has successfully connected with the Wi-Fi and the Broker.
Setting up Node-Red Dashboard as an MQTT BME280 Subscriber
Now let us build the Node-Red Dashboard to view the BME280 sensor readings in an interactive manner. Our ESP32 publisher is continuously sending temperature, humidity, and pressure 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: Install Node-RED on Raspberry Pi (32-bit and 64-bit RPI OS)
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.
Make sure the dashboard is already installed. Head over to the extreme right side and find dashboard.
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.
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 ‘BME280 Readings.’
We will display the temperature and humidity readings on gauges and the pressure readings on a line chart. Therefore we add six 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, one chart and two mqtt in nodes to the flow as shown below:
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/temperature.’ Click the Done button.
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/humidity.’ Click the Done button.
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/pressure.’ Click the Done button.
Now double click the gauges and edit their properties as well. Set the first one for temperature and the second one for humidity.
For the chart, edit the properties as shown below. The line chart will plot pressure readings.
Finally, join the nodes as shown below and click the Deploy button found at the top.
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 and a line chart with readings from the BME280 sensor.
ESP32 as an MQTT BME280 Subscriber
In the previous section, we showed you how to publish ESP32 sensor readings from BME280 to Node-Red using MQTT. Let us now set up an ESP32 board connected with an OLED as a subscriber to the esp32/temperature, esp32/humidity and esp32/pressure topics. When this ESP32 gets connected with the MQTT Broker, it will be able to access the temperature, humidity and pressure readings which we will then display on the OLED.
Connecting ESP32 with OLED
We will need the following components.
Required Components
- ESP32 board
- OLED
- Bread Board
- Jumper wires
The OLED display has 4 terminals which we will connect with the ESP32 board. As the OLED display requires an operating voltage in the range of 3.3-5V, we will connect the VCC terminal with 3.3V which will be in common with the ESP32 board. SCL of the display will be connected with the SCL pin of the module and the SDA of the display will be connected with the SDA of the module. By default, the I2C pin in ESP32 for SDA is GPIO21, and for SCL is GPIO22. The ground of both the devices will be held common.
The table below shows the terminals of the devices which should be connected together.
OLED Display | ESP32 |
---|---|
VCC | VCC=3.3V |
GND | GND |
SCL | GPIO22 |
SDA | GPIO21 |
The connections between the two devices can be seen below.
Installing SSD1306 OLED Library in Arduino IDE
To use the OLED display in our project, we have to install the Adafruit SSD1306 OLED library in Arduino IDE. Follow the steps below to successfully install it.
Open Arduino IDE and click on Sketch > Library > Manage Libraries. Type ‘SSD1306’ in the search tab and install the Adafruit SSD1306 OLED library.
We will also require the Adafruit GFX library which is a dependency for SSD1306. Type ‘Adafruit GFX’ in the search tab and install it as well.
You may like this in-depth guide on OLED interfacing with ESP32:
ESP32 MQTT BME280 Subscriber Arduino Sketch
Open your Arduino IDE and go to File > New to open a new file. Copy the code given below in that file and save it. You need to enter your network credentials and your Raspberry Pi IP address. This sketch will develop an ESP32 MQTT subscriber by following the steps given below:
- Connecting the ESP32 board with the local network
- Setting up the OLED
- Connecting the ESP32 to the MQTT broker
- Subscribing to the MQTT topics in order to receive them
#include <WiFi.h>
extern "C" {
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
}
#include <AsyncMqttClient.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
//replace with your network credentials
#define WIFI_SSID "YOUR_SSID"
#define WIFI_PASSWORD "YOUR_PASSWORD"
#define MQTT_HOST IPAddress(192, 168, 1, XXX) //specify your Raspberry Pi IP Address
#define MQTT_PORT 1883
//MQTT Topics
#define MQTT_SUB_TEMP "esp32/temperature"
#define MQTT_SUB_HUM "esp32/humidity"
#define MQTT_SUB_PRES "esp32/pressure"
AsyncMqttClient mqttClient;
TimerHandle_t mqttReconnectTimer;
TimerHandle_t wifiReconnectTimer;
void connectToWifi() {
Serial.println("Connecting to Wi-Fi...");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}
void connectToMqtt() {
Serial.println("Connecting to MQTT...");
mqttClient.connect();
}
void WiFiEvent(WiFiEvent_t event) {
Serial.printf("[WiFi-event] event: %d\n", event);
switch(event) {
case SYSTEM_EVENT_STA_GOT_IP:
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
connectToMqtt();
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
Serial.println("WiFi lost connection");
xTimerStop(mqttReconnectTimer, 0); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
xTimerStart(wifiReconnectTimer, 0);
break;
}
}
void onMqttConnect(bool sessionPresent) {
Serial.println("Connected to MQTT.");
Serial.print("Session present: ");
Serial.println(sessionPresent);
uint16_t packetIdSub = mqttClient.subscribe(MQTT_SUB_TEMP, 2);
Serial.print("Subscribing at QoS 2, packetId: ");
Serial.println(packetIdSub);
packetIdSub = mqttClient.subscribe(MQTT_SUB_HUM, 2);
Serial.print("Subscribing at QoS 2, packetId: ");
Serial.println(packetIdSub);
packetIdSub = mqttClient.subscribe(MQTT_SUB_PRES, 2);
Serial.print("Subscribing at QoS 2, packetId: ");
Serial.println(packetIdSub);
}
void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
Serial.println("Disconnected from MQTT.");
if (WiFi.isConnected()) {
xTimerStart(mqttReconnectTimer, 0);
}
}
void onMqttSubscribe(uint16_t packetId, uint8_t qos) {
Serial.println("Subscribe acknowledged.");
Serial.print(" packetId: ");
Serial.println(packetId);
Serial.print(" qos: ");
Serial.println(qos);
}
void onMqttUnsubscribe(uint16_t packetId) {
Serial.println("Unsubscribe acknowledged.");
Serial.print(" packetId: ");
Serial.println(packetId);
}
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
Serial.println("Publish received.");
Serial.print(" topic: ");
Serial.println(topic);
String messageTemp;
for (int i = 0; i < len; i++) {
Serial.print((char)payload[i]);
messageTemp += (char)payload[i];
}
if (String(topic) == "esp32/temperature"){
Serial.print("\n Temperature: ");
Serial.println(messageTemp);
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0,0);
display.print("Temp: ");
display.print(messageTemp);
display.print(" ");
display.setTextSize(0.5);
display.cp437(true);
display.write(167);
display.setTextSize(1);
display.print("C");
display.display();
}
if (String(topic) == "esp32/humidity"){
Serial.print("\n Humidity: ");
Serial.println(messageTemp);
display.setTextSize(1);
display.setCursor(0,20);
display.print("Hum: ");
display.print(messageTemp);
display.print(" %");
display.display();
}
if (String(topic) == "esp32/pressure"){
Serial.print("\n Pressure: ");
Serial.println(messageTemp);
display.setTextSize(1);
display.setCursor(0,40);
display.print("BP: ");
display.setTextSize(1);
display.print(messageTemp);
display.print(" hPa");
display.display();
}
}
void setup() {
Serial.begin(115200);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); //initialize OLED
//display.clearDisplay();
display.display();
display.setTextSize(1);
display.setTextColor(WHITE);
Serial.println();
mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToMqtt));
wifiReconnectTimer = xTimerCreate("wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToWifi));
WiFi.onEvent(WiFiEvent);
mqttClient.onConnect(onMqttConnect);
mqttClient.onDisconnect(onMqttDisconnect);
mqttClient.onSubscribe(onMqttSubscribe);
mqttClient.onUnsubscribe(onMqttUnsubscribe);
mqttClient.onMessage(onMqttMessage);
mqttClient.setServer(MQTT_HOST, MQTT_PORT);
connectToWifi();
}
void loop(){
}
How does Code Works?
Most of the code is similar to the previous code which was used to publish to topics via MQTT. In this one we will be subscribing to those topics and display the messages on the OLED connected with the ESP32 board.
We will start off by including the necessary libraries. This includes the MQTT and FreeRTPS libraries that we included previously to set up the ESP32 MQTT Client as well as the OLED libraries that we just installed.
#include <WiFi.h>
extern "C" {
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
}
#include <AsyncMqttClient.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
Define OLED Parameters
We will define the width and height of our OLED display in pixels. We are using a 128×64 display hence the width will be 128 and the height will be 64.
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
Next, we will initialize the display by creating an object of Adafruit_SSD1306 and specifying the width, height, I2C instance (&Wire), and -1 as parameters inside it.’ -1′ specifies that the OLED display which we are using does not have a RESET pin. If you are using the RESET pin then specify the GPIO through which you are connecting it with your development board.
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
Next, define your network credentials in the WIFI_SSID and WIFI_PASSWORD variables. The ESP32 will connect to this network.
//replace with your network credentials
#define WIFI_SSID "YOUR_SSID"
#define WIFI_PASSWORD "YOUR_PASSWORD"
Define your Raspberry Pi IP address. This will be used by ESP32 to connect to the Mosquitto Broker.
#define MQTT_HOST IPAddress(192, 168, 1, XXX) //specify your Raspberry Pi IP Address
Also, specify the MQTT port which is 1883 (default).
#define MQTT_PORT 1883
Next, we will define three topics which the ESP32 board will subscribe to. The temperature readings were published to esp32/temperature, the humidity readings were published to esp32/humidity and the pressure readings were published to esp32/pressure. Hence, this ESP32 subscribes to these topics in order to acquire the sensor data.
//MQTT Topics
#define MQTT_SUB_TEMP "esp32/temperature"
#define MQTT_SUB_HUM "esp32/humidity"
#define MQTT_SUB_PRES "esp32/pressure"
To manage the MQTT client, we will create an AsyncMqttClient object named ‘mqttClient.’
AsyncMqttClient mqttClient;
Moreover, we will also be using FreeRTOS timers to reconnect to the Wi-Fi and the broker in case of disconnection. Hence, we will create two task handles, one for the MQTT and another for the Wi-Fi.
TimerHandle_t mqttReconnectTimer;
TimerHandle_t wifiReconnectTimer;
MQTT Functions
Next we have a series of MQTT callback functions that come with the library.
The connectToWifi() function is responsible for connecting the ESP32 board to our Wi-Fi network. The connectToMqtt() function is responsible for connecting the ESP32 board to the MQTT broker. The WiFiEvent() function is responsible for operating the Wi-Fi events. This includes printing the ESP32 IP address when it connects with the Wi-Fi and incase of disconnection, it starts the Wi-Fi reconnection timer and stops the MQTT reconnection timer.
void connectToWifi() {
Serial.println("Connecting to Wi-Fi...");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}
void connectToMqtt() {
Serial.println("Connecting to MQTT...");
mqttClient.connect();
}
void WiFiEvent(WiFiEvent_t event) {
Serial.printf("[WiFi-event] event: %d\n", event);
switch(event) {
case SYSTEM_EVENT_STA_GOT_IP:
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
connectToMqtt();
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
Serial.println("WiFi lost connection");
xTimerStop(mqttReconnectTimer, 0);
xTimerStart(wifiReconnectTimer, 0);
break;
}
}
Subscribing to Topics
The onMqttConnect() function is called when the ESP32 successfully connects with the broker. Then it subscribes to the topic using the subscribe() method on the AsyncMqttClient object. It takes in two parameters. The first parameter is the topic which is to be subscribed to and the second parameter is the QoS. This returns the packet id which is displayed in the serial monitor.
On the other hand, the onMqttDisconect() function is called when the ESP32 disconnects from the broker. If it is connected with Wi-Fi then it starts the MQTT reconnection timer. Relevant messages are printed in the serial monitor in each case.
void onMqttConnect(bool sessionPresent) {
Serial.println("Connected to MQTT.");
Serial.print("Session present: ");
Serial.println(sessionPresent);
uint16_t packetIdSub = mqttClient.subscribe(MQTT_SUB_TEMP, 2);
Serial.print("Subscribing at QoS 2, packetId: ");
Serial.println(packetIdSub);
packetIdSub = mqttClient.subscribe(MQTT_SUB_HUM, 2);
Serial.print("Subscribing at QoS 2, packetId: ");
Serial.println(packetIdSub);
packetIdSub = mqttClient.subscribe(MQTT_SUB_PRES, 2);
Serial.print("Subscribing at QoS 2, packetId: ");
Serial.println(packetIdSub);
}
void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
Serial.println("Disconnected from MQTT.");
if (WiFi.isConnected()) {
xTimerStart(mqttReconnectTimer, 0);
}
}
The onMqttSubscribe() prints the packet id and the QoS in the serial monitor, along with ‘Subscribe acknowledged.’ The onMqttUnsubscribe() prints the packet id along with ‘Unsubscribe acknowledged.’ These functions are called in the case of a successful subscription or un-subscription of a topic respectively.
void onMqttSubscribe(uint16_t packetId, uint8_t qos) {
Serial.println("Subscribe acknowledged.");
Serial.print(" packetId: ");
Serial.println(packetId);
Serial.print(" qos: ");
Serial.println(qos);
}
void onMqttUnsubscribe(uint16_t packetId) {
Serial.println("Unsubscribe acknowledged.");
Serial.print(" packetId: ");
Serial.println(packetId);
}
The onMqttMessage() is responsible for obtaining the MQTT message from the subscribed topic and displaying it on the OLED. The string variable ‘messageTemp’ holds the MQTT message. To display the sensor readings on the OLED, we will first check the topic and then display the readings accordingly. We will set the cursor, clear the display, set the text size and then print the readings along with the units on the OLED. Finally, we will call the display() function on the display object so that the text displays on the OLED.
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
Serial.println("Publish received.");
Serial.print(" topic: ");
Serial.println(topic);
String messageTemp;
for (int i = 0; i < len; i++) {
Serial.print((char)payload[i]);
messageTemp += (char)payload[i];
}
if (String(topic) == "esp32/temperature"){
Serial.print("\n Temperature: ");
Serial.println(messageTemp);
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0,0);
display.print("Temp: ");
display.print(messageTemp);
display.print(" ");
display.setTextSize(0.5);
display.cp437(true);
display.write(167);
display.setTextSize(1);
display.print("C");
display.display();
}
if (String(topic) == "esp32/humidity"){
Serial.print("\n Humidity: ");
Serial.println(messageTemp);
display.setTextSize(1);
display.setCursor(0,20);
display.print("Hum: ");
display.print(messageTemp);
display.print(" %");
display.display();
}
if (String(topic) == "esp32/pressure"){
Serial.print("\n Pressure: ");
Serial.println(messageTemp);
display.setTextSize(1);
display.setCursor(0,40);
display.print("BP: ");
display.setTextSize(1);
display.print(messageTemp);
display.print(" hPa");
display.display();
}
}
setup()
Inside the setup() function, we will open a serial connection at a baud rate of 115200. Moreover, we will also initialize the OLED display by using display.begin(). Make sure you specify the correct address of your display. In our case, it is 0X3C.
Serial.begin(115200);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); //initialize OLED
Next we create the timers for the MQTT and Wi-Fi reconnection.
mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToMqtt));
wifiReconnectTimer = xTimerCreate("wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToWifi));
Call the WiFiEvent() function, to display the Wi-Fi status in the serial monitor.
WiFi.onEvent(WiFiEvent);
Moreover, call the rest of the callback functions that we previously described as well. These include onConnect(), onDisconnect(), onSubscribe(), onUnSubscribe() and onMessage(). Additionally, set the MQTT server by calling the setServer() function on the AsyncMqttClient object. Specify the MQTT_HOST and MQTT_PORT as parameters inside it. Connect ESP32 to the Wi-Fi network by calling connectToWifi() function.
mqttClient.onConnect(onMqttConnect);
mqttClient.onDisconnect(onMqttDisconnect);
mqttClient.onSubscribe(onMqttSubscribe);
mqttClient.onUnsubscribe(onMqttUnsubscribe);
mqttClient.onMessage(onMqttMessage);
mqttClient.setServer(MQTT_HOST, MQTT_PORT);
connectToWifi();
Demonstration
To see the demonstration of the above code, upload the code to ESP32 connected with the OLED. Before uploading the code, make sure to select the ESP32 Dev Module from Tools > Board.
Also, select the correct COM port to which this ESP32 board is connected from Tools > Port.
Make sure the ESP32 that is publishing BME280 readings is powered on.
Once the code is uploaded to ESP32, the OLED Display will start displaying the sensor readings on its screen. These include temperature, humidity, and barometric pressure. You can have a look at it in the picture given below.
Open the serial monitor and you will be able to see the following messages indicating a successful connection to Wi-Fi, MQTT Broker, and subscription.
You may also like to read:
- ESP32 MQTT Publish Subscribe DHT22 Readings with Arduino IDE
- Connect ESP32 to EMQX Cloud MQTT Broker (Arduino IDE)
- ESP32 MQTT Publish Subscribe DS18B20 Readings with Arduino IDE
- Connect ESP32 to AWS IoT MQTT and Publish Sensor Readings
- ESP32 MQTT Client: Subscribe and Publish BME280 sensor readings on HiveMQ