SMART FARMING ESP32 - FULL AUTOMATIC

 

Kelompok 1 – Water & Irrigation (4 relay)

Relay

Fungsi

Pin ESP32

RELAY_AIR

Isi air tandon

25

RELAY_POMPA_HARIAN

Pompa utama hidroponik

13

RELAY_SALUR

Pompa salur ke tanaman

14

RELAY_IRIGASI_TANAH

Pompa tetes tanah

33

Kelompok 2 – Nutrisi & Circulation (4 relay)

Relay

Fungsi

Pin ESP32

RELAY_NUTRISI_A

Dosing nutrisi A

26

RELAY_NUTRISI_B

Dosing nutrisi B

32

RELAY_SIRKULASI

Pompa sirkulasi untuk mixing

27

RELAY_FAN

Fan / exhaust

12

Kelompok 3 – pH Control (2 relay)

Relay

Fungsi

Pin ESP32

RELAY_UP

Naikkan pH

2

RELAY_DOWN

Turunkan pH

4

 

Kelompok 1 – Tank & Water Level

Sensor

Fungsi

Pin ESP32

HC-SR04

Mengukur tinggi air di tanki

TRIG → 5, ECHO → 18

TDS

Mengukur konduktivitas / PPM air

34

Kelompok 2 – Tanah & Lingkungan

Sensor

Fungsi

Pin ESP32

Soil Moisture

Mengukur kelembaban tanah

35

DHT22

Mengukur suhu & kelembaban udara

15

Kelompok 3 – pH & Nutrisi

Sensor

Fungsi

Pin ESP32

pH sensor

Mengukur pH larutan

39 / VN

 

 

 

/* Smart Farming ESP32 - Full Automatic

   Relay logic:

     RELAY_AIR           - isi air tandon (GPIO 25)

     RELAY_NUTRISI_A     - dosing nutrisi A (GPIO 26)

     RELAY_NUTRISI_B     - dosing nutrisi B (GPIO 32)

     RELAY_SIRKULASI     - pompa sirkulasi (GPIO 27)

     RELAY_POMPA_HARIAN  - pompa utama hidroponik (GPIO 13)

     RELAY_SALUR         - pompa salur ke tanaman (GPIO 14)

     RELAY_IRIGASI_TANAH - pompa tetes tanah (GPIO 33)

     RELAY_FAN           - fan/exhaust (GPIO 12)

     RELAY_UP            - pH naik (GPIO 2)

     RELAY_DOWN          - pH turun (GPIO 4)

 

   Sensors:

     HC-SR04 TRIG -> GPIO 5, ECHO -> GPIO 18

     TDS analog -> GPIO 34

     SOIL analog -> GPIO 35

     PH analog  -> GPIO 39

     DHT22      -> GPIO 15

*/

 

#include

#include

#include

#include

#include

#include "DHT.h"

 

// ----------------- CONFIG -----------------

const char* ssid = "BINTANG TERANG";

const char* password = "1sampai8";

 

#define DHTPIN 15

#define DHTTYPE DHT22

DHT dht(DHTPIN,DHTTYPE);

 

// Tank

const float TANK_HEIGHT_CM = 25.0;

const float TANK_VOLUME_L = 15.0;

const float VOLUME_TARGET_L = 8.0;

 

// Soil thresholds

const float SOIL_ON_PERCENT = 40.0;

const float SOIL_OFF_PERCENT = 60.0;

 

// TDS / PPM baseline

const float PPM_BASE = 300.0;

const float PPM_DAILY_INC = 28.0;

 

// pH target

const float PH_TARGET = 6.0;

const float PH_TOLERANCE = 0.2;

 

// NTP / WiFi

const unsigned long NTP_UPDATE_INTERVAL = 15UL*60UL*1000UL;

 

// ----------------- PIN -----------------

#define RELAY_AIR           25

#define RELAY_NUTRISI_A     26

#define RELAY_NUTRISI_B     32

#define RELAY_SIRKULASI     27

#define RELAY_POMPA_HARIAN  13

#define RELAY_SALUR         14

#define RELAY_IRIGASI_TANAH 33

#define RELAY_FAN           12

#define RELAY_UP            2

#define RELAY_DOWN          4

 

#define TRIG_PIN 5

#define ECHO_PIN 18

#define TDS_PIN 34

#define SOIL_PIN 35

#define PH_PIN 39

 

// ----------------- GLOBALS -----------------

LiquidCrystal_I2C lcd(0x27,16,2);

WiFiUDP ntpUDP;

NTPClient timeClient(ntpUDP,"pool.ntp.org",7*3600);

Preferences preferences;

 

int dayNumber = 1;

float targetPPM = PPM_BASE;

float currentPPM = 0.0;

float currentVolumeL = 0.0;

float soilPercent = 0.0;

float temp = 0.0, hum = 0.0;

float phValue = 7.0;

 

unsigned long lastNTPUpdate=0;

unsigned long lastSlideChange=0;

const unsigned long SLIDE_MS=3000;

int lcdPage=0;

unsigned long lastDebug=0;

 

// State machine

enum CycleState{

  C_IDLE,

  C_CHECK_VOLUME,

  C_FILLING_WATER,

  C_CHECK_PPM,

  C_DOSE_A,

  C_WAIT_AFTER_A,

  C_DOSE_B,

  C_CIRCULATE_MIX,

  C_IRRIGATE_SALUR,

  C_COMPLETE

};

CycleState cycleState = C_IDLE;

unsigned long stateStart=0;

const unsigned long MAX_FILL_MS=120000;

 

// ----------------- HELPERS -----------------

void relayWrite(int pin,bool on){digitalWrite(pin,on?HIGH:LOW);}

void allRelaysOff(){

  relayWrite(RELAY_AIR,false); relayWrite(RELAY_NUTRISI_A,false);

  relayWrite(RELAY_NUTRISI_B,false); relayWrite(RELAY_SIRKULASI,false);

  relayWrite(RELAY_POMPA_HARIAN,false); relayWrite(RELAY_SALUR,false);

  relayWrite(RELAY_IRIGASI_TANAH,false); relayWrite(RELAY_FAN,false);

  relayWrite(RELAY_UP,false); relayWrite(RELAY_DOWN,false);

}

void saveDayNumber(){

  preferences.begin("hydro_nvs",false);

  preferences.putInt("day_num",dayNumber);

  preferences.end();

}

void serialDebug(){

  Serial.println("------------------------------------------------");

  Serial.printf("Time: %s | Hari ke-%d\n",timeClient.getFormattedTime().c_str(),dayNumber);

  Serial.printf("Volume: %.2f L | TargetMin: %.2f L\n",currentVolumeL,VOLUME_TARGET_L);

  Serial.printf("PPM: %.0f | TargetPPM: %.0f\n",currentPPM,targetPPM);

  Serial.printf("Soil: %.0f%%\n",soilPercent);

  Serial.printf("Temp: %.1fC Hum: %.1f%%\n",temp,hum);

  Serial.printf("pH: %.2f\n",phValue);

  Serial.printf("WiFi: %s\n",WiFi.status()==WL_CONNECTED?"ON":"OFF");

  Serial.printf("CycleState: %d\n",(int)cycleState);

}

 

// ----------------- SENSORS -----------------

float readTDSppm(){

  long sum=0; const int SAMPLES=10;

  for(int i=0;i<SAMPLES;i++){sum+=analogRead(TDS_PIN); delay(2);}

  float raw=sum/(float)SAMPLES;

  float voltage=raw*(3.3/4095.0);

  float ec=(voltage/3.3)*2000.0;

  float ppm=ec*0.5;

  return ppm;

}

 

float readTankVolumeLiters(){

  long totalDur=0; const int SAMPLES=5;

  for(int i=0;i<SAMPLES;i++){

    digitalWrite(TRIG_PIN,LOW); delayMicroseconds(2);

    digitalWrite(TRIG_PIN,HIGH); delayMicroseconds(10);

    digitalWrite(TRIG_PIN,LOW);

    long dur=pulseIn(ECHO_PIN,HIGH,30000); if(dur==0) dur=30000;

    totalDur+=dur; delay(5);

  }

  float avgDur=totalDur/(float)SAMPLES;

  float distanceCm=(avgDur*0.0343)/2.0;

  float effectiveDistance = distanceCm-2.0;

  if(effectiveDistance<0) effectiveDistance=0;

  if(effectiveDistance>TANK_HEIGHT_CM) effectiveDistance=TANK_HEIGHT_CM;

  float waterHeight=TANK_HEIGHT_CM-effectiveDistance;

  return (waterHeight/TANK_HEIGHT_CM)*TANK_VOLUME_L;

}

 

float readSoilPercent(){

  int raw=analogRead(SOIL_PIN);

  float pct=map(constrain(raw,0,4095),0,4095,100,0);

  if(pct<0)pct=0; if(pct>100)pct=100;

  return pct;

}

 

float readPH(){

  int raw=analogRead(PH_PIN);

  float voltage=(float)raw/4095.0*3.3;

  float ph=7.0 + (2.5-voltage); // kalibrasi sederhana

  if(ph<0) ph=0; if(ph>14) ph=14;

  return ph;

}

 

void lcdPrint(int x,int y,String text){

  lcd.setCursor(x,y);

  lcd.print("                ");

  lcd.setCursor(x,y);

  lcd.print(text);

}

 

// ----------------- CYCLE -----------------

void enterState(CycleState s){cycleState=s; stateStart=millis(); Serial.printf("[CYCLE] Enter state %d\n",(int)s);}

void processCycle(){

  unsigned long elapsed=millis()-stateStart;

  switch(cycleState){

    case C_IDLE: break;

    case C_CHECK_VOLUME:

      if(currentVolumeL<VOLUME_TARGET_L){enterState(C_FILLING_WATER);relayWrite(RELAY_AIR,true);}

      else enterState(C_CHECK_PPM); break;

    case C_FILLING_WATER:

      if(currentVolumeL>=VOLUME_TARGET_L){relayWrite(RELAY_AIR,false);enterState(C_CHECK_PPM);}

      else if(elapsed>MAX_FILL_MS){relayWrite(RELAY_AIR,false);enterState(C_IDLE); Serial.println("[CYCLE] Fill timeout");} break;

    case C_CHECK_PPM:

      currentPPM=readTDSppm();

      Serial.printf("[CYCLE] Read PPM: %.1f target: %.1f\n",currentPPM,targetPPM);

      if(currentPPM<targetPPM) enterState(C_DOSE_A);

      else enterState(C_CIRCULATE_MIX); break;

    case C_DOSE_A: relayWrite(RELAY_NUTRISI_A,true); delay(3000); relayWrite(RELAY_NUTRISI_A,false); enterState(C_WAIT_AFTER_A); break;

    case C_WAIT_AFTER_A: if(elapsed>=30000) enterState(C_DOSE_B); break;

    case C_DOSE_B: relayWrite(RELAY_NUTRISI_B,true); delay(3000); relayWrite(RELAY_NUTRISI_B,false); enterState(C_CIRCULATE_MIX); break;

    case C_CIRCULATE_MIX: relayWrite(RELAY_SIRKULASI,true); delay(15000); relayWrite(RELAY_SIRKULASI,false); enterState(C_IRRIGATE_SALUR); break;

    case C_IRRIGATE_SALUR: relayWrite(RELAY_SALUR,true); delay(10000); relayWrite(RELAY_SALUR,false); enterState(C_COMPLETE); break;

    case C_COMPLETE:

      dayNumber++; targetPPM=PPM_BASE+(dayNumber-1)*PPM_DAILY_INC; saveDayNumber(); enterState(C_IDLE); break;

  }

}

 

// ----------------- SETUP -----------------

void setup(){

  Serial.begin(115200);

 

  pinMode(RELAY_AIR,OUTPUT); pinMode(RELAY_NUTRISI_A,OUTPUT); pinMode(RELAY_NUTRISI_B,OUTPUT);

  pinMode(RELAY_SIRKULASI,OUTPUT); pinMode(RELAY_POMPA_HARIAN,OUTPUT); pinMode(RELAY_SALUR,OUTPUT);

  pinMode(RELAY_IRIGASI_TANAH,OUTPUT); pinMode(RELAY_FAN,OUTPUT);

  pinMode(RELAY_UP,OUTPUT); pinMode(RELAY_DOWN,OUTPUT);

 

  pinMode(TRIG_PIN,OUTPUT); pinMode(ECHO_PIN,INPUT);

  pinMode(TDS_PIN,INPUT); pinMode(SOIL_PIN,INPUT); pinMode(PH_PIN,INPUT);

 

  allRelaysOff();

 

  lcd.init(); lcd.backlight(); lcd.clear(); lcdPrint(0,0,"Booting...");

 

  WiFi.begin(ssid,password);

  int attempts=0;

  while(WiFi.status()!=WL_CONNECTED && attempts<20){delay(500);Serial.print(".");attempts++;}

  if(WiFi.status()==WL_CONNECTED){Serial.println("\nWiFi connected"); lcdPrint(0,1,"WiFi: OK"); timeClient.begin(); timeClient.update(); lastNTPUpdate=millis();}

  else{Serial.println("\nWiFi failed"); lcdPrint(0,1,"WiFi: FAIL");}

 

  preferences.begin("hydro_nvs",true); dayNumber=preferences.getInt("day_num",1); preferences.end();

  targetPPM=PPM_BASE+(dayNumber-1)*PPM_DAILY_INC;

 

  dht.begin(); delay(1000); lcd.clear();

}

 

// ----------------- LOOP -----------------

void loop(){

  // WiFi reconnect & NTP update

  static unsigned long lastReconnect=0;

  if(WiFi.status()!=WL_CONNECTED && millis()-lastReconnect>30000){WiFi.begin(ssid,password);lastReconnect=millis();}

  else if(millis()-lastNTPUpdate>NTP_UPDATE_INTERVAL){timeClient.update();lastNTPUpdate=millis();}

 

  // Read sensors

  currentVolumeL=readTankVolumeLiters();

  currentPPM=readTDSppm();

  soilPercent=readSoilPercent();

  temp=dht.readTemperature(); hum=dht.readHumidity();

  phValue=readPH();

 

  // Soil pump

  static bool soilPumpOn=false;

  if(!soilPumpOn && soilPercent<SOIL_ON_PERCENT){relayWrite(RELAY_IRIGASI_TANAH,true); soilPumpOn=true;}

  else if(soilPumpOn && soilPercent>SOIL_OFF_PERCENT){relayWrite(RELAY_IRIGASI_TANAH,false); soilPumpOn=false;}

 

  // pH control automatic

  if(phValue<PH_TARGET-PH_TOLERANCE){relayWrite(RELAY_UP,true); relayWrite(RELAY_DOWN,false);}

  else if(phValue>PH_TARGET+PH_TOLERANCE){relayWrite(RELAY_DOWN,true); relayWrite(RELAY_UP,false);}

  else{relayWrite(RELAY_UP,false); relayWrite(RELAY_DOWN,false);}

 

  int hour=0,minute=0;

  if(WiFi.status()==WL_CONNECTED) timeClient.update();

  hour=timeClient.getHours(); minute=timeClient.getMinutes();

 

  // Pompa harian

  if(cycleState==C_IDLE){if(hour>=8 && hour<18) relayWrite(RELAY_POMPA_HARIAN,true); else relayWrite(RELAY_POMPA_HARIAN,false);}

  else relayWrite(RELAY_POMPA_HARIAN,false);

 

  // Daily schedule

  if(cycleState==C_IDLE){if(hour==6 && minute==0) allRelaysOff(); if(hour==18 && minute==0) enterState(C_CHECK_VOLUME);}

 

  // Cycle

  if(cycleState!=C_IDLE) processCycle();

 

  // LCD slides

  if(millis()-lastSlideChange>=SLIDE_MS){

    lastSlideChange=millis();

    lcdPage=(lcdPage+1)%6;

    lcd.clear();

    if(lcdPage==0){

        String t = WiFi.status()==WL_CONNECTED ? timeClient.getFormattedTime().substring(0,8) : "NoTime";

        lcdPrint(0,0, String("Jam:") + t);

        lcdPrint(0,1, String("Hari:") + String(dayNumber) + " W:" + (WiFi.status()==WL_CONNECTED ? "ON" : "OFF"));

    } else if(lcdPage==1){

        lcdPrint(0,0, String("Vol:") + String(currentVolumeL,1) + "L Tar:" + String(VOLUME_TARGET_L,1));

        lcdPrint(0,1, String("PPM:") + String(currentPPM,0) + " T:" + String(targetPPM,0));

    } else if(lcdPage==2){

        lcdPrint(0,0, String("Soil:") + String(soilPercent,0) + "%");

        lcdPrint(0,1, String("SoilPump:") + (digitalRead(RELAY_IRIGASI_TANAH) ? "ON" : "OFF"));

    } else if(lcdPage==3){

        lcdPrint(0,0, String("Temp:") + String(temp,1) + "C");

        lcdPrint(0,1, String("Hum:") + String(hum,1) + "%");

    } else if(lcdPage==4){

        lcdPrint(0,0, String("pH:") + String(phValue,2));

        lcdPrint(0,1, String("UP:") + (digitalRead(RELAY_UP)?"ON":"OFF") + " DN:" + (digitalRead(RELAY_DOWN)?"ON":"OFF"));

    } else if(lcdPage==5){

        lcdPrint(0,0, String("State:") + String((int)cycleState));

        lcdPrint(0,1, String("Fan:") + (digitalRead(RELAY_FAN)?"ON":"OFF") + " Tmp:" + String(temp,1) + "C");

    }

  }

 

  // Serial debug

  if(millis()-lastDebug>5000){lastDebug=millis(); serialDebug();}

  delay(200);

}

 

 

Komentari Tulisan Ini
Tulisan Lainnya
PENJELASAN STATUS SISTEM HIDROPONIK

    Bagian Kode Nilai Parameter Arti H 1 1 Hari Tanam (Hari ke-) Tanaman Anda sedang berada di Hari ke-1 fase pertumbuhan.

14/10/2025 22:07 - Oleh Admin - Dilihat 99 kali
PRAKTIK RANGKAIAN LAMPU FLIP FLOP

kami akan melaksanakan praktik rangkaian lampu flip-flop, yaitu rangkaian sederhana yang menunjukkan prinsip kerja sistem penyalaan lampu bergantian secara otomatis. Tujuan dari prak

13/10/2025 14:11 - Oleh Admin - Dilihat 63 kali
PEMBUKAAN PELATIHAN SMART FARMING PROJECT BASED LEARING (PBL)

Pelatihan Smart Farming berbasis Internet of Things (IoT) ini merupakan langkah nyata dalam menghadapi tantangan era digital di sektor pertanian. Dengan penerapan teknologi, diharapk

13/10/2025 12:17 - Oleh Admin - Dilihat 51 kali
PENDAFTARAN PROJECT BASED LEARNING (PBL) TAHAP 4

Ikuti Pelatihan smart Farming berbasis Internet of Things (IoT) bersama para ahli dan praktisi terbaik. Materi mencakup: Pengenalan Teknologi IoT untuk Pertanian Sistem Iriga

13/10/2025 11:57 - Oleh Admin - Dilihat 69 kali
ESP 8266 lolin 3 relay https://bintangterang.or.id/iot

Materi Pembahasan: ESP8266 Lolin untuk Kontrol 3 Relay via Server HTTPS 1. Pendahuluan ESP8266 Lolin adalah modul mikrokontroler berbasis WiFi yang dapat diprogram menggunakan Arduino

20/09/2025 01:01 - Oleh Admin - Dilihat 174 kali
Implementasi CBT–CBA Di LPK BINTANG TERANG

Timeline Implementasi CBT–CBA LPK BINTANG TERANG (2024–2026)   No Kegiatan 2024 2025 2026 1 Perencanaan & Penyusunan K

19/09/2025 13:17 - Oleh Admin - Dilihat 87 kali
PELATIHAN KOMPUTER & PELATIHAN OTOMOTIF

Terima kasih yang sebesar-besarnya kepada BBPVP Bandung atas penyelenggaraan pelatihan Tailor Made Training (TMT). Pelatihan ini sangat bermanfaat bagi kami, semoga kerja sama ini dapat

16/09/2025 06:03 - Oleh Admin - Dilihat 118 kali
IoT di Indonesia: Dari Smart City ke Smart Farm — Tren, Tantangan, dan Prospek 2025

1. IoT, Lebih dari Sekadar Tren Internet of Things (IoT) bukan lagi sekadar jargon teknologi, tapi sudah jadi bagian nyata dalam kehidupan sehari-hari. IoT merujuk pada perangkat yang

04/09/2025 07:54 - Oleh Admin - Dilihat 84 kali
TAILORE MADE TRANING

Raih Kompetensi Kantor Modern Bersama Practical Office Program TAILORE MADE TRANING di LPK Bintang Terang! Di era digital ini, penguasaan aplikasi perkantoran menjadi salah satu kunci

11/07/2025 21:31 - Oleh Admin - Dilihat 150 kali
Perbedaan Microsoft Word dan Google Docs: Mana yang Lebih Cocok untuk Kamu?

Cari tahu perbedaan Microsoft Word dan Google Docs: mana yang lebih cocok untuk kerja atau belajar? Simak penjelasan lengkap dan rekomendasi terbaik dari LPK Bintang Terang! Word vs Go

04/06/2025 19:29 - Oleh Admin - Dilihat 1082 kali