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

SensorTipeContohAPI umum
DHT22 (#5)Digital protokolSuhu & RH udaradht.readTemperature()
BME280 (#13)Digital I2CSuhu, tekanan, RHbme.readHumidity()
Soil moistureAnalogKelembaban media tanamanalogRead()
LDR + resistorAnalogTerang/gelapanalogRead()

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 mentah
  • analogReadResolution(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:

FungsiGPIOCatatan
Soil moisture (signal)GPIO 34Input only · ADC1 · tidak bentrok DHT GPIO 4
LDR (titik tengah divider)GPIO 35Input only · ADC1
DHT22 (#5)GPIO 4Digital — jangan pakai untuk analog
Relay (#8)GPIO 26Digital output
Servo (#33)GPIO 27PWM

Pro tip: GPIO 34 dan 35 tidak punya pull-up internal — untuk LDR pakai voltage divider eksternal, bukan INPUT_PULLUP saja.

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 modulKe ESP32
VCC3V3
GNDGND
AOUTGPIO 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

  1. Celup probe di air (atau tanah basah) — catat SOIL_WET
  2. Angkat ke udara (kering) — catat SOIL_DRY
  3. Beberapa modul resistive: basah = nilai lebih rendah — balik argumen map() jika perlu
  4. Simpan konstanta di config.h atau NVS (#12) untuk deploy lapangan

Capacitive vs Resistive Probe

Modul soil moisture murah di pasar umumnya dua tipe:

TipeCara kerjaUmur pakai
ResistiveArus kecil lewat media basah — hantaran listrikKorosi elektroda setelah minggu basah terus
CapacitiveMengukur kapasitansi dielektrik tanahLebih 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/kontrolON/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)

Di Home Assistant (#21):

  • 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

KomponenHarga perkiraan (IDR)
ESP32 DevKit35.000 – 55.000
Modul soil moisture12.000 – 25.000
LDR + resistor 10k3.000 – 8.000
Breadboard + jumper10.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/data dan cahaya/data
  • mosquitto_sub menerima 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 size setBufferSize(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:

ADC membuka sensor analog murah di proyek ESP32 — lanjutkan di halaman artikel Koding Indonesia.