ESP32: serve a sensor dashboard from the chip itself
Turn your ESP32 into a tiny web server that shows live sensor readings on any phone or laptop on the same Wi-Fi. No cloud, no app.
Here is the project I send people when they ask “what is the point of an ESP32, really.” You read a sensor, the ESP32 serves a small HTML page on your local Wi-Fi, and any phone on the same network can see the live readings.
No cloud account. No MQTT broker. No app. Just one chip and one URL.
What you need
- ESP32 dev board
- Any sensor that works over I2C or SPI (this tutorial uses the BME280, but a BMP280 or even a DHT22 on a separate GPIO works too)
- USB cable
Install the libraries
In the Arduino IDE:
Sketch>>Include Library>>Manage Libraries>> installWebServer(built in, no install needed) andAdafruit BME280.
For BME280 specifically, also install Adafruit Unified Sensor.
The code
#include <WiFi.h>
#include <WebServer.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
const char* ssid = "your-wifi-ssid";
const char* password = "your-wifi-password";
WebServer server(80);
Adafruit_BME280 bme;
float temperature = 0;
float humidity = 0;
float pressure = 0;
void handleRoot() {
String html = "<!DOCTYPE html><html><head>";
html += "<meta charset='utf-8'>";
html += "<meta http-equiv='refresh' content='5'>";
html += "<title>ESP32 sensor dashboard</title>";
html += "<style>body{font-family:sans-serif;background:#0f1115;color:#e6e9ef;"
"display:flex;flex-direction:column;align-items:center;padding:2rem;}"
".card{background:#161a22;padding:1.5rem 2rem;margin:0.5rem;border-radius:6px;"
"min-width:220px;text-align:center;}"
".value{font-size:2.5rem;color:#4ea1ff;}</style></head><body>";
html += "<h1>ESP32 sensor dashboard</h1>";
html += "<div class='card'><div class='value'>" + String(temperature, 1) + " °C</div>"
"<div>temperature</div></div>";
html += "<div class='card'><div class='value'>" + String(humidity, 1) + " %</div>"
"<div>humidity</div></div>";
html += "<div class='card'><div class='value'>" + String(pressure, 0) + " hPa</div>"
"<div>pressure</div></div>";
html += "</body></html>";
server.send(200, "text/html", html);
}
void setup() {
Serial.begin(115200);
Wire.begin();
if (!bme.begin(0x76)) {
Serial.println("Could not find BME280. Check I2C address (0x76 vs 0x77).");
while (1);
}
WiFi.begin(ssid, password);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Connected. IP: ");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.begin();
}
void loop() {
server.handleClient();
temperature = bme.readTemperature();
humidity = bme.readHumidity();
pressure = bme.readPressure() / 100.0;
}
Upload it, open Serial Monitor, and look for the IP address. It looks like
192.168.1.42. Open that in any browser on the same network.
The HTML uses
<meta http-equiv='refresh' content='5'>to reload the page every 5 seconds. That works on any device, no JavaScript needed. If you want smoother updates, the book version uses WebSockets.
Why the I2C address might be 0x77 instead of 0x76
Some BME280 boards have SDO tied to VCC instead of GND, which changes the
address. If bme.begin(0x76) fails, try bme.begin(0x77). If both fail, run
an I2C scanner sketch and find the actual address.
Things that will trip you up
- Wrong Wi-Fi credentials. The Serial Monitor will print
Connecting...forever. Editssidandpassword. - 5GHz Wi-Fi. The ESP32 only does 2.4GHz. If your router has both bands and they have the same SSID, force the device onto the 2.4GHz band in the router settings.
- Captive portals. Coffee shop Wi-Fi and most guest networks block direct connections. Test this at home first.
- The BME280 is not on the I2C pins you think. On most ESP32 dev boards,
SDA is GPIO 21 and SCL is GPIO 22. That is the default for
Wire.begin(), which is what this sketch uses.
What you just built
A self-contained sensor dashboard that:
- Boots in about 3 seconds.
- Survives a power cycle without any setup.
- Works on any phone, laptop, or tablet on the same network.
- Sends zero data to any third party.
This is the foundation for a lot of home automation projects. The next steps are usually adding more sensors, logging to MQTT, or replacing the HTML refresh with WebSockets for smoother updates. All of those are in the books.
What to build next
- The LittleFS web server tutorial replaces string-built HTML.
- The MQTT tutorial moves the data to a broker.
- The book IoT with ESP32 bundles the connectivity tutorials.