Pendahuluan
Di artikel WiFi ESP32 dan node deep sleep DHT22, kita masih menulis ssid dan password langsung di sketch. Itu cepat untuk belajar, tapi tidak layak produksi: setiap ganti WiFi atau deploy ke pelanggan lain, kamu harus edit kode, compile ulang, dan upload via USB.
Artikel ini mengajarkan dua fondasi firmware ESP32 yang wajib untuk proyek lapangan:
- WiFiManager — portal konfigurasi WiFi lewat hotspot captive (tanpa Serial Monitor)
- NVS Preferences — simpan pengaturan (topic MQTT, interval, flag) di flash secara persisten
Kita gabungkan keduanya dalam satu sketch: baca DHT22, publish MQTT JSON (topic Seri 1), tanpa satu pun kredensial WiFi di source code.
Prasyarat: Paham koneksi WiFi, DHT22, dan publish MQTT. Familiar dengan deep sleep membantu untuk node baterai nanti.
Masalah Hardcode WiFi
| Skenario | Tanpa WiFiManager | Dengan WiFiManager |
|---|---|---|
| Pindah rumah / kantor | Edit sketch + upload USB | Buka portal, pilih WiFi baru |
| Deploy ke banyak unit | Satu firmware per lokasi | Satu firmware universal |
| Password WiFi di GitHub | Risiko bocor | Tidak ada password di repo |
| Node di atap / kebun | Harus bawa laptop | Setup lewat HP saja |
Di artikel WiFi ESP32 (#4) sudah disebutkan: gunakan WiFiManager atau file konfigurasi terpisah — ini janji yang kita penuhi di artikel Seri 2. Kali ini kita implementasi lengkapnya.
NVS (Non-Volatile Storage) & Preferences
ESP32 punya partisi flash bernama NVS untuk menyimpan key-value yang tetap ada setelah reboot atau deep sleep. Di Arduino, library Preferences adalah wrapper resmi:
prefs.begin("namespace")— buka namespace (misalnya"kindo")prefs.putString("mqtt_topic", ...)— simpan stringprefs.getString("mqtt_topic", default)— baca dengan nilai defaultprefs.clear()— hapus semua key di namespace
WiFiManager sendiri sudah menyimpan kredensial WiFi ke NVS internal. Kita pakai Preferences tambahan untuk parameter aplikasi: topic MQTT, durasi deep sleep (opsional), nama perangkat.
WiFiManager: Alur Portal Captive
- ESP32 tidak menemukan WiFi tersimpan → buat AP
KindoESP32-Setup - HP/laptop connect ke AP tersebut → browser terbuka halaman konfigurasi
- Pilih SSID rumah, masukkan password, isi field custom (topic MQTT)
- ESP32 simpan ke flash, reboot, connect ke WiFi rumah
- Boot berikutnya langsung connect — portal tidak muncul lagi
Reset konfigurasi: Tahan tombol BOOT (GPIO 0) saat boot, atau panggil wm.resetSettings() + prefs.clear() di kode maintenance.
Komponen & Wiring
Sama seperti tutorial DHT22 Seri 1 — sensor digital di GPIO 4:
ESP32 DevKit DHT22
───────────── ─────
3.3V ─────── VCC
GND ─────── GND
GPIO 4 ─────── DATA
│
[10kΩ] pull-up ke 3.3V (modul breakout biasanya sudah ada)
- ESP32 DevKit (USB untuk upload pertama)
- Sensor DHT22 + kabel jumper
- HP Android/iOS untuk portal WiFiManager
Install Library
Di Arduino IDE 2.x → Sketch → Include Library → Manage Libraries:
- WiFiManager oleh tzapu (versi 2.x)
- DHT sensor library oleh Adafruit + dependency Adafruit Unified Sensor
- PubSubClient oleh Nick O'Leary
Board: esp32 by Espressif (v3.x). Library Preferences dan WiFi sudah built-in.
Broker latihan: test.mosquitto.org:1883 (sama Seri 1).
Topic default: kodingindonesia/esp32/dht22/data — payload JSON {"suhu":28.5,"kelembaban":65.2} (bisa diubah lewat portal).
Broker bukan website:
test.mosquitto.orgtidak dibuka di browser. Pakai MQTT Explorer ataumosquitto_sub. Detail di artikel MQTT.
Kode Lengkap: WiFiManager + NVS + DHT22 + MQTT
Tidak ada const char* ssid / password di bawah. Ganti default topic jika perlu; sisanya diatur lewat portal.
#include <WiFi.h>
#include <WiFiManager.h>
#include <Preferences.h>
#include <PubSubClient.h>
#include <DHT.h>
#define DHT_PIN 4
#define DHT_TYPE DHT22
#define BTN_RESET_WIFI 0 // BOOT — tahan saat power-on untuk reset WiFi
const char* NS_KINDO = "kindo";
const char* DEFAULT_TOPIC = "kodingindonesia/esp32/dht22/data";
const char* MQTT_HOST = "test.mosquitto.org";
const int MQTT_PORT = 1883;
DHT dht(DHT_PIN, DHT_TYPE);
WiFiClient espClient;
PubSubClient mqttClient(espClient);
Preferences prefs;
String topicSensor;
WiFiManagerParameter paramTopic(
"mqtt_topic", "MQTT topic sensor", DEFAULT_TOPIC, 64);
bool tombolResetDitekan() {
pinMode(BTN_RESET_WIFI, INPUT_PULLUP);
return digitalRead(BTN_RESET_WIFI) == LOW;
}
void muatPengaturan() {
prefs.begin(NS_KINDO, true);
topicSensor = prefs.getString("mqtt_topic", DEFAULT_TOPIC);
prefs.end();
}
void simpanTopicDariPortal() {
prefs.begin(NS_KINDO, false);
prefs.putString("mqtt_topic", paramTopic.getValue());
prefs.end();
topicSensor = String(paramTopic.getValue());
}
bool setupWiFiManager() {
WiFiManager wm;
wm.setConfigPortalTimeout(180);
wm.addParameter(¶mTopic);
if (tombolResetDitekan()) {
Serial.println("Reset WiFi + NVS (tombol BOOT)");
wm.resetSettings();
prefs.begin(NS_KINDO, false);
prefs.clear();
prefs.end();
}
muatPengaturan();
paramTopic.setValue(topicSensor.c_str(), 64);
Serial.println("WiFiManager: autoConnect...");
if (!wm.autoConnect("KindoESP32-Setup")) {
Serial.println("Portal gagal / timeout");
return false;
}
simpanTopicDariPortal();
Serial.println("WiFi OK — SSID: " + WiFi.SSID());
return true;
}
bool koneksiMQTT() {
mqttClient.setServer(MQTT_HOST, MQTT_PORT);
mqttClient.setBufferSize(512);
String clientId = "ESP32-NVS-" + String(random(0xffff), HEX);
if (mqttClient.connect(clientId.c_str())) {
Serial.println("MQTT terhubung");
return true;
}
Serial.print("MQTT gagal, rc=");
Serial.println(mqttClient.state());
return false;
}
void publishDHT() {
float suhu = dht.readTemperature();
float kelembaban = dht.readHumidity();
if (isnan(suhu) || isnan(kelembaban)) {
Serial.println("DHT22 gagal — cek wiring");
return;
}
char payload[96];
snprintf(payload, sizeof(payload),
"{\"suhu\":%.1f,\"kelembaban\":%.1f}", suhu, kelembaban);
mqttClient.loop();
if (mqttClient.publish(topicSensor.c_str(), payload, false)) {
Serial.print("Publish OK → ");
Serial.println(payload);
} else {
Serial.println("Publish gagal");
}
}
void setup() {
Serial.begin(115200);
delay(500);
dht.begin();
delay(2000);
if (!setupWiFiManager()) {
delay(3000);
ESP.restart();
}
if (!koneksiMQTT()) {
Serial.println("MQTT gagal — restart 5 detik");
delay(5000);
ESP.restart();
}
publishDHT();
}
void loop() {
mqttClient.loop();
delay(10000);
publishDHT();
}
Penjelasan Bagian Kritis
wm.autoConnect("KindoESP32-Setup")— blocking sampai WiFi tersimpan atau timeout 180 detikWiFiManagerParameter— field custom di portal; nilainya kita simpan ke NVS viaprefs.putStringwm.resetSettings()— hapus kredensial WiFi tersimpan (dipicu tombol BOOT)prefs.begin(NS_KINDO, true)— mode read-only saat boot normalmqttClient.loop()— wajib sebelumpublish()(konsisten Seri 1)setBufferSize(512)— cukup untuk payload JSON DHT22
Uji Coba (Step-by-Step)
- Upload sketch, buka Serial Monitor 115200
- Pertama kali: ESP32 membuat AP
KindoESP32-Setup - Di HP: Settings → WiFi → connect
KindoESP32-Setup - Portal terbuka otomatis (atau buka
192.168.4.1) - Pilih WiFi rumah, password, cek field MQTT topic sensor
- Simpan — ESP32 reboot dan connect
- Serial:
WiFi OK→MQTT terhubung→Publish OK - Di MQTT Explorer /
mosquitto_sub, subscribe topic yang kamu set - Reboot ESP32 (tanpa upload) — harus langsung connect tanpa portal
mosquitto_sub -h test.mosquitto.org -t "kodingindonesia/esp32/dht22/data" -v
Pro tip: Gunakan topic unik per perangkat, misalnya
kodingindonesia/anton/esp32/dht22/data, agar tidak bentrok di broker publik.
Gabung dengan Deep Sleep (#11)
Sketch di atas cocok untuk node USB/adaptor. Untuk baterai, pindahkan logika publishDHT() ke dalam setup() seperti artikel deep sleep, lalu tidur lagi. WiFiManager hanya perlu dijalankan saat pertama kali atau setelah reset — jangan buka portal tiap bangun (boros baterai).
// Pola deep sleep + WiFiManager (pseudocode)
prefs.begin("kindo", true);
bool wifiConfigured = prefs.getBool("wifi_ok", false);
prefs.end();
if (!wifiConfigured || tombolResetDitekan()) {
setupWiFiManager(); // portal sekali
prefs.begin("kindo", false);
prefs.putBool("wifi_ok", true);
prefs.end();
} else {
WiFi.begin(); // credentials sudah di NVS internal WiFi stack
// ... timeout connect ...
}
Tips & Troubleshooting
- Portal tidak muncul: Pastikan tidak ada WiFi tersimpan — reset dengan tahan BOOT saat boot, atau
wm.resetSettings() - Captive portal tidak redirect (Android): Buka manual
http://192.168.4.1 - WiFi connect loop: Cek 2.4 GHz — ESP32 tidak support jaringan WiFi 5 GHz saja; dekatkan ke router
- Topic MQTT kosong: Cek
prefs.getStringdefault; isi ulang lewat portal - DHT22 NaN:
delay(2000)setelahdht.begin(); GPIO 4 + pull-up - Compile error WiFiManager: Update library tzapu ke 2.x; board esp32 v3.x
- NVS penuh: Jarang di hobby project;
prefs.clear()pada namespacekindojika perlu factory reset
Keamanan & Produksi
- Jangan commit file
secrets.hdengan password — WiFiManager menghilangkan kebutuhan itu untuk WiFi - Portal default tidak pakai password AP — untuk deploy komersial, set
wm.setAPStaticIPConfig+ password AP atau gunakan MQTT dengan auth di broker sendiri (artikel #16) - Segera pindah dari
test.mosquitto.orgke broker pribadi untuk data produksi
Langkah Selanjutnya (Seri 2)
- Sensor BME280 via I2C — lebih akurat dari DHT22, plus tekanan udara
- Artikel #15: OTA update — butuh WiFiManager (#12) agar firmware bisa di-update tanpa kabel setelah deploy
- Broker Mosquitto pribadi + autentikasi — simpan host/user/pass di NVS dengan pola sama
- Kembali ke node deep sleep untuk gabungkan hemat baterai + konfigurasi lapangan
Dengan NVS dan WiFiManager, ESP32 kamu siap dipasang di lokasi pelanggan tanpa membawa laptop setiap kali jaringan berubah. Lanjutkan Seri 2 di halaman artikel Koding Indonesia.