Pendahuluan

Selamat datang di artikel penutup seri ESP32 IoT Koding Indonesia! Sejauh ini kamu sudah belajar:

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 (#6) dan publish MQTT (#7). Paham juga koneksi WiFi ESP32 (#4) dan wiring DHT22 (#5).

Arsitektur Proyek

DHT22 · GPIO 4 suhu · kelembaban · tiap 2 dtk ESP32 — WebServer + PubSubClient satu variabel sensor · dua saluran lokal remote Web Server :80 / · /api/data browser WiFi rumah MQTT :1883 .../dht22/data publish tiap 10 dtk Teknisi lapangan dashboard HTML cepat MQTT Explorer pantau dari mana saja
Capstone Seri 1: UI lokal (#6) + telemetri MQTT (#7) — lanjut hemat baterai di Deep Sleep (#11).
SaluranCara aksesFungsi
Web Server (lokal)http://IP_ESP32/ di WiFi rumahDashboard HTML + API /api/data
MQTT (remote)test.mosquitto.org:1883Publish JSON ke kodingindonesia/esp32/dht22/data tiap 10 detik

Topic MQTT sama dengan proyek gabungan (#9) agar konsisten di seluruh seri.

Catatan topik: Di artikel MQTT (#7) kita pakai kodingindonesia/esp32/dht22 (payload teks). Dari artikel gabungan ke dashboard ini kita pakai subtopic .../dht22/data dengan payload JSON — pola yang sama dengan artikel #9.

Broker bukan websitetest.mosquitto.org tidak 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 placeholder GANTI_SSID_WIFI / GANTI_PASSWORD_WIFI, lalu upload. Untuk produksi tanpa hardcode, lihat NVS + WiFiManager (#12).

#include <WiFi.h>
#include <WebServer.h>
#include <PubSubClient.h>
#include <DHT.h>

const char* ssid     = "GANTI_SSID_WIFI";
const char* password = "GANTI_PASSWORD_WIFI";

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(512);
  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();
  delay(2000);

  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

  1. Upload kode, buka Serial Monitor (115200 baud)
  2. Catat IP, misalnya 192.168.1.100
  3. Buka http://192.168.1.100 di browser (WiFi sama)
  4. Cek API: http://192.168.1.100/api/data
  5. Refresh browser untuk nilai terbaru (dashboard statis per request — auto-refresh bisa ditambah dengan JavaScript)

2. Saluran MQTT (remote)

  1. Buka MQTT Explorer → test.mosquitto.org:1883
  2. Cari topic kodingindonesia/esp32/dht22/data
  3. Pastikan JSON suhu/kelembaban masuk setiap ~10 detik

Alternatif: mosquitto_sub (Terminal)

mosquitto_sub -h test.mosquitto.org -t "kodingindonesia/esp32/dht22/data" -v

Tool CLI dari paket Mosquitto — Linux, Mac, dan Windows setelah install Mosquitto.

Alur Program

  1. bacaSensor() — baca DHT22 tiap 2 detik, simpan ke variabel global
  2. server.handleClient() — layani dashboard & API saat ada request HTTP
  3. mqttClient.loop() — jaga koneksi MQTT tetap hidup
  4. publishMQTT() — 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 di loop().
  • rc=-2 / rc=4 MQTT: Broker tidak terjangkau atau masalah auth — test.mosquitto.org:1883 tanpa password.
  • Nilai suhu NaN di dashboard: Cek wiring DHT22, pull-up 10kΩ, dan delay(2000) setelah dht.begin().
  • Web lambat saat MQTT reconnect: Normal di jaringan WiFi rumah — 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/#.

Indeks Seri ESP32 IoT (10 Artikel)

  1. Mengenal ESP32 (#1)
  2. Install Arduino IDE & ESP32 Board (#2)
  3. Blink LED — Tutorial Pertama (#3)
  4. ESP32 ke WiFi & Kirim Data (#4)
  5. Baca Sensor DHT22 (#5)
  6. Web Server Monitoring DHT22 (#6)
  7. MQTT — Kirim Data ke Broker (#7)
  8. Kontrol Lampu via MQTT & Relay (#8)
  9. Gabungan DHT22 + Relay MQTT (#9)
  10. Dashboard Web Server + MQTT (#10) — artikel ini (capstone)

Roadmap Belajar Selanjutnya — Seri 2

Seri 10 artikel ini adalah fondasi capstone. Seri 2 ESP32/IoT Lanjutan sudah lengkap — dua puluh sembilan artikel (urutan publish):

  1. Deep sleep ESP32 + DHT22 hemat baterai (#11)
  2. NVS + WiFiManager (#12) — konfigurasi tanpa hardcode
  3. Broker Mosquitto pribadi + autentikasi (#16)
  4. I2C + sensor BME280 (#13) — suhu, kelembaban & tekanan
  5. OLED SSD1306 (#14) — tampilkan data di layar I2C
  6. OTA update firmware ESP32 (#15) — update tanpa kabel USB
  7. Home Assistant + ESP32 MQTT (#21) — dashboard smart home
  8. ESPHome flash ESP32 tanpa Arduino (#22) — YAML + OTA
  9. Node-RED dashboard IoT MQTT (#23) — otomasi visual
  10. Sensor gerak PIR + lampu MQTT (#24) — automasi koridor dengan debounce
  11. MQTT TLS + QoS + LWT (#17) — amankan broker Mosquitto
  12. NTP & timestamp ESP32 (#34) — waktu akurat untuk log sensor MQTT
  13. Subscriber Python → MySQL (#18) — histori sensor MQTT ke database
  14. InfluxDB + Grafana (#19) — dashboard grafik histori sensor
  15. REST API vs MQTT (#20) — kapan pakai HTTP vs push MQTT
  16. ESP-NOW antar ESP32 tanpa router WiFi (#25) — peer-to-peer tanpa router
  17. LoRa ESP32 + SX1278 jarak jauh (#26) — sensor kilometer tanpa WiFi
  18. ESP32-CAM MJPEG streaming (#27) — live video & capture foto via WiFi
  19. Gateway LoRa → MQTT (#28) — sensor jarak jauh ke dashboard Grafana
  20. Migrasi ke PlatformIO di VS Code (#29) — project ESP32 lebih rapi dengan platformio.ini
  21. ESP32 + Firebase Realtime Database (#30) — sensor ke cloud tanpa server sendiri
  22. FreeRTOS multi-task Sensor + WiFi + MQTT (#31) — pecah firmware jadi task paralel
  23. Bluetooth BLE kirim data sensor ke smartphone (#32) — GATT server tanpa WiFi
  24. Kontrol Servo & PWM gerakan presisi via MQTT (#33) — SG90 sudut 0–180°
  25. ADC ESP32: Soil Moisture & LDR via MQTT (#35) — sensor analog tanah & cahaya
  26. ESP8266 / NodeMCU vs ESP32 (#36) — kapan pakai board murah vs upgrade
  27. SD Card & SPI logging offline (#37) — backup data sensor di lapangan
  28. HTTPS & sertifikat ESP32 (WiFiClientSecure) (#38) — REST API aman ke cloud
  29. Capstone Smart Greenhouse (#39) — multi-sensor + aktuator + dashboard MQTT

Seri 2 resmi selesai (29/29) — indeks lengkap juga ada di capstone #39.

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.