Background

This is a quick tutorial for beginners – using ESP32 & DHT11 to collect sensor data and publish it using MQTT. Mobile and server apps can subscribe to the temperature and humidity data from the DHT11. In a future tutorial, I’ll be showing how to use this data in a Flutter app to create a beautiful dashboard. See First Flaps with Flutter and flutter.io for more details.

The goal of this project is to collect temperature and humidity data and submit the data to an MQTT broker. However, we must tune the project so that it doesn’t waste resources. There is a balance between how frequently to read and publish data. We want the information to be recent, but, doesn’t need to be instantaneous.

Disclaimer: This guide contains affiliate links to the hardware being used. There is no additional cost for using affiliate links. I only link to products that are relevant to the guide and that I have personally purchased.

Even though we are using a wired power source (5v USB), many sensor-related projects that go into production do not have that luxury. Because we need this project the be flexible, where it can run with wired or battery power there are a few considerations that help us find that balance.

Factors for conserving energy:

  1. Reduce the frequency that data is collected and published
  2. Increase the time between retries when unable to connect to the network
    • Attempting a network connection is REALLY expensive
  3. Put the ESP32 into a deep sleep between sensor read intervals
    • While being active the ESP32 uses ~200mA. However, when deep sleep is configured properly, the same controller can use as little as 10uA. That is HUGE if you decide to power this with 18650 batteries.

What do you need?

ESP-WROOM-32

For this project, I’m using ESP32 for three reasons. First, it has a huge community of users and a massive backlog of documentation. Second, ESP32 supports WiFi. Lastly, because I have a spare one laying around. It’s also common to use the ESP8266 instead of the ESP32

DHT-11

The DHT-11 and DHT-22 are interchangeable for this project. The only notable difference between the two is that the DHT-22 is slightly more accurate (within 2%) and operates in a larger temperature range.

As there is a large variety of DHT-11/22, you may find that your DHT doesn’t match exactly with this guide. That’s okay. Just make sure that the Power, Ground, and Data pins are connected to the ESP32 appropriately.

Software Requirements

To add the additional libraries, please follow the instructions as per each. However, in general, download/clone the zip file and import it using the “Manage Libraries…” option in the Arduino IDE.

Code Walkthrough

In any Arduino project, you have two main pieces of code, setup() and loop(). Setup only runs one time, when the device boots. Whereas the loop runs indefinitely – think of it as a big while-loop.

For brevity, we’re just looking at the setup and loop methods, not all of the variable declarations, imports, and helper methods. For a full code example, look to the bottom of the page.

This guide covers the direct powered (plugged in) scenario. For the full code related to deep sleep and battery power, check the Github repo.

Setup()

void setup() {
  Serial.begin(115200);
  delay(10);

  WiFi.mode(WIFI_STA);
  WiFi.disconnect();

  delay(100);

  connectWifi();
  initMQTT();
}

To start, we configure the Serial baud, this allows us to write logs out to serial and see them in the inspector while developing. Super handy for debugging.

Next, WiFi.mode(WIFI_STA) makes sure that your device is only acting as a WiFi client and not an Access Point. Double check that the WiFi is not already connected by closing any existing connection.

Connect to the network and then also initialize MQTT. As our MQTT is dependent on a network connection, connect wifi first.

Loop()

void loop() {
  if(WiFi.status() != WL_CONNECTED){
    connectWifi();
  } else {
    if(!client.connected()){
      connectMQTT();
    } else {
      publishDHT11();
    }
  }
  client.loop();
  delay(300000);
}

Every time the loop comes around, make sure that a usable network connection exists by comparing the WiFi.status() to the constant value WL_CONNECTED – if not, connect.

If we are already connected to the network, check if the MQTT client is connected. If not, attempt to connect with the MQTT broker. Otherwise, read data and publish it to the broker using publishDHT11().

Finally, tell the loop to delay(300000) which makes the loop pause for 300,000ms -> 300 seconds -> 5 minutes.

Publishing to MQTT Broker

void publishDHT11(){
  int dhtState = DHT.read(DHT11PIN);

  if(dhtState == 0 || dhtState == -1){
    humidity = DHT.humidity;
    tempC = DHT.temperature;

    Serial.println(String(humidity).c_str());
    Serial.println(String(convertCtoF(tempC)).c_str());

    client.publish("codetober/humid", String(humidity).c_str());
    client.publish("codetober/tempF", String(convertCtoF(tempC)).c_str());
    client.publish("codetober/tempC", String(tempC).c_str());
  } else {
    Serial.println("Error fetching DHT11 Data");
  }
}

The very first thing to do is to capture the data from the DHT-11 sensor. As long as there wasn’t an error getting the data from the sensor; extract it, format it, and get ready to publish.

When publishing to the MQTT broker, we organize the data by specifying a Topic (destination). Topics are organized in a hierarchy, so when we specify “codetober/tempF” and “codetober/humid” we are creating group for our data within the parent container “codetober”.

When reading the data, a client can subscribe to each of our topics individually, or to all of the data at once!

Example to get F temperature “codetober/tempF”. To get all of the data, a client can simply subscribe to “codetober/#” where “#” is a wildcard.

What’s next?

In a future guide, I’ll be showing how to read the data from the MQTT broker and display it in a mobile app developed using Flutter.

If you need to learn the basics of MQTT, I recently published an article for beginners that describes MQTT and how it is used.

For a full reference of this code and the deep sleep example, look in the Github repo.

Full Code Example

#include <PubSubClient.h>
#include<WiFi.h>
#include<dht11.h>

#define DHT11PIN 2

const char* ssid = "";
const char* password = "";
const char* mqtt_server = "broker.hivemq.com";

int status = WL_IDLE_STATUS;
WiFiClient wifiClient;
PubSubClient client(wifiClient);

dht11 DHT;

float tempC = 0.0;
float cToFRate = 1.8;
float cToF32 = 32;
float humidity = 0.0;

void setup() {
  Serial.begin(115200);
  delay(10);

  WiFi.mode(WIFI_STA);
  WiFi.disconnect();

  delay(100);

  connectWifi();
  initMQTT();
}

void loop() {
  if(WiFi.status() != WL_CONNECTED){
    connectWifi();
  } else {
    if(!client.connected()){
      connectMQTT();
    } else {
      publishDHT11();
    }
  }
  client.loop();
  delay(300000);
}

void initMQTT(){
  Serial.println("Initializing MQTT");
  
  randomSeed(micros());
  client.setServer(mqtt_server, 1883);
  connectMQTT();
}

void connectMQTT(){
  
  while(!client.connected()){
    String clientId = "Client-";
    clientId += String(random(0xffff), HEX);
    if(client.connect(clientId.c_str())){
      Serial.println("Successfully connected MQTT");
    } else {
      Serial.println("Error!");
      Serial.println(client.state());
      delay(60000);
    }
  }
}

void publishDHT11(){
  int dhtState = DHT.read(DHT11PIN);

  if(dhtState == 0 || dhtState == -1){
    humidity = DHT.humidity;
    tempC = DHT.temperature;

    Serial.println(String(humidity).c_str());
    Serial.println(String(convertCtoF(tempC)).c_str());

    client.publish("codetober/humid", String(humidity).c_str());
    client.publish("codetober/tempF", String(convertCtoF(tempC)).c_str());
    client.publish("codetober/tempC", String(tempC).c_str());
  } else {
    Serial.println("Error fetching DHT11 Data");
  }
}

void connectWifi(){
  WiFi.begin(ssid, password);
  
  while(status != WL_CONNECTED){
    status = WiFi.status();
    delay(1000);
    Serial.print(".");
  }
}

float convertCtoF(float t){
  return ((t * cToFRate) + cToF32);
}