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 dan publish MQTT. Paham juga koneksi WiFi ESP32 dan wiring DHT22.

Arsitektur Proyek

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 agar konsisten di seluruh seri.

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 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

  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 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.