Pendahuluan
Selamat datang di artikel penutup seri ESP32 IoT Koding Indonesia! Sejauh ini kamu sudah belajar:
Dashboard lokal lewat Web Server (artikel web server)
Telemetri remote lewat MQTT (artikel MQTT)
Proyek gabungan sensor + aktuator (artikel DHT22 + relay)
Kali ini kita satukan dua saluran monitoring dalam satu firmware:
Lokal — buka IP ESP32 di browser rumah (cepat, tanpa internet)
Remote — publish suhu ke broker MQTT (bisa dipantau dari mana saja lewat MQTT Explorer)
Ini pola nyata di IoT: edge device punya UI lokal untuk teknisi di lapangan, sekaligus mengirim data ke sistem pusat.
Yang Kamu Butuhkan
ESP32 DevKit + sensor DHT22 (GPIO 4, pull-up 10kΩ)
Kabel jumper
Library: DHT sensor library, PubSubClient (WebServer & WiFi built-in)
HP/laptop di WiFi yang sama + MQTT Explorer
Prasyarat: Sudah pernah membuat Web Server ESP32 dan publish MQTT. Paham juga koneksi WiFi ESP32 dan wiring DHT22.
Arsitektur Proyek
| Saluran | Cara akses | Fungsi |
|---|---|---|
| Web Server (lokal) | http://IP_ESP32/ di WiFi rumah | Dashboard HTML + API /api/data |
| MQTT (remote) | test.mosquitto.org:1883 | Publish JSON ke kodingindonesia/esp32/dht22/data tiap 10 detik |
Topic MQTT sama dengan proyek gabungan agar konsisten di seluruh seri.
Broker bukan website —
test.mosquitto.orgtidak dibuka di browser. Gunakan MQTT Explorer atau ESP32.
Keamanan: Web server hanya untuk jaringan lokal — jangan expose ke internet tanpa autentikasi. Broker publik hanya untuk belajar; pakai topic unik dan jangan kirim data sensitif.
Kode Lengkap: Web Server + MQTT Publish
Ganti ssid dan password, lalu upload:
#include <WiFi.h>
#include <WebServer.h>
#include <PubSubClient.h>
#include <DHT.h>
const char* ssid = "NamaWiFiKamu";
const char* password = "PasswordWiFiKamu";
const char* mqttServer = "test.mosquitto.org";
const int mqttPort = 1883;
const char* topicSensor = "kodingindonesia/esp32/dht22/data";
#define DHT_PIN 4
#define DHT_TYPE DHT22
DHT dht(DHT_PIN, DHT_TYPE);
WebServer server(80);
WiFiClient espClient;
PubSubClient mqttClient(espClient);
float suhuTerakhir = 0;
float kelembabanTerakhir = 0;
unsigned long waktuBacaTerakhir = 0;
unsigned long terakhirPublish = 0;
const unsigned long intervalBaca = 2000;
const unsigned long intervalPublish = 10000;
String halamanDashboard() {
String html = "<!DOCTYPE html><html lang='id'><head><meta charset='UTF-8'>";
html += "<meta name='viewport' content='width=device-width,initial-scale=1'>";
html += "<title>ESP32 IoT Dashboard</title><style>";
html += "body{font-family:system-ui,sans-serif;max-width:480px;margin:40px auto;padding:20px;background:#f0f4f8;}";
html += "h1{color:#2979FF;font-size:1.4rem;} .card{background:#fff;border:3px solid #000;";
html += "box-shadow:4px 4px 0 #000;padding:20px;margin:12px 0;}";
html += ".nilai{font-size:2.2rem;font-weight:800;} .label{color:#666;font-size:0.85rem;text-transform:uppercase;}";
html += "</style></head><body><h1>ESP32 IoT Dashboard</h1>";
html += "<div class='card'><div class='label'>Suhu</div><div class='nilai'>";
html += String(suhuTerakhir, 1) + " °C</div></div>";
html += "<div class='card'><div class='label'>Kelembaban</div><div class='nilai'>";
html += String(kelembabanTerakhir, 1) + " %</div></div>";
html += "<p style='font-size:0.8rem;color:#888;'>Lokal · Koding Indonesia</p>";
html += "</body></html>";
return html;
}
void handleAPI() {
String json = "{\"suhu\":" + String(suhuTerakhir, 1);
json += ",\"kelembaban\":" + String(kelembabanTerakhir, 1) + "}";
server.send(200, "application/json", json);
}
void bacaSensor() {
if (millis() - waktuBacaTerakhir < intervalBaca) return;
waktuBacaTerakhir = millis();
float h = dht.readHumidity();
float t = dht.readTemperature();
if (!isnan(h) && !isnan(t)) {
kelembabanTerakhir = h;
suhuTerakhir = t;
}
}
void koneksiWiFi() {
Serial.print("Menghubungkan ke WiFi");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi terhubung!");
Serial.print("Dashboard lokal: http://");
Serial.println(WiFi.localIP());
}
void koneksiMQTT() {
mqttClient.setServer(mqttServer, mqttPort);
mqttClient.setBufferSize(256);
while (!mqttClient.connected()) {
Serial.print("MQTT connect...");
String clientId = "ESP32-Dash-" + String(random(0xffff), HEX);
if (mqttClient.connect(clientId.c_str())) {
Serial.println(" OK");
} else {
Serial.print(" gagal rc=");
Serial.println(mqttClient.state());
delay(5000);
}
}
}
void publishMQTT() {
if (waktuBacaTerakhir == 0) return;
if (isnan(suhuTerakhir) || isnan(kelembabanTerakhir)) return;
String payload = "{\"suhu\":" + String(suhuTerakhir, 1);
payload += ",\"kelembaban\":" + String(kelembabanTerakhir, 1) + "}";
if (mqttClient.publish(topicSensor, payload.c_str())) {
Serial.print("MQTT publish: ");
Serial.println(payload);
}
}
void setup() {
Serial.begin(115200);
randomSeed(micros());
dht.begin();
koneksiWiFi();
server.on("/", []() { server.send(200, "text/html", halamanDashboard()); });
server.on("/api/data", handleAPI);
server.onNotFound([]() { server.send(404, "text/plain", "404"); });
server.begin();
Serial.println("Web Server aktif port 80");
koneksiMQTT();
delay(2000);
}
void loop() {
if (WiFi.status() != WL_CONNECTED) {
koneksiWiFi();
}
server.handleClient();
bacaSensor();
if (!mqttClient.connected()) {
koneksiMQTT();
}
mqttClient.loop();
unsigned long sekarang = millis();
if (sekarang - terakhirPublish >= intervalPublish) {
terakhirPublish = sekarang;
publishMQTT();
}
}Uji Coba
1. Dashboard lokal
Upload kode, buka Serial Monitor (115200 baud)
Catat IP, misalnya
192.168.1.100Buka
http://192.168.1.100di browser (WiFi sama)Cek API:
http://192.168.1.100/api/dataRefresh browser untuk nilai terbaru (dashboard statis per request — auto-refresh bisa ditambah dengan JavaScript)
2. Saluran MQTT (remote)
Buka MQTT Explorer →
test.mosquitto.org:1883Cari topic
kodingindonesia/esp32/dht22/dataPastikan JSON suhu/kelembaban masuk setiap ~10 detik
Alternatif: mosquitto_sub (Terminal)
mosquitto_sub -h test.mosquitto.org -t "kodingindonesia/esp32/dht22/data" -vTool CLI dari paket Mosquitto — Linux, Mac, dan Windows setelah install Mosquitto.
Alur Program
bacaSensor()— baca DHT22 tiap 2 detik, simpan ke variabel globalserver.handleClient()— layani dashboard & API saat ada request HTTPmqttClient.loop()— jaga koneksi MQTT tetap hiduppublishMQTT()— kirim JSON ke broker tiap 10 detik
Kedua saluran membaca variabel sensor yang sama — tidak ada pembacaan ganda yang bentrok.
Tips & Troubleshooting
Browser tidak bisa buka IP ESP32: Pastikan HP/laptop satu WiFi dengan ESP32. Cek IP di Serial Monitor.
MQTT publish gagal tapi web OK: Cek internet router.
mqttClient.loop()harus dipanggil diloop().rc=-2 / rc=4 MQTT: Broker tidak terjangkau atau masalah auth —
test.mosquitto.org:1883tanpa password.Nilai suhu NaN di dashboard: Cek wiring DHT22, pull-up 10kΩ, dan
delay(2000)setelahdht.begin().Web lambat saat MQTT reconnect: Normal di shared hosting WiFi — reconnect MQTT pakai
delay(5000)di dalam blocking loop; untuk produksi pertimbangkan non-blocking reconnect.Topic tidak muncul: Case-sensitive. Coba subscribe wildcard
kodingindonesia/esp32/#.
Roadmap Belajar Selanjutnya
Seri 10 artikel ESP32 IoT ini sudah mencakup dasar hingga proyek gabungan. Untuk naik level:
Home Assistant — hubungkan sensor MQTT + switch relay
Broker Mosquitto pribadi di Raspberry Pi / VPS dengan autentikasi
Deep sleep ESP32 untuk node sensor baterai
OTA update firmware ESP32 tanpa kabel USB
Simpan histori sensor ke InfluxDB / MySQL via subscriber Python
Terima kasih sudah mengikuti seri ini! Kamu sekarang punya fondasi solid: sensor → WiFi → web lokal → MQTT → aktuator → dashboard hybrid. Share proyek kamu dan pantau terus artikel baru di Koding Indonesia.