Sending real-time Temperature & Humidity data from a DHT22 sensor to ThingSpeak using an ESP32 development board.
In this project, we build on the basic ThingSpeak account setup from the previous tutorial and create a real IoT application: an ESP32 board reads temperature and humidity values from a DHT22 sensor and uploads them to a ThingSpeak channel every 15 seconds, where they can be visualized as live charts.
The ESP32 connects to a local Wi-Fi network, reads sensor data, and sends it via an HTTP GET request to the ThingSpeak API.
| Component | Purpose |
|---|---|
| ESP32 Development Board | Main microcontroller with built-in Wi-Fi |
| DHT22 (AM2302) Sensor | Measures temperature and humidity |
| Jumper Wires | Connects sensor to ESP32 |
| ThingSpeak Account | Free cloud platform to store and visualize IoT data |
Only one external library is required for this project โ the DHT sensor library. No HTTP or ThingSpeak-specific library is required because we send data manually using simple HTTP GET requests.
HTTPClient library, exactly as shown in this tutorial.
The DHT22 sensor requires only 3 connections to the ESP32: power, ground, and one data pin.
| DHT22 Pin | ESP32 Pin |
|---|---|
| VCC | 3.3V |
| GND | GND |
| DATA | GPIO 27 |
#define DHTPIN 27. If you wire the
sensor to a different GPIO, update this value accordingly.
Before uploading the code, prepare your ThingSpeak channel:
This project uses only Field 1 (Temperature) and Field 2 (Humidity), even though a channel can support up to 8 fields.
Three libraries are used: Wi-Fi connectivity, HTTP requests, and DHT sensor communication.
#include <WiFi.h> #include <HTTPClient.h> #include "DHT.h"
We specify which GPIO pin the sensor's data line is connected to, and which DHT model we're using (DHT22).
#define DHTPIN 27 #define DHTTYPE DHT22 DHT dht(DHTPIN, DHTTYPE);
The SSID and password connect the board to your local network. The serverName string is the ThingSpeak "update" endpoint containing your unique Write API Key.
const char* ssid = "Robotics Lab"; const char* password = "Robotics@321"; String serverName = "https://api.thingspeak.com/update?api_key=XXNUJDNUCHY4FTYU";
serverName with your own Write API Key from your ThingSpeak channelโs "API Keys" tab.
The board attempts to connect repeatedly until successful, printing dots to the Serial Monitor as feedback, then prints the assigned local IP address once connected.
Serial.begin(9600); WiFi.begin(ssid, password); while(WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(WiFi.localIP()); dht.begin();
Inside the main loop, the program checks the Wi-Fi status. If connected, it reads temperature/humidity, builds the request URL, and sends it via HTTP GET.
if(WiFi.status() == WL_CONNECTED) { HTTPClient http; float h = dht.readHumidity(); float t = dht.readTemperature(); String url = serverName + "&field1=" + t + "&field2=" + h; http.begin(url.c_str()); int httpResponseCode = http.GET(); if (httpResponseCode > 0) { Serial.println(httpResponseCode); } http.end(); delay(15000); }
delay(15000) is placed at the end of each successful upload cycle โ sending
faster than this will cause updates to be silently ignored.
If Wi-Fi is not connected, the board prints a "WiFi Disconnected" message with a short 1-second delay, rather than the full 15 seconds, so reconnection status feedback still refreshes at a fast pace.
else { Serial.println("WiFi Disconnected"); delay(1000); }
// Import necessary libraries #include <WiFi.h> #include <HTTPClient.h> #include "DHT.h" #define DHTPIN 27 // dht connection #define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321 DHT dht(DHTPIN, DHTTYPE); // Set our wifi name and password const char* ssid = "Robotics Lab"; const char* password = "Robotics@321"; // Your thingspeak channel url with api_key query String serverName = "https://api.thingspeak.com/update?api_key=XXNUJDNUCHY4FTYU"; // please replace with your write API Key void setup() { Serial.begin(9600); WiFi.begin(ssid, password); Serial.println("Connecting"); while(WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.print("Connected to WiFi network with IP Address: "); Serial.println(WiFi.localIP()); delay(2000); dht.begin(); } void loop() { if(WiFi.status()== WL_CONNECTED) { HTTPClient http; float h = dht.readHumidity(); float t = dht.readTemperature(); Serial.print("Temperature = "); Serial.println(t); Serial.print("Humidity = "); Serial.println(h); Serial.println("*****************************************"); String url = serverName + "&field1=" + t + "&field2=" + h; http.begin(url.c_str()); int httpResponseCode = http.GET(); if (httpResponseCode > 0) { Serial.print("HTTP Response code: "); Serial.println(httpResponseCode); } else { Serial.print("Error code: "); Serial.println(httpResponseCode); } http.end(); delay(15000); } else { Serial.println("WiFi Disconnected"); delay(1000); } }
Follow these steps in the Arduino IDE:
Connecting..... Connected to WiFi network with IP Address: 192.168.1.45 Temperature = 26.70 Humidity = 58.30 ***************************************** HTTP Response code: 200
Once data starts arriving, ThingSpeak automatically generates two default charts โ one per field โ labeled "Field 1 Chart" and "Field 2 Chart". You can rename these fields (e.g. "Room Temperature" and "Room Humidity") and customize each chart's appearance from the channel settings.
| Setting | Description |
|---|---|
| Title | Custom name shown above the chart |
| X-Axis Label | Typically set to "Time" |
| Y-Axis Label | Typically set to "Value", or the unit (ยฐC, %) |
| Time Scale | Controls how much historical data is displayed |
| Type | Line, bar, or column style |
delay(15000) (or slightly higher) to avoid dropped data points.
This project can be extended further, such as:
| Idea | Benefit |
|---|---|
| Add a gauge widget | Real-time visual indicator of current values |
| Export data to CSV | Download historical logs for analysis |
| Add alerts | Get notified when temperature/humidity crosses a threshold |
| MATLAB Analysis | Run analytics directly within ThingSpeak |