PROJECT 9 · SENSOR + WEB

DHT11 Temperature & Humidity Web Server

Read a DHT11 and show live temperature & humidity on a page that refreshes every 10 seconds.

⬇ Download the sketch Save Project_9_ESP32_DHT11_Web_Server.ino, then open it in the Arduino IDE (setup: Foundation 3). Needs the DHT, Adafruit Unified Sensor, ESPAsyncWebServer + AsyncTCP libraries and your Wi‑Fi credentials.

🎯 Objective

By the end of this project, you will:

1 · The DHT11 sensor & auto‑updating pages

The DHT11 is a small, slow, cheap sensor that reports temperature (°C) and relative humidity (%) over one data wire. Read it every couple of seconds: no faster.

To refresh without reloading, the browser polls (like Project 8): two timers fetch /temperature and /humidity every 10 s and drop the numbers into the page:

setInterval(function () {
  xhttp.open("GET", "/temperature", true);   // fetch just the number
  ...
}, 10000);                                     // every 10 seconds

2 · Install the required libraries

The first two are in the Library Manager; the async pair install as ZIPs (as in Projects 7–8).

3 · Parts & wiring

QtyPartNotes
1ESP32 · breadboard · jumper wires-
1DHT11 module3‑pin: −, +, S
DHT11: S to GPIO 4, + to 3.3 V, − to GND
S → GPIO 4 · + → 3.3 V · − → GND.
This kit's DHT11 is a 3‑pin module with the pull‑up built in, so it runs straight off 3.3 V: no extra resistor.

4 · Upload & use

Set your Wi‑Fi credentials, upload, open Serial Monitor at 115200, press EN/RESET:

==============================================
 Project 9: ESP32 DHT11 Web Server
==============================================
DHT11 data -> GPIO 4
Connecting to Wi-Fi: MyNetwork
....
Wi-Fi connected!
Open this address in your browser:  http://192.168.1.42
----------------------------------------------
[DHT] Temperature: 21.4 C
[DHT] Humidity: 63.9 %

The improved logging prints labeled readings with units (the original printed a bare number), so you can confirm the sensor works before opening the page.

Internet note: the page pulls its thermometer/drop icons from a font CDN, so the viewing device needs internet for the icons (the numbers work regardless).

5 · Code Walkthrough: Understanding the Sketch

The async server again, with a sensor behind it. Here is what is new.

Step 1: Telling the library which sensor, on which pin

#include <Adafruit_Sensor.h>
#include <DHT.h>

#define DHTPIN 4          // data pin
#define DHTTYPE DHT11     // the family member we have

DHT dht(DHTPIN, DHTTYPE);

DHT dht(DHTPIN, DHTTYPE) creates an object: a bundle of the pin number, the sensor type, and the code that talks to it. From here on you ask dht for readings rather than handling the pin yourself.

That library is doing real work. The DHT11 speaks a one-wire protocol where bits are encoded as pulses of different lengths, so reading it by hand means timing pulses to the microsecond. dht.readTemperature() hides all of it.

Step 2: Reading safely, with a fallback

String readDHTTemperature() {
  float t = dht.readTemperature();       // Celsius by default
  if (isnan(t)) {
    Serial.println("[DHT] Failed to read temperature!");
    return "--";                         // show a dash, not a wrong number
  }
  return String(t);
}

isnan() asks "is this Not a Number?" A DHT11 read fails now and then, from a loose wire or from being asked too often, and the library reports that by returning NaN. Checking for it is what keeps -- on the page instead of a meaningless value.

Both readers return String rather than float, because their output goes straight into a web page. dht.readTemperature(true) returns Fahrenheit.

Step 3: Placeholders in the page, filled at send time

<span id="temperature">%TEMPERATURE%</span>
<span id="humidity">%HUMIDITY%</span>
String processor(const String& var){
  if (var == "TEMPERATURE") return readDHTTemperature();
  else if (var == "HUMIDITY")  return readDHTHumidity();
  return String();
}

Same mechanism as Project 7, used for data instead of controls. The first page you load therefore already shows real values, with no wait.

Step 4: Two endpoints, and a page that refreshes itself

server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/plain", readDHTTemperature().c_str());
});
setInterval(function () {
  ...
  document.getElementById("temperature").innerHTML = this.responseText;
  xhttp.open("GET", "/temperature", true);
}, 10000);

Exactly Project 8's polling pattern, with two differences: each value has its own endpoint returning plain text, and the interval is 10 seconds rather than 1. The DHT11 produces a fresh sample only about once a second and is not a fast-moving quantity, so polling harder would gain nothing. innerHTML replaces just the contents of one <span>, which is why the figures change while the rest of the page sits still.

Step 5: an empty loop() again

Readings are taken on demand, when a browser asks. If you wanted logging every minute regardless of visitors, that is where a millis() timer from Project 4 would go.

Key concepts: what each line really does

CodeWhat it does
DHT dht(DHTPIN, DHTTYPE)Creates the sensor object holding the pin, the type, and the protocol code.
dht.begin()Prepares the pin and the library. Like Serial.begin(), it belongs in setup().
dht.readTemperature()Returns degrees Celsius as a float, already converted. Pass true for Fahrenheit.
dht.readHumidity()Returns relative humidity in percent.
isnan(t)Detects a failed reading. The library returns NaN on error, and NaN equals nothing, so it needs its own test.
String(t)Turns the number into text for the web page.
%TEMPERATURE% + processor()Placeholder and the function that fills it, so the first page load already carries real values.
send_P(200, "text/plain", ...)Replies with a bare number, no HTML: all the JavaScript needs.
innerHTML = this.responseTextSwaps the text inside one element, leaving the rest of the page untouched.
setInterval(..., 10000)Ten seconds between polls, matched to how fast a DHT11 can actually produce new data.

6 · Demonstration

Phone showing the ESP32 DHT Server with temperature and humidity
Readings update on their own: breathe near the sensor and watch humidity rise.

7 · Wrapping Up: What You've Learned

This is the first project that measures the world and publishes it, which is what most IoT devices spend their lives doing.

8 · Troubleshooting

SymptomLikely causeFix
Reads -- / "Failed to read"Signal not on GPIO 4 / bad wiringS → GPIO 4, + → 3.3 V, − → GND
Compile error (DHT/sensor)Libraries missingInstall DHT + Adafruit Unified Sensor
Compile error (async)Async libs missingInstall ESPAsyncWebServer + AsyncTCP
Icons missing on pageNo internet on the phoneNumbers still work; give the device internet
Values never changeDHT11 is slowWait ~10 s between updates
← Project 8 · Output Sync Project 10 · OLED Display →