In this ESP-IDF project, we will learn to publish DHT22 sensor readings to Node-Red with ESP32 MQTT. We will perform MQTT communication with ESP32 and Node-Red and publish and subscribe to MQTT topics. The ESP32 development board will act as an MQTT publisher and Node-Red will act as a subscriber. Using MQTT protocol, the ESP32 will publish DHT22 sensor readings on the Node-Red Dashboard which will subscribe to the corresponding MQTT topics. The sensor data constituting of temperature and humidity will be interactively displayed on the Node-Red Dashboard.
We will use Mosquitto MQTT Broker on Raspberry Pi but you can install it on your Windows and Linux machine also, if you are using them instead. The publisher and subscriber will make connections with the MQTT broker installed on Raspberry Pi. After that, the ESP32 will publish sensor data to the Node-Red dashboard on the specified topics.
Before we move ahead, make sure you have the latest version of VS Code installed on your system with the ESP-IDF extension configured.
ESP32 MQTT Publish DHT22 Sensor Readings to Node-Red
Our aim is to create a project that uses ESP32 MQTT protocol using ESP-IDF to publish DHT sensor readings to Node-Red. Let us look the steps that are involved in this project.
- The ESP32 board is interfaced with a DHT22 sensor which is used to measure the relative humidity and the ambient temperature. This ESP32 board will connect to the MQTT broker. In our case, we will use Mosquitto broker on Raspberry Pi. If you want to know how to successfully install the broker on your Raspberry Pi, you can refer to the following guide: (Install Mosquitto MQTT Broker on Raspberry Pi
- After acquiring the sensor data from the DHT sensor, the ESP32 board publishes these readings on individual MQTT topics.: esp32/dht/temperature is for publishing DHT temperature and esp32/dht/humidity is for publishing DHT humidity values.
- Node-Red will subscribe to these two topics. Therefore, it will be able to receive the sensor readings and interactively display them in its dashboard with the help of gauges.
Interfacing DHT22 Sensor with ESP32
DHT22 sensor (AM2302) is a simple, low priced temperature and humidity sensor that uses capacitive humidity sensor and a thermistor to calculate the sensor data in the surrounding atmosphere. This includes the relative humidity and the ambient temperature. It is a pre-calibrated sensor that outputs a calibrated digital output with a 1-wire protocol. Therefore, they are easily connected with the microcontrollers eg. ESP32 development boards using a single GPIO pin.
We will need the following components to connect our ESP32 board with the DHT22 sensor.
- ESP32 board
- DHT22 sensor
- 10k ohm resistor (not required if using the DHT22 sensor module)
- Breadboard
- Connecting Wires
The connections between the devices are specified in the table below:
DHT22 Pin | ESP32 |
---|---|
1 (Vcc) | The VCC pin will be connected with 3.3V or Vin pin of ESP32 board. |
2 (Data Out) | Any GPIO pin of the ESP32 board along with a 10k ohm pull-up resistor. We are using GPIO27 to connect with pin 2 along with the pull-up resistor. |
3 (NC) | This pin is not used. |
4 (GND) | Common ground with ESP32. |
Connect the DHT22 sensor with the ESP32 development board as shown in the schematic diagram below:
You can read this dedicated guide for DHT22:
ESP32 MQTT DHT22 Readings with Node-Red and ESP-IDF
We will build and create a project in VS Code with ESP-IDF extension. The code will read temperature and humidity readings by using the DHT22 library and then use mqtt_client library to connect ESP32 with the MQTT broker, and publish the DHT sensor data on different topics. We will use DHT22 library provided by Andrey-m on GitHub.
Create Example Project
Open your VS Code and head over to View > Command Palette. Type ESP-IDF: New Project in the search bar and press enter.
Specify the project name and directory, along with the ESP-IDF board and target. For the ESP-IDF board, we have chosen the custom board option. For ESP-IDF target, we have chosen ESP32 module. Click ‘Choose Template’ button to proceed forward.
In the Extension, select ESP-IDF option:
We will click the ‘sample_project’ under the get-started tab. Now click ‘Create project using template sample_project.’
You will get a notification that the project has been created. To open the project in a new window, click ‘Yes.’
This opens the project that we created inside the EXPLORER tab. There are several folders inside our project folder. This is the same for every project which you will create through ESP-IDF Explorer.
- First, let’s add the necessary header files for the libraries required for this project. Create a new folder called ‘components/DHT22’ and add the following files listed below:
CMakeLists.txt
idf_component_register(SRCS "DHT22.c"
INCLUDE_DIRS "include")
DHT22.h
/*
DHT22 temperature sensor driver
*/
#ifndef DHT22_H_
#define DHT22_H_
#define DHT_OK 0
#define DHT_CHECKSUM_ERROR -1
#define DHT_TIMEOUT_ERROR -2
// == function prototypes =======================================
void setDHTgpio(int gpio);
void errorHandler(int response);
int readDHT();
float getHumidity();
float getTemperature();
int getSignalLevel( int usTimeOut, bool state );
#endif
DHT22.c
/*------------------------------------------------------------------------------
DHT22 temperature & humidity sensor AM2302 (DHT22) driver for ESP32
Jun 2017: Ricardo Timmermann, new for DHT22
Code Based on Adafruit Industries and Sam Johnston and Coffe & Beer. Please help
to improve this code.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
PLEASE KEEP THIS CODE IN LESS THAN 0XFF LINES. EACH LINE MAY CONTAIN ONE BUG !!!
---------------------------------------------------------------------------------*/
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/gpio.h"
#include "DHT22.h"
// == global defines =============================================
static const char* TAG = "DHT";
int DHTgpio = 4; // my default DHT pin = 4
float humidity = 0.;
float temperature = 0.;
// == set the DHT used pin=========================================
void setDHTgpio( int gpio )
{
DHTgpio = gpio;
}
// == get temp & hum =============================================
float getHumidity() { return humidity; }
float getTemperature() { return temperature; }
// == error handler ===============================================
void errorHandler(int response)
{
switch(response) {
case DHT_TIMEOUT_ERROR :
ESP_LOGE( TAG, "Sensor Timeout\n" );
break;
case DHT_CHECKSUM_ERROR:
ESP_LOGE( TAG, "CheckSum error\n" );
break;
case DHT_OK:
break;
default :
ESP_LOGE( TAG, "Unknown error\n" );
}
}
/*-------------------------------------------------------------------------------
;
; get next state
;
; I don't like this logic. It needs some interrupt blocking / priority
; to ensure it runs in realtime.
;
;--------------------------------------------------------------------------------*/
int getSignalLevel( int usTimeOut, bool state )
{
int uSec = 0;
while( gpio_get_level(DHTgpio)==state ) {
if( uSec > usTimeOut )
return -1;
++uSec;
ets_delay_us(1); // uSec delay
}
return uSec;
}
/*----------------------------------------------------------------------------
;
; read DHT22 sensor
copy/paste from AM2302/DHT22 Docu:
DATA: Hum = 16 bits, Temp = 16 Bits, check-sum = 8 Bits
Example: MCU has received 40 bits data from AM2302 as
0000 0010 1000 1100 0000 0001 0101 1111 1110 1110
16 bits RH data + 16 bits T data + check sum
1) we convert 16 bits RH data from binary system to decimal system, 0000 0010 1000 1100 → 652
Binary system Decimal system: RH=652/10=65.2%RH
2) we convert 16 bits T data from binary system to decimal system, 0000 0001 0101 1111 → 351
Binary system Decimal system: T=351/10=35.1°C
When highest bit of temperature is 1, it means the temperature is below 0 degree Celsius.
Example: 1000 0000 0110 0101, T= minus 10.1°C: 16 bits T data
3) Check Sum=0000 0010+1000 1100+0000 0001+0101 1111=1110 1110 Check-sum=the last 8 bits of Sum=11101110
Signal & Timings:
The interval of whole process must be beyond 2 seconds.
To request data from DHT:
1) Sent low pulse for > 1~10 ms (MILI SEC)
2) Sent high pulse for > 20~40 us (Micros).
3) When DHT detects the start signal, it will pull low the bus 80us as response signal,
then the DHT pulls up 80us for preparation to send data.
4) When DHT is sending data to MCU, every bit's transmission begin with low-voltage-level that last 50us,
the following high-voltage-level signal's length decide the bit is "1" or "0".
0: 26~28 us
1: 70 us
;----------------------------------------------------------------------------*/
#define MAXdhtData 5 // to complete 40 = 5*8 Bits
int readDHT()
{
int uSec = 0;
uint8_t dhtData[MAXdhtData];
uint8_t byteInx = 0;
uint8_t bitInx = 7;
for (int k = 0; k<MAXdhtData; k++)
dhtData[k] = 0;
// == Send start signal to DHT sensor ===========
gpio_set_direction( DHTgpio, GPIO_MODE_OUTPUT );
// pull down for 3 ms for a smooth and nice wake up
gpio_set_level( DHTgpio, 0 );
ets_delay_us( 3000 );
// pull up for 25 us for a gentile asking for data
gpio_set_level( DHTgpio, 1 );
ets_delay_us( 25 );
gpio_set_direction( DHTgpio, GPIO_MODE_INPUT ); // change to input mode
// == DHT will keep the line low for 80 us and then high for 80us ====
uSec = getSignalLevel( 85, 0 );
// ESP_LOGI( TAG, "Response = %d", uSec );
if( uSec<0 ) return DHT_TIMEOUT_ERROR;
// -- 80us up ------------------------
uSec = getSignalLevel( 85, 1 );
// ESP_LOGI( TAG, "Response = %d", uSec );
if( uSec<0 ) return DHT_TIMEOUT_ERROR;
// == No errors, read the 40 data bits ================
for( int k = 0; k < 40; k++ ) {
// -- starts new data transmission with >50us low signal
uSec = getSignalLevel( 56, 0 );
if( uSec<0 ) return DHT_TIMEOUT_ERROR;
// -- check to see if after >70us rx data is a 0 or a 1
uSec = getSignalLevel( 75, 1 );
if( uSec<0 ) return DHT_TIMEOUT_ERROR;
// add the current read to the output data
// since all dhtData array where set to 0 at the start,
// only look for "1" (>28us us)
if (uSec > 40) {
dhtData[ byteInx ] |= (1 << bitInx);
}
// index to next byte
if (bitInx == 0) { bitInx = 7; ++byteInx; }
else bitInx--;
}
// == get humidity from Data[0] and Data[1] ==========================
humidity = dhtData[0];
humidity *= 0x100; // >> 8
humidity += dhtData[1];
humidity /= 10; // get the decimal
// == get temp from Data[2] and Data[3]
temperature = dhtData[2] & 0x7F;
temperature *= 0x100; // >> 8
temperature += dhtData[3];
temperature /= 10;
if( dhtData[2] & 0x80 ) // negative temp, brrr it's freezing
temperature *= -1;
// == verify if checksum is ok ===========================================
// Checksum is the sum of Data 8 bits masked out 0xFF
if (dhtData[4] == ((dhtData[0] + dhtData[1] + dhtData[2] + dhtData[3]) & 0xFF))
return DHT_OK;
else
return DHT_CHECKSUM_ERROR;
}
- Now head over to the main.c file. The main folder contains the source code meaning the main.c file will be found here. Go to main > main.c and open it. Copy the code given below in that file and save it.
ESP32 ESP-IDF MQTT DHT22 Code
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_wifi.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "rom/ets_sys.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
#include "esp_log.h"
#include "mqtt_client.h"
#include "DHT22.h"
static const char *TAG = "MQTT_EXAMPLE";
#define EXAMPLE_ESP_WIFI_SSID "YOUR_SSID"
#define EXAMPLE_ESP_WIFI_PASS "YOUR_PASSWORD"
#define MAX_RETRY 10
static int retry_cnt = 0;
#define MQTT_PUB_TEMP_DHT "esp32/dht/temperature"
#define MQTT_PUB_HUM_DHT "esp32/dht/humidity"
uint32_t MQTT_CONNECTED = 0;
static void mqtt_app_start(void);
static esp_err_t wifi_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
switch (event_id)
{
case WIFI_EVENT_STA_START:
esp_wifi_connect();
ESP_LOGI(TAG, "Trying to connect with Wi-Fi\n");
break;
case WIFI_EVENT_STA_CONNECTED:
ESP_LOGI(TAG, "Wi-Fi connected\n");
break;
case IP_EVENT_STA_GOT_IP:
ESP_LOGI(TAG, "got ip: startibg MQTT Client\n");
mqtt_app_start();
break;
case WIFI_EVENT_STA_DISCONNECTED:
ESP_LOGI(TAG, "disconnected: Retrying Wi-Fi\n");
if (retry_cnt++ < MAX_RETRY)
{
esp_wifi_connect();
}
else
ESP_LOGI(TAG, "Max Retry Failed: Wi-Fi Connection\n");
break;
default:
break;
}
return ESP_OK;
}
void wifi_init(void)
{
esp_event_loop_create_default();
esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL);
esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL);
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS,
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
},
};
esp_netif_init();
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
esp_wifi_set_mode(WIFI_MODE_STA);
esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config);
esp_wifi_start();
}
/*
* @brief Event handler registered to receive MQTT events
*
* This function is called by the MQTT client event loop.
*
* @param handler_args user data registered to the event.
* @param base Event base for the handler(always MQTT Base in this example).
* @param event_id The id for the received event.
* @param event_data The data for the event, esp_mqtt_event_handle_t.
*/
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
esp_mqtt_event_handle_t event = event_data;
switch ((esp_mqtt_event_id_t)event_id)
{
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
MQTT_CONNECTED = 1;
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
MQTT_CONNECTED = 0;
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
}
esp_mqtt_client_handle_t client = NULL;
static void mqtt_app_start(void)
{
ESP_LOGI(TAG, "STARTING MQTT");
esp_mqtt_client_config_t mqttConfig = {
.uri = "mqtt://YOUR_RPI_IP_ADDRESS:1883"};
client = esp_mqtt_client_init(&mqttConfig);
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
esp_mqtt_client_start(client);
}
void DHT_Publisher_task(void *pvParameter)
{
setDHTgpio(GPIO_NUM_27);
while(1) {
int ret = readDHT();
errorHandler(ret);
float hum = getHumidity();
char humidity[12];
sprintf(humidity, "%.2f", hum);
float temp = getTemperature();
char temperature[12];
sprintf(temperature, "%.2f", temp);
printf("Humidity %.2f %%\n", getHumidity());
printf("Temperature %.2f degC\n\n", getTemperature());
if (MQTT_CONNECTED){
esp_mqtt_client_publish(client, MQTT_PUB_HUM_DHT, humidity, 0, 0, 0);
esp_mqtt_client_publish(client, MQTT_PUB_TEMP_DHT, temperature, 0, 0, 0);
}
vTaskDelay(5000 / portTICK_RATE_MS);
}
}
void app_main()
{
nvs_flash_init();
wifi_init();
xTaskCreate(&DHT_Publisher_task, "DHT_Publisher_task", 2048, NULL, 5, NULL );
}
How the Code Works?
Firstly, we will start by including the necessary libraries that includes the gpio driver, DHT22 library, FreeRTOS libraries to generate delays, esp_wifi.h to enable Wi-Fi connectivity, esp_event.h to monitor the Wi-Fi and MQTT events, mqtt_client.h to setup MQTT protocol for publish/subscribe and esp_log.h as the logging library.
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_wifi.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "rom/ets_sys.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
#include "esp_log.h"
#include "mqtt_client.h"
#include "DHT22.h"
This code uses Informational Logging. The log function takes in two arguments. The first argument is the tag and the second argument is a formatted string. Therefore this global variable will be useful while calling the ESP_LOGI() functions. Here, “MQTT_EXAMPLE” and is the tag that will be used while logging.
static const char *TAG = "MQTT_EXAMPLE";
Next, we have defined the parameters for the Wi-Fi SSID and password. Specify your own network credentials to successfully connect your ESP32 board with the Wi-Fi network.
#define EXAMPLE_ESP_WIFI_SSID "YOUR_SSID"
#define EXAMPLE_ESP_WIFI_PASS "YOUR_PASSWORD"
We will define some variables to monitor the Wi-Fi connection including the maximum retry value that is set as 10 and the retry count that initially holds the value 0.
#define MAX_RETRY 10
static int retry_cnt = 0;
After that we define the topics the ESP32 board will publish to. These include: esp32/dht/temperature for temperature readings and esp32/dht/humidity for humidity readings.
#define MQTT_PUB_TEMP_DHT "esp32/dht/temperature"
#define MQTT_PUB_HUM_DHT "esp32/dht/humidity"
Handling Wi-Fi Events
We have the wifi_event_handler() function which handles the Wi-Fi events. This acts as a callback function, when any Wi-Fi event occurs. There are a different events that the Wi-Fi driver posts to the event task. These include: SYSTEM_EVENT_STA_START, SYSTEM_EVENT_STA_CONNECTED, SYSTEM_EVENT_STA_GOT_IP and SYSTEM_EVENT_STA_DISCONNECTED. These events occur if Wi-Fi is connected, a station gets connected to the AP, Wi-Fi gets IP or if the station disconnects from the AP respectively.
In our wifi_event_handler() function, we will check if the Wi-Fi event id corresponds to either of these four events. If it does, then it will print a relevant message on the ESP-IDF according to the event type and call an appropriate function. If either SYSTEM_EVENT_STA_START event, the function esp_wifi_connect() will be called. This will be responsible for connecting the ESP32 Wi-Fi station to the access point. If the event SYSTEM_EVENT_STA_DISCONNECTED occurs, then the function esp_wifi_connect() will be called till the maximum retry value which we set as 10. If the Wi-Fi doesn’t connect within the number of tries, then the ESP-IDF terminal prints the message that the max retry failed. If SYSTEM_EVENT_STA_GOT_IP event occurs, then the function mqtt_app_start() will be called to initiate the MQTT client connection.
static esp_err_t wifi_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
switch (event_id)
{
case WIFI_EVENT_STA_START:
esp_wifi_connect();
ESP_LOGI(TAG, "Trying to connect with Wi-Fi\n");
break;
case WIFI_EVENT_STA_CONNECTED:
ESP_LOGI(TAG, "Wi-Fi connected\n");
break;
case IP_EVENT_STA_GOT_IP:
ESP_LOGI(TAG, "got ip: startibg MQTT Client\n");
mqtt_app_start();
break;
case WIFI_EVENT_STA_DISCONNECTED:
ESP_LOGI(TAG, "disconnected: Retrying Wi-Fi\n");
if (retry_cnt++ < MAX_RETRY)
{
esp_wifi_connect();
}
else
ESP_LOGI(TAG, "Max Retry Failed: Wi-Fi Connection\n");
break;
default:
break;
}
return ESP_OK;
}
Initialize Wi-Fi
The wifi_init() function will be called inside the app_main() function to initialize Wi-Fi in station mode for ESP32.
void wifi_init(void)
{
esp_event_loop_create_default();
esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL);
esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL);
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS,
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
},
};
esp_netif_init();
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
esp_wifi_set_mode(WIFI_MODE_STA);
esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config);
esp_wifi_start();
}
We start off by creating the default event loop by calling the function esp_event_loop_create_default(). Afterwards, we use esp_event_handler_register() to register event handlers to the system event loop for both the WIFI_EVENT and IP_EVENT.
esp_event_loop_create_default();
esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL);
esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL);
In this part, we set up the SSID and password of the network we want our ESP32 to connect with, as well as assign the security setting.
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS,
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
},
};
Here we are initializing lwIP through the function esp_netif_init() and create an IwIP task which is also known as a TCP/IP task. lwIP is a TCP/IP library stack provided by ESP-IDF that is used to perform various protocols such as TCP, UDP, DHCP etc.
esp_netif_init();
The following line is used for Wi-Fi default initialization for station mode. This API is used to initialize as well as register event handlers for the default interface which is station in our case. It creates a network interface instance binding Wi-Fi driver and TCP/IP stack. When a station is in the process of connecting to an AP, various processes automatically get handled through this function.
esp_netif_create_default_wifi_sta();
Then we initialize the Wi-Fi allocate resource for the Wi-Fi driver. It is responsible for initiating the Wi-Fi task. It takes in a single parameter cfg which is a pointer to the already initialized Wi-Fi configuration structure which is set to WIFI_INIT_CONFIG_DEFAULT() so that the initialization of the configuration is at default values. Here wifi_init_config_t is a structure that denotes the Wi-Fi stack configuration parameters that are passed inside the esp_wifi_init() function.
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
Moreover, we set Wi-Fi mode as station. To set the Wi-Fi mode as station, we use the function esp_wifi_set_mode() and pass the WiFI_MODE_STA as a parameter inside it.
esp_wifi_set_mode(WIFI_MODE_STA);
Then we assign our network’s SSID and password. This way the Wi-Fi driver gets configured with the AP network credentials and Wi-Fi mode. Then, we start the Wi-Fi connection at our assigned configuration.
esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config);
esp_wifi_start();
Handling MQTT Events
Next, we have the mqtt_event_handler() function which handles the MQTT events. This function is called by the MQTT client event loop. We will check if the event id corresponds to either of the three events which can be MQTT_EVENT_CONNECTED, MQTT_EVENT_DISCONNECTED or MQTT_EVENT_ERROR. If it does, then it will print a relevant message on the ESP-IDF according to the event type and call an appropriate function.
The table below shows the descriptions of the MQTT events that will be used in this example.
MQTT_EVENT_CONNECTED | This event is posted when the client successfully connects to the broker and is ready to send/receive data. |
MQTT_EVENT_DISCONNECTED | This event is posted when the client cancels the connection between the broker as the server is not available. The client is not able to send/receive data in this scenario. |
MQTT_EVENT_ERROR | This events gets posted by the client when it experiences an error. |
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
esp_mqtt_event_handle_t event = event_data;
switch ((esp_mqtt_event_id_t)event_id)
{
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
MQTT_CONNECTED = 1;
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
MQTT_CONNECTED = 0;
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
}
If the event MQTT_EVENT_CONNECTED is posted by the client, the “MQTT_CONNECTED” variable is set to the value 1, which initially held the value 0.
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
MQTT_CONNEECTED = 1;
break;
If the event MQTT_EVENT_DISCONNECTED is posted by the client, the MQTT_CONNECTED variable is set to the value 0 and the terminal prints the message indicating that this event occurred. Similarly, for the rest of the events a relevant message is printed on the terminal along with the event_id incase of a different event.
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
MQTT_CONNEECTED = 0;
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
mqtt_app_start()
Inside the mqtt_app_start() function, an MQTT client handle is created according to the configuration parameters set using esp_mqtt_client_init(). This function takes in a single parameter which is the pointer to the MQTT configuration structure. The MQTT configuration structure holds the MQTT Broker IP address which acts as the mqtt server. Make sure to specify the IP address of your Raspberry Pi which has Mosquitto broker running on it, inside the MQTT configuration structure.
After that we will call the function esp_mqtt_client_register_event() which is used to register the MQTT event. It takes in four parameters. The first parameter is the MQTT client handle. The second parameter is the event type. The third parameter is the handler callback and the last parameter is the handle context which is mqtt_client_handle, ‘client’ in our case. Finally, we will start the MQTT client using the function esp_mqtt_client_start() and specify the already created client handle as a parameter inside it.
esp_mqtt_client_handle_t client = NULL;
static void mqtt_app_start(void)
{
ESP_LOGI(TAG, "STARTING MQTT");
esp_mqtt_client_config_t mqttConfig = {
.uri = "mqtt://YOUR_RPI_IP_ADDRESS:1883"};
client = esp_mqtt_client_init(&mqttConfig);
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
esp_mqtt_client_start(client);
}
DHT_Publisher_Task()
Inside the DHT_Publisher_Task() function, the DHT22 sensor data is acquired and published to the corresponding topics after every 5 seconds using MQTT protocol.
void DHT_Publisher_task(void *pvParameter)
{
setDHTgpio(GPIO_NUM_27);
while(1) {
int ret = readDHT();
errorHandler(ret);
float hum = getHumidity();
char humidity[12];
sprintf(humidity, "%.2f", hum);
float temp = getTemperature();
char temperature[12];
sprintf(temperature, "%.2f", temp);
printf("Humidity %.2f %%\n", getHumidity());
printf("Temperature %.2f degC\n\n", getTemperature());
if (MQTT_CONNECTED){
esp_mqtt_client_publish(client, MQTT_PUB_HUM_DHT, humidity, 0, 0, 0);
esp_mqtt_client_publish(client, MQTT_PUB_TEMP_DHT, temperature, 0, 0, 0);
}
vTaskDelay(5000 / portTICK_RATE_MS);
}
}
Firstly, the GPIO pin connected with the data pin is set using setDHTgpio() function. This function takes in a single parameter which is the ESP32 GPIO pin number that will output the sensor data.
setDHTgpio(GPIO_NUM_27);
Then inside an infinite while loop, we will start reading from the DHT22 sensor after every 5 seconds using readDHT(). The errorHandler() function checks for the response after reading from DHT22. To obtain the humidity reading in percentage, getHumidity() function is used. This returns the humidity reading as a float data type. Likewise, to obtain the temperature reading in degree Celsius, getTemperature() function is used. This also returns the temperature reading as a float data type. Both of these functions return the values in float data type. We will first convert the float data type to an array of characters and round off to two decimal places. The readings are continuously read after a delay of 5 seconds and printed on the ESP-IDF terminal.
Note that the DHT22 sensor’s sampling rate is 0.5Hz which means that it outputs new readings after every 2 seconds. Hence, the time delay is of importance.
int ret = readDHT();
errorHandler(ret);
float hum = getHumidity();
char humidity[12];
sprintf(humidity, "%.2f", hum);
float temp = getTemperature();
char temperature[12];
sprintf(temperature, "%.2f", temp);
printf("Humidity %.2f %%\n", getHumidity());
printf("Temperature %.2f degC\n\n", getTemperature());
If MQTT_CONNECTED is true, then the sensor readings will get published to the corresponding topics every 5 seconds.
if (MQTT_CONNECTED){
esp_mqtt_client_publish(client, MQTT_PUB_HUM_DHT, humidity, 0, 0, 0);
esp_mqtt_client_publish(client, MQTT_PUB_TEMP_DHT, temperature, 0, 0, 0);
}
vTaskDelay(5000 / portTICK_RATE_MS);
app_main()
Inside the app_main() function, first NVS is initialized by calling the nvs_flash_init() function. Then, wifi_init() function is called which will initialize Wi-Fi in station mode for ESP32. After that, we use xTaskCreate() to create the DHT_Publisher_Task that will publish the sensor readings on the corresponding topics after every 5 seconds.
void app_main()
{
nvs_flash_init();
wifi_init();
xTaskCreate(&DHT_Publisher_task, "DHT_Publisher_task", 2048, NULL, 5, NULL );
}
Compiling the Sketch
To flash your chip, type the following command in the serial terminal. Remember to replace the COM port with the one through which your board is connected.
idf.py -p COMX flash monitor
After the code is successfully flashed, you can view different messages on the ESP-IDF terminal. First, the Wi-Fi gets successfully connected and then the MQTT connection is established. You can also view the DHT sensor readings continuously updating on the terminal.
Now let us set up our Node-Red dashboard to view the sensor data.
Node-Red Dashboard as MQTT DHT Readings Subscriber
Our ESP32 publisher is sending the individual DHT22 readings to the specific topics after every 5 seconds. In this section, we will show you how to setup Node-Red as an MQTT subscriber to our ESP32 DHT topics. By using Node-Red dashboard, we will be able to view the sensor readings on our smart phone/laptop/computer. Lets first learn how to build the dashboard.
You can use any other MQTT subscription service as well to subscribe to the MQTT topics such as MQTTx Desktop Client which we previously used.
Refer to the getting started guide with Node-Red on Raspberry Pi:
To access Node-RED, we require 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 IP address of your Raspberry Pi followed by the port number.
For example:
192.168.18.8:1880
Create Flow
This will open the Node-RED interface. Now we will start creating the flow.
Go to the right side and find dashboard. Install the dashboard if it isn’t already installed.
Click ‘Dashboard’ to open its tab. Now we create the dashboard tab node. To create a new tab, go to the Dashboard tab and click +tab. Give a name according to your preference and an icon available from the list of links given below:
After that, click Update to save the changes.
We has created one tab named Home and using icons from Angular Material Icons.
Next we will add the widgets. We will add one group to the tab for the sensor. To create a group, click +group in the tab.
We have named the group as ‘DHT Readings.’
Now let us add the widgets to our group. To display the temperature and humidity readings we will choose gauges. Go to the Nodes section found at the far left and scroll down to find the required nodes under Dashboard.
Drag and drop two gauges and two mqtt in nodes to the flow as shown below:
Let us edit the properties of the nodes now. Click the first mqtt in node. This opens its properties. For the server (MQTT Broker), which is Mosquitto Broker on Raspberry Pi, set it to localhost:1883. Add the name of the topic to be subscribed, specify the qos and then click the Done button.
This particular node is subscribed to the topic ‘esp32/dht/temperature.’
Likewise, edit the properties of the second mqtt in node. This particular node is subscribed to the topic ‘esp32/dht/humidity.’
Edit the properties of the first and second gauge as shown below:
Join the nodes as shown below. Then, click the Deploy button at the top.
The status of the MQTT in nodes changes to ‘connected.’
To open the dashboard, type: http://Your_RPI_IP_address:1880/ui in a new web browser and press enter. The user interface will open showing the two gauges with readings from the DHT sensor.
This is how the Node-Red Dashboard looks like when viewed from a smart phone.
You may also like to read:
- ESP32 MQTT Client Publish and Subscribe using ESP-IDF
- ESP32 MQTT Publish DS18B20 Sensor Readings to Node-Red using ESP-IDF
- ESP32 MQTT Publish BME280 Sensor Readings to Node-Red using ESP-IDF
If you are using Arduino IDE instead of ESP-IDF, you can read these:
- ESP32 MQTT Publish Multiple Sensor Readings to Node-Red with Arduino IDE
- ESP32 MQTT Publish Subscribe with Arduino IDE – Control Outputs
- ESP32 MQTT Publish Subscribe DS18B20 Readings with Arduino IDE
- ESP32 MQTT Publish Subscribe BME280 Readings with Arduino IDE
- ESP32 MQTT Publish Subscribe DHT22 Readings with Arduino IDE
We are a team of experienced Embedded Software Developers with skills in developing Embedded Linux, RTOS, and IoT products from scratch to deployment with a demonstrated history of working in the embedded software industry. Contact us for your projects: admin@esp32tutorials.com