Pendahuluan — Dari Digital ke Analog
Di Seri 1, sensor utama adalah DHT22 — output digital suhu & kelembaban udara lewat protokol satu-wire (artikel #5). Banyak proyek IoT lapangan butuh input lain: kelembaban tanah untuk pompa greenhouse dan tingkat cahaya (LDR) untuk lampu otomatis atau flash kamera.
Artikel Tier 2 ini mengajarkan ADC (Analog-to-Digital Converter) bawaan ESP32: baca tegangan 0–3,3 V dari modul soil moisture dan LDR, kalibrasi ke persen, lalu publish JSON ke MQTT — pola yang melengkapi servo (#33) dan relay #8 di capstone #10 dan greenhouse (#39).
Prasyarat: Sudah paham GPIO (#3), DHT22 (#5), dan MQTT publish (#7). Broker Mosquitto (#16) dipakai di sketch. Timestamp JSON mengikuti NTP (#34).
Digital vs Analog di ESP32
| Sensor | Tipe | Contoh | API umum |
|---|---|---|---|
| DHT22 (#5) | Digital protokol | Suhu & RH udara | dht.readTemperature() |
| BME280 (#13) | Digital I2C | Suhu, tekanan, RH | bme.readHumidity() |
| Soil moisture | Analog | Kelembaban media tanam | analogRead() |
| LDR + resistor | Analog | Terang/gelap | analogRead() |
DHT22 tidak bisa menggantikan soil probe — yang diukur adalah udara, bukan tanah. Untuk greenhouse (#39), kombinasi BME280 + soil moisture + relay pompa adalah pola standar.
ADC di ESP32 — 12-bit
ESP32 punya SAR ADC 12-bit (nilai mentah 0–4095 pada resolusi default). Tegangan referensi sekitar 3,3 V — jangan kirim >3,3 V ke pin ADC atau modul bisa rusak.
analogRead(pin)— baca nilai mentahanalogReadResolution(12)— set resolusi (default sering sudah 12)map(value, in_min, in_max, 0, 100)— konversi ke persen setelah kalibrasi
Pin ADC Aman dengan WiFi
Saat WiFi aktif, hindari pin ADC2 (GPIO 0, 2, 4, 12–15, 25–27) untuk pembacaan analog — bisa konflik. Gunakan pin ADC1 input-only:
| Fungsi | GPIO | Catatan |
|---|---|---|
| Soil moisture (signal) | GPIO 34 | Input only · ADC1 · tidak bentrok DHT GPIO 4 |
| LDR (titik tengah divider) | GPIO 35 | Input only · ADC1 |
| DHT22 (#5) | GPIO 4 | Digital — jangan pakai untuk analog |
| Relay (#8) | GPIO 26 | Digital output |
| Servo (#33) | GPIO 27 | PWM |
Pro tip: GPIO 34 dan 35 tidak punya pull-up internal — untuk LDR pakai voltage divider eksternal, bukan
INPUT_PULLUPsaja.
Hardware — Modul Soil Moisture
- Modul soil moisture capacitive atau resistive (corrosion-resistant lebih awet)
- ESP32 DevKit
- Kabel jumper · breadboard
- Opsional: pompa mini + relay untuk demo otomasi (#8)
Modul umum punya 3 pin: VCC, GND, AOUT (analog out). Supply 3,3 V cukup untuk pembacaan ADC — beberapa modul juga punya pin digital D0 (threshold) yang tidak kita pakai di artikel ini.
Hardware — LDR (Photoresistor)
LDR mengubah resistansi sesuai cahaya. ESP32 butuh voltage divider:
- LDR ke 3,3 V
- LDR ke resistor tetap 10 kΩ ke GND
- Sambungan tengah LDR–resistor → GPIO 35
Makin terang → tegangan di GPIO 35 naik (polaritas bisa dibalik dengan layout divider — catat saat kalibrasi).
Wiring Soil Moisture ke ESP32
| Pin modul | Ke ESP32 |
|---|---|
| VCC | 3V3 |
| GND | GND |
| AOUT | GPIO 34 |
Sketch Dasar — Baca ADC Lokal
Uji sensor sebelum WiFi:
#define SOIL_PIN 34
#define LDR_PIN 35
// Kalibrasi soil — ukur di proyekmu, nilai ini contoh!
const int SOIL_DRY = 3200; // tanah kering / udara
const int SOIL_WET = 1400; // tanah basah
void setup() {
Serial.begin(115200);
analogReadResolution(12);
}
int soilPercent(int raw) {
int pct = map(raw, SOIL_DRY, SOIL_WET, 0, 100);
return constrain(pct, 0, 100);
}
void loop() {
int soilRaw = analogRead(SOIL_PIN);
int ldrRaw = analogRead(LDR_PIN);
int soilPct = soilPercent(soilRaw);
int lightPct = map(ldrRaw, 500, 3500, 0, 100);
lightPct = constrain(lightPct, 0, 100);
Serial.printf("Soil raw=%d → %d%% | LDR raw=%d → %d%%\n",
soilRaw, soilPct, ldrRaw, lightPct);
delay(2000);
}
Kalibrasi Soil Moisture
- Celup probe di air (atau tanah basah) — catat
SOIL_WET - Angkat ke udara (kering) — catat
SOIL_DRY - Beberapa modul resistive: basah = nilai lebih rendah — balik argumen
map()jika perlu - Simpan konstanta di
config.hatau NVS (#12) untuk deploy lapangan
Capacitive vs Resistive Probe
Modul soil moisture murah di pasar umumnya dua tipe:
| Tipe | Cara kerja | Umur pakai |
|---|---|---|
| Resistive | Arus kecil lewat media basah — hantaran listrik | Korosi elektroda setelah minggu basah terus |
| Capacitive | Mengukur kapasitansi dielektrik tanah | Lebih awet — disarankan untuk greenhouse (#39) |
Keduanya keluaran AOUT analog — wiring ke GPIO 34 dan sketch analogRead sama. Bedanya di kalibrasi dan interval penyiraman: jangan siram setiap menit hanya karena ADC turun 1% — pakai hysteresis seperti relay PIR di artikel #24.
Filter Noise — Rata-rata Bergerak
ADC mentah berfluktuasi karena kabel panjang dan WiFi. Ambil median atau rata-rata N sampel:
int readSoilFiltered() {
const int N = 10;
long sum = 0;
for (int i = 0; i < N; i++) {
sum += analogRead(SOIL_PIN);
delay(5);
}
return (int)(sum / N);
}
Di firmware produksi, jalankan pembacaan ADC di task terpisah seperti contoh FreeRTOS (#31) — task sensor tidak memblokir mqtt.loop().
Broker & Topic MQTT
Konvensi Seri 2 — broker 192.168.1.50, user kindo_esp32:
- Tanah:
kodingindonesia/esp32/tanah/data - Cahaya LDR:
kodingindonesia/esp32/cahaya/data - Referensi DHT22:
kodingindonesia/esp32/dht22/data - Relay pompa (#8):
kodingindonesia/esp32/lampu/kontrol—ON/OFF
Payload JSON + Timestamp NTP
Format konsisten dengan sensor lain — sertakan unix setelah sinkron NTP (#34):
{
"kelembaban_tanah": 42,
"unix": 1782977400
}
{
"cahaya_percent": 78,
"unix": 1782977400
}
Contoh unix 1782977400 = 2026-07-02T14:30:00 UTC — sama di seluruh Seri 2.
Sketch Lengkap — MQTT Publish Tanah & Cahaya
#include <WiFi.h>
#include <PubSubClient.h>
#define SOIL_PIN 34
#define LDR_PIN 35
const char* ssid = "GANTI_NAMA_WIFI";
const char* pass = "GANTI_PASSWORD_WIFI";
const char* mqtt_host = "192.168.1.50";
const char* mqtt_user = "kindo_esp32";
const char* mqtt_pass = "GANTI_PASSWORD_MQTT";
const char* topic_tanah = "kodingindonesia/esp32/tanah/data";
const char* topic_cahaya = "kodingindonesia/esp32/cahaya/data";
const int SOIL_DRY = 3200;
const int SOIL_WET = 1400;
WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
unsigned long lastPub = 0;
int soilPercent(int raw) {
return constrain(map(raw, SOIL_DRY, SOIL_WET, 0, 100), 0, 100);
}
int readFiltered(uint8_t pin) {
long sum = 0;
for (int i = 0; i < 10; i++) { sum += analogRead(pin); delay(5); }
return (int)(sum / 10);
}
void reconnectMqtt() {
while (!mqtt.connected()) {
if (mqtt.connect("kindo-esp32-adc", mqtt_user, mqtt_pass)) {
Serial.println("MQTT connected");
} else {
delay(2000);
}
}
}
void setup() {
Serial.begin(115200);
analogReadResolution(12);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) { delay(300); }
mqtt.setServer(mqtt_host, 1883);
reconnectMqtt();
}
void loop() {
if (!mqtt.connected()) reconnectMqtt();
mqtt.loop();
if (millis() - lastPub > 10000) {
lastPub = millis();
int soil = soilPercent(readFiltered(SOIL_PIN));
int ldr = constrain(map(readFiltered(LDR_PIN), 500, 3500, 0, 100), 0, 100);
char bufTanah[64];
snprintf(bufTanah, sizeof(bufTanah),
"{\"kelembaban_tanah\":%d,\"unix\":1782977400}", soil);
mqtt.publish(topic_tanah, bufTanah);
char bufCahaya[64];
snprintf(bufCahaya, sizeof(bufCahaya),
"{\"cahaya_percent\":%d,\"unix\":1782977400}", ldr);
mqtt.publish(topic_cahaya, bufCahaya);
Serial.printf("Published tanah=%d%% cahaya=%d%%\n", soil, ldr);
}
}
PlatformIO
Di PlatformIO (#29):
lib_deps =
knolleary/PubSubClient
Otomasi Pompa — Tanah Kering + Relay
Gabungkan dengan relay (#8): jika kelembaban_tanah < 30, publish ON ke pompa; jika > 60, OFF (hysteresis anti-flicker). Logika bisa di firmware ESP32 atau di Node-RED (#23) — function node subscribe tanah/data dan publish ke lampu/kontrol.
Itu pratinjau arsitektur greenhouse (#39): soil moisture → MQTT → pompa relay + servo flap (#33) + grafik Grafana (#19).
Integrasi Home Assistant (Opsional)
- MQTT sensor state topic
kodingindonesia/esp32/tanah/data· value template{{ value_json.kelembaban_tanah }}· unit% - MQTT sensor state topic
kodingindonesia/esp32/cahaya/data· value template{{ value_json.cahaya_percent }} - Automation: cahaya < 20% → nyalakan lampu relay
Node-RED & Dashboard
Tambahkan ui_gauge di Node-RED (#23) untuk kelembaban_tanah dan cahaya_percent — satu dashboard dengan chart suhu dari dht22/data.
Uji dengan mosquitto_sub
mosquitto_sub -h 192.168.1.50 -u kindo_esp32 -P GANTI_PASSWORD_MQTT \
-t kodingindonesia/esp32/tanah/data -v
mosquitto_sub -h 192.168.1.50 -u kindo_esp32 -P GANTI_PASSWORD_MQTT \
-t kodingindonesia/esp32/cahaya/data -v
ESP32-CAM & LDR
Di ESP32-CAM (#27), LDR bisa mengontrol LED flash otomatis — baca cahaya analog lalu nyalakan GPIO flash saat gelap. Wiring LDR di board kamera terpisah karena GPIO 4 dipakai flash internal.
Keamanan & Produksi
- Ganti
GANTI_NAMA_WIFI,GANTI_PASSWORD_WIFI,GANTI_PASSWORD_MQTT - Jangan commit kredensial ke GitHub
- Kalibrasi soil per media tanam — cocopeat vs tanah kebun beda kurva
- Pakai MQTT TLS (#17) jika data sensor dipakai keputusan irigasi produksi
- Modul resistive: jangan biarkan probe basah 24/7 tanpa power cycle — korosi lebih cepat
Estimasi Biaya
| Komponen | Harga perkiraan (IDR) |
|---|---|
| ESP32 DevKit | 35.000 – 55.000 |
| Modul soil moisture | 12.000 – 25.000 |
| LDR + resistor 10k | 3.000 – 8.000 |
| Breadboard + jumper | 10.000 – 20.000 |
| Total | ~60.000 – 108.000 |
Checklist Sebelum Demo
- ☐ Serial menampilkan raw ADC soil & LDR berubah saat kondisi berubah
- ☐ Kalibrasi basah/kering tercatat
- ☐ WiFi + MQTT publish ke
tanah/datadancahaya/data - ☐
mosquitto_submenerima JSON valid - ☐ GPIO 34/35 · tidak bentrok DHT/relay/servo
FAQ Singkat
- Soil selalu 0% atau 100%?
- Balik atau sesuaikan
SOIL_DRY/SOIL_WET— setiap modul berbeda. - LDR tidak berubah?
- Cek voltage divider dan GPIO 35 — ukur multimeter di titik tengah.
- Bisa gabung DHT22 + soil satu board?
- Ya — DHT GPIO 4 digital, soil GPIO 34 analog; publish ke topic terpisah.
- Perlu library khusus?
- Tidak untuk ADC dasar — cukup
analogRead+ PubSubClient dari #7.
Tips & Troubleshooting
- Nilai ADC naik-turun liar: Tambah filter rata-rata · perpendek kabel · kapasitor 100 nF dekat pin
- WiFi connect tapi ADC aneh: Pastikan pin ADC1 (34/35), bukan ADC2 saat WiFi on
- MQTT tidak keluar: Cek
mqtt.loop()dan buffer sizesetBufferSize(256) - Topic kosong di broker: Case-sensitive — harus persis
kodingindonesia/esp32/tanah/data - JSON parse gagal di subscriber: Hindari koma trailing · validasi dengan
mosquitto_sub -v
Langkah Selanjutnya — Tier 2 Seri 2
Input analog melengkapi aktuator servo (#33) dan relay (#8) — sensor tanah siap untuk capstone. Lanjut ke pelengkap Tier 2:
- ESP8266 / NodeMCU vs ESP32 — kapan pakai board murah vs upgrade
- NTP (#34) — ganti unix contoh dengan waktu nyata
- Subscriber Python (#18) — simpan histori kelembaban tanah ke MySQL
- Grafana (#19) — grafik tanah vs cahaya vs suhu DHT
- Capstone greenhouse (#39) — BME280 + soil + pompa + dashboard
ADC membuka sensor analog murah di proyek ESP32 — lanjutkan di halaman artikel Koding Indonesia.