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