🔗Goal
Build a temperature and humidity logger that sends sensor data to an MQTT broker over WiFi. The ESP32 reads a DHT22 sensor every 30 seconds and publishes the values as a JSON message. You can then view the data using any MQTT client — from a simple command-line tool to a full dashboard like MQTT Explorer.
Here is how the system works at a high level:
graph LR
A[DHT22 Sensor] -->|GPIO 4| B[ESP32]
B -->|WiFi| C[MQTT Broker]
C --> D[MQTT Explorer / Dashboard]
C --> E[mosquitto_sub]MQTT (Message Queuing Telemetry Transport) is a lightweight messaging protocol designed for IoT devices. The ESP32 acts as a publisher, sending sensor data to a topic on the broker. Any device that subscribes to that topic receives the data in real time.
See our Introduction to MQTT for a beginner-friendly explanation of brokers, topics, publishing, and subscribing.
🔗Prerequisites
You will need the following components:
| Component | Qty | Notes | Buy |
|---|---|---|---|
| ESP32 dev board | 1 | AliExpress | Amazon.de .co.uk .com | |
| DHT22 sensor | 1 | Also sold as AM2302 | AliExpress | Amazon.de .co.uk .com |
| 10k ohm resistor | 1 | Pull-up for the DHT22 data line | AliExpress | Amazon.de .co.uk .com |
| Breadboard | 1 | AliExpress | Amazon.de .co.uk .com | |
| Jumper wires | ~5 | AliExpress | Amazon.de .co.uk .com |
Links marked Amazon/AliExpress are affiliate links. We may earn a small commission at no extra cost to you.
You will also need the following Arduino libraries:
| Library | Author | Purpose |
|---|---|---|
| DHT sensor library | Adafruit | DHT22 sensor driver |
| Adafruit Unified Sensor | Adafruit | Dependency for the DHT library |
| PubSubClient | Nick O'Leary | MQTT client for Arduino |
For viewing the data, you will need one of the following on your computer:
- mosquitto_sub (command-line MQTT client, part of the Mosquitto package)
- MQTT Explorer (graphical MQTT client, free and cross-platform)
See our DHT22 guide for more details on the sensor, including timing requirements and the difference between DHT11 and DHT22.
🔗Tutorial
🔗Step 1: Wiring
The DHT22 has four physical pins, but only three are used. If you are looking at the front of the sensor (the side with the grid), the pins from left to right are: VCC, DATA, NC (not connected), and GND.
| Component | Pin | ESP32 Pin |
|---|---|---|
| DHT22 | Pin 1 (VCC) | 3.3V |
| DHT22 | Pin 2 (DATA) | GPIO 4 |
| DHT22 | Pin 3 (NC) | Not connected |
| DHT22 | Pin 4 (GND) | GND |
| 10k ohm resistor | Between DATA and VCC | GPIO 4 to 3.3V |
The $10\,\text{k}\Omega$ pull-up resistor ensures stable communication on the data line. Some DHT22 breakout modules include this resistor on the PCB — check your module before adding an external one.
Pin labels and GPIO numbers vary between ESP32 boards. Always check your board's pinout diagram and datasheet.
🔗Step 2: Choose an MQTT broker
For this project, we use the free public broker test.mosquitto.org on port 1883. This is great for testing but not secure — anyone can see your messages.
For a permanent setup, consider running your own broker (like Mosquitto on a Raspberry Pi) or using a private cloud broker.
| Broker | Address | Port | Notes |
|---|---|---|---|
| test.mosquitto.org | test.mosquitto.org | 1883 | Free, public, unencrypted. Good for testing |
| Local Mosquitto | Your server's IP | 1883 | Private. Install with sudo apt install mosquitto |
🔗Step 3: Upload the code
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
// ----- Configuration -----
const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* MQTT_BROKER = "test.mosquitto.org";
const int MQTT_PORT = 1883;
const char* MQTT_CLIENT_ID = "esp32_temp_logger";
const char* MQTT_PUB_TOPIC = "esp32/sensor/data";
const char* MQTT_SUB_TOPIC = "esp32/command";
#define DHT_PIN 4
#define DHT_TYPE DHT22
// ----- Objects -----
WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
DHT dht(DHT_PIN, DHT_TYPE);
unsigned long lastPublish = 0;
const unsigned long PUBLISH_INTERVAL = 30000; // 30 seconds
// ----- WiFi connection -----
void connectWiFi() {
if (WiFi.status() == WL_CONNECTED) return;
Serial.print("Connecting to WiFi");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 40) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println();
Serial.print("Connected! IP: ");
Serial.println(WiFi.localIP());
} else {
Serial.println();
Serial.println("WiFi connection failed. Will retry...");
}
}
// ----- MQTT connection -----
void connectMQTT() {
if (mqtt.connected()) return;
Serial.print("Connecting to MQTT broker...");
while (!mqtt.connected()) {
if (mqtt.connect(MQTT_CLIENT_ID)) {
Serial.println(" connected!");
mqtt.subscribe(MQTT_SUB_TOPIC);
Serial.print("Subscribed to: ");
Serial.println(MQTT_SUB_TOPIC);
} else {
Serial.print(" failed (rc=");
Serial.print(mqtt.state());
Serial.println("). Retrying in 5 seconds...");
delay(5000);
}
}
}
// ----- MQTT message callback -----
void mqttCallback(char* topic, byte* payload, unsigned int length) {
String message;
for (unsigned int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.print("Received on [");
Serial.print(topic);
Serial.print("]: ");
Serial.println(message);
// Example: respond to a "status" command
if (message == "status") {
mqtt.publish(MQTT_PUB_TOPIC, "{\"status\":\"online\"}");
Serial.println("Status request received. Responded.");
}
}
// ----- Publish sensor data -----
void publishSensorData() {
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
if (isnan(temperature) || isnan(humidity)) {
Serial.println("ERROR: Failed to read from DHT22 sensor.");
return;
}
// Build JSON payload
String payload = "{";
payload += "\"temperature\":" + String(temperature, 1) + ",";
payload += "\"humidity\":" + String(humidity, 1) + ",";
payload += "\"unit\":\"celsius\"";
payload += "}";
// Publish to MQTT
if (mqtt.publish(MQTT_PUB_TOPIC, payload.c_str())) {
Serial.print("Published: ");
Serial.println(payload);
} else {
Serial.println("ERROR: MQTT publish failed.");
}
}
void setup() {
Serial.begin(115200);
dht.begin();
connectWiFi();
mqtt.setServer(MQTT_BROKER, MQTT_PORT);
mqtt.setCallback(mqttCallback);
connectMQTT();
Serial.println("MQTT Temperature Logger ready.");
Serial.print("Publishing to topic: ");
Serial.println(MQTT_PUB_TOPIC);
Serial.print("Listening on topic: ");
Serial.println(MQTT_SUB_TOPIC);
}
void loop() {
// Reconnect if needed
connectWiFi();
if (!mqtt.connected()) {
connectMQTT();
}
mqtt.loop();
// Publish every 30 seconds
unsigned long now = millis();
if (now - lastPublish >= PUBLISH_INTERVAL) {
lastPublish = now;
publishSensorData();
}
}🔗Step 4: View the data with mosquitto_sub
Once the ESP32 is publishing, you can subscribe to the topic from any computer on the network. If you have the Mosquitto client tools installed:
mosquitto_sub -h test.mosquitto.org -t "esp32/sensor/data"You should see JSON messages arriving every 30 seconds:
{"temperature":23.5,"humidity":48.2,"unit":"celsius"}
{"temperature":23.4,"humidity":48.5,"unit":"celsius"}🔗Step 5: Send a command to the ESP32
You can also publish a command to the ESP32 from your computer:
mosquitto_pub -h test.mosquitto.org -t "esp32/command" -m "status"The ESP32 will respond by publishing {"status":"online"} to the sensor data topic.
🔗Step 6: Visualize with MQTT Explorer (optional)
MQTT Explorer is a free graphical tool that shows all topics on a broker as a tree. Connect it to test.mosquitto.org on port 1883 and navigate to esp32 > sensor > data to see your temperature and humidity values update in real time.
Because
test.mosquitto.orgis a public broker, you will also see messages from other users around the world. This is normal. For privacy, use a unique topic name or run your own broker.
🔗Understanding the JSON Payload
The ESP32 publishes data in JSON format, which is the standard for IoT messaging:
{"temperature":23.5,"humidity":48.2,"unit":"celsius"}The DHT22 sensor specifications:
| Parameter | Range | Accuracy |
|---|---|---|
| Temperature | $-40$ to $+80\,°\text{C}$ | $\pm 0.5\,°\text{C}$ |
| Humidity | $0$ to $100\,\%\,\text{RH}$ | $\pm 2\,\%\,\text{RH}$ |
| Sampling rate | Once every $2\,\text{s}$ | Minimum interval between reads |
🔗Common Issues and Solutions
| Problem | Cause | Fix |
|---|---|---|
| "Failed to read from DHT22" | Wiring issue or missing pull-up resistor | Check wiring. Ensure the 10k ohm pull-up is between DATA and VCC. DHT22 needs at least 2 seconds between reads |
| WiFi connects but MQTT fails | Broker unreachable or port blocked | Check internet connection. Try ping test.mosquitto.org from your computer. Some networks block port 1883 |
| MQTT connects but no data appears in mosquitto_sub | Topic name mismatch | Ensure the topic in the code matches exactly what you subscribe to (case-sensitive) |
| Messages appear sporadically | WiFi drops and reconnects | The reconnection logic handles this, but if your WiFi is unstable, check signal strength. Move closer to the router |
mqtt.state() returns -2 | Network connection failed | WiFi might be connected but internet access is blocked. Check router firewall settings |
| JSON looks malformed | Float values contain NaN | The DHT22 occasionally returns NaN. The code checks for this, but ensure the sensor is wired correctly |
| Readings arrive less often than every 30 seconds | mqtt.loop() is blocked or slow | Ensure nothing in loop() uses long delay() calls. The mqtt.loop() function must run frequently |