#include <Wire.h> // Library komunikasi I2C (untuk RTC)
#include "RTClib.h" // Library RTC DS1307/DS3231
#include <ESP32Servo.h> // Library servo ESP32
// ====== Konstanta Relay ======
#define RELAY_OFF LOW
#define RELAY_ON HIGH
// ====== Pin Setup ======
// HC-SR04 (sensor jarak air kolam)
const int trigPin = 5;
const int echoPin = 18;
// Relay
const int relayPumpPin = 19; // Relay pompa air (isi kolam)
const int relayLampPin = 27; // Relay lampu kolam
// LDR Module
const int ldrAOPin = 34; // AO -> dibaca analogRead (monitor level cahaya)
const int ldrDOPin = 32; // DO -> dibaca digitalRead (otomatis gelap/terang)
// Servo
const int servoPin = 23; // Servo pakan ikan
// Batas jarak air (dalam cm) untuk logika pompa
const float MAX_WATER_DISTANCE = 25.0; // jika lebih besar -> air kurang / bocor
const float NORMAL_DISTANCE = 15.0; // jika lebih kecil -> air normal
RTC_DS1307 rtc; // objek RTC
Servo feederServo; // objek servo pakan
int lastFeedSecond = -1; // menyimpan detik terakhir servo bergerak (untuk jeda 4 detik)
// ===================== SETUP =====================
void setup() {
Serial.begin(115200); // mulai Serial Monitor dengan baud 115200
// Konfigurasi pin HC-SR04
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
// Konfigurasi pin relay
pinMode(relayPumpPin, OUTPUT);
pinMode(relayLampPin, OUTPUT);
digitalWrite(relayPumpPin, RELAY_OFF); // awal: pompa dimatikan
digitalWrite(relayLampPin, RELAY_OFF); // awal: lampu dimatikan
// LDR DO sebagai input digital
pinMode(ldrDOPin, INPUT);
// ================== SERVO SETUP (ESP32Servo) ==================
// Alokasi timer PWM untuk ESP32 (wajib di beberapa kasus)
ESP32PWM::allocateTimer(0);
ESP32PWM::allocateTimer(1);
ESP32PWM::allocateTimer(2);
ESP32PWM::allocateTimer(3);
feederServo.setPeriodHertz(50); // frekuensi standar servo (50 Hz)
feederServo.attach(servoPin, 500, 2400); // pin servo, pulse min & max (µs)
feederServo.write(0); // posisi awal servo (tutup pakan)
// Inisialisasi RTC
Wire.begin();
if (!rtc.begin()) {
Serial.println("RTC tidak ditemukan!");
while (1); // berhenti di sini kalau RTC tidak terdeteksi
}
if (!rtc.isrunning()) {
// Jika RTC belum jalan, set waktu sesuai waktu saat compile
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
Serial.println("SIKOPI - Sistem Kolam Ikan Pintar Berbasis IoT (TEST SERVO 4 DETIK)");
}
// ===================== LOOP =====================
void loop() {
DateTime now = rtc.now(); // baca waktu saat ini dari RTC
checkWaterLevel(); // cek level air kolam + kontrol pompa
testServoEvery4Seconds(now); // gerakkan servo setiap 4 detik (mode uji coba)
checkLightLevel(); // cek LDR + kontrol lampu kolam
printStatus(now); // tampilkan waktu di Serial Monitor
delay(1000); // jeda 1 detik tiap loop
}
// ===================== TEST SERVO 4 DETIK =====================
// Fungsi ini membuat servo bergerak setiap 4 detik sekali
void testServoEvery4Seconds(const DateTime& now) {
int currentSecond = now.second(); // ambil detik dari RTC
// Jika detik habis dibagi 4 (0,4,8,12,...) dan belum pernah dieksekusi di detik itu
if (currentSecond % 4 == 0 && currentSecond != lastFeedSecond) {
Serial.println("== TEST SERVO: GERAK SETIAP 4 DETIK ==");
giveFood(); // gerakkan servo (buka-tutup pakan)
lastFeedSecond = currentSecond; // simpan detik terakhir
}
}
// GERAK SERVO (90° lalu kembali 0°)
// Disimulasikan sebagai membuka tutup tempat pakan ikan
void giveFood() {
feederServo.write(90); // servo ke 90° (buka pintu pakan)
delay(1000); // tunggu 1 detik
feederServo.write(0); // balik ke 0° (tutup lagi)
delay(500); // jeda sebentar
Serial.println("Servo selesai gerak.");
}
// ===================== LEVEL AIR (HC-SR04 + Relay Pompa) =====================
void checkWaterLevel() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// baca durasi pantulan ECHO
long duration = pulseIn(echoPin, HIGH);
float distance = (duration * 0.034) / 2.0; // konversi ke cm
Serial.print("Jarak air: ");
Serial.print(distance);
Serial.println(" cm");
if (distance > MAX_WATER_DISTANCE) {
// Jika jarak ke air terlalu jauh -> air berkurang/bocor -> pompa ON
digitalWrite(relayPumpPin, RELAY_ON);
Serial.println("Pompa: HIDUP (Air kurang/bocor)");
} else if (distance < NORMAL_DISTANCE) {
// Jika air cukup dekat -> level air normal -> pompa OFF
digitalWrite(relayPumpPin, RELAY_OFF);
Serial.println("Pompa: MATI (Air normal)");
} else {
// Di antara dua batas -> tidak mengubah status pompa
Serial.println("Pompa: KONDISI TENGAH (tidak diubah)");
}
}
// ===================== LAMPU OTOMATIS (LDR AO & DO) =====================
void checkLightLevel() {
int AO = analogRead(ldrAOPin); // baca nilai analog LDR
int DO = digitalRead(ldrDOPin); // baca nilai digital LDR
Serial.print("LDR AO=");
Serial.print(AO);
Serial.print(" | DO=");
Serial.println(DO);
if (DO == HIGH) {
digitalWrite(relayLampPin, RELAY_ON); // lampu ON
Serial.println("Lampu: HIDUP (gelap)");
} else {
digitalWrite(relayLampPin, RELAY_OFF); // lampu OFF
Serial.println("Lampu: MATI (terang)");
}
}
// ===================== STATUS RTC =====================
void printStatus(const DateTime& now) {
Serial.print("RTC: ");
Serial.print(now.hour()); Serial.print(":");
Serial.print(now.minute()); Serial.print(":");
Serial.println(now.second());
Serial.println("---------------------");
}