#define BLYNK_TEMPLATE_ID "TMPL6l2b4iWG7"
#define BLYNK_TEMPLATE_NAME "Mobile IoT"
#define BLYNK_AUTH_TOKEN "qMmNiYi9obFgRUpfeEgvgiJEeN9JSSaw" // Ganti dengan token Blynk Anda
#include <Wire.h>
#include <LiquidCrystal_I2C.h> // Library untuk LCD 20x4 I2C
#include <WiFi.h>
#include <BlynkSimpleEsp32.h> // Library untuk Blynk dengan ESP32
#include "ESP32Servo.h" // Library untuk kontrol servo
#include "RTClib.h" // Library untuk RTC
#include <OneWire.h> // Library untuk sensor DS18B20
#include <DallasTemperature.h> // Library untuk sensor DS18B20
// RTC initialization
RTC_DS1307 rtc;
char daysOfTheWeek[7][12] = {"Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jum'at", "Sabtu"};
unsigned long previousMillis = 0;
const long disp_interval = 300; // Interval untuk menampilkan data ke Serial Monitor
// Pin untuk servo, tombol, dan LED
const int servoPin = 13;
const int btnPin = 4;
const int ledPin = 12;
int btnStatus = 0;
// DS18B20 Setup
#define ONE_WIRE_BUS 5
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
// Pin untuk sensor pH dan turbidity
const int phPin = 34;
const int turbidityPin = 35;
// Servo objek
Servo servo1; // Menggunakan objek servo dari Wokwi
// WiFi credentials
char ssid[] = "Wokwi-GUEST"; // SSID WiFi Wokwi
char pass[] = ""; // Password WiFi Wokwi (kosongkan, karena Wokwi_Guest tidak membutuhkan password)
BlynkTimer timer; // Timer untuk mengatur jadwal pakan otomatis
// LCD 20x4 I2C Setup (Alamat default 0x3F untuk LCD I2C)
LiquidCrystal_I2C lcd(0x27, 20, 4);
// Variabel untuk Time Input Widget
int scheduledHour = 0;
int scheduledMinute = 0;
void setup() {
Serial.begin(115200);
Serial.println("Alat Pakan Ikan Koi");
Wire.begin();
lcd.init();
lcd.backlight();
Wire.begin();
Serial.println("Scanning for I2C devices...");
for (byte i = 8; i < 120; i++) {
Wire.beginTransmission(i);
if (Wire.endTransmission() == 0) {
Serial.print("Found device at address 0x");
Serial.println(i, HEX);
}
}
// Menghubungkan servo ke pin yang sesuai
servo1.attach(servoPin);
servo1.write(0); // Set posisi awal servo ke 0 derajat
pinMode(btnPin, INPUT);
pinMode(ledPin, OUTPUT);
// Inisialisasi DS18B20
sensors.begin();
// Inisialisasi RTC
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
Serial.flush();
while (1) delay(10);
}
if (!rtc.isrunning()) {
Serial.println("RTC is NOT running, let's set the time!");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
// Menghubungkan ESP32 ke WiFi
Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
// Menjalankan fungsi secara terjadwal
timer.setInterval(1000L, sendData); // Mengirim data setiap 1 detik
timer.setInterval(1000L, checkPakanSchedule); // Mengecek jadwal pemberian pakan otomatis
}
void loop() {
Blynk.run(); // Menjalankan Blynk
timer.run(); // Menjalankan timer untuk update data ke Blynk
btnStatus = digitalRead(btnPin); // Membaca status tombol
if (btnStatus == HIGH) {
// Jika tombol ditekan, buka servo 2 kali
for (int i = 0; i < 2; i++) {
kontrol_katup(1, 500, 45); // Buka servo 1 kali dengan sudut 45 derajat
// delay(500); // Jeda 500ms setelah buka servo pertama
kontrol_katup(1, 500, 0); // Tutup servo (kembali ke posisi 0 derajat)
// delay(500); // Jeda 500ms setelah tutup servo pertama
}
}
float temperatureC = sensors.getTempCByIndex(0); // Mendapatkan suhu dari DS18B20
String temperatureStatus = "Normal"; // Status default
int phValue = analogRead(phPin); // Membaca sensor pH (nilai potensiometer)
float phVoltage = phValue * (3.3 / 4095.0); // Mengubah menjadi tegangan (0 hingga 3.3V)
float phScale = (phVoltage / 3.3) * 14.0;
String phStatus = "Neutral (NETRAL)"; // Status default
int turbidityValue = analogRead(turbidityPin); // Membaca sensor kekeruhan (nilai potensiometer)
int turbidityNTU = map(turbidityValue, 0, 4095, 0, 100); // Memetakan ke NTU (0-100 skala)
// Menampilkan data di LCD 20x4
lcd.setCursor(0,0);
lcd.print("Suhu: ");
lcd.print(temperatureC);
lcd.println(" C");
lcd.setCursor(0,1);
lcd.print("pH: ");
lcd.print(phScale);
lcd.setCursor(0,2);
lcd.print("Turbidity: ");
lcd.print(turbidityNTU);
lcd.println(" NTU");
if (temperatureC > 28.0) {
temperatureStatus = "Suhu Terlalu Tinggi"; // Jika suhu lebih besar dari 28
} else if (temperatureC < 18.0) {
temperatureStatus = "Suhu Terlalu Rendah"; // Jika suhu lebih kecil dari 18
} else {
temperatureStatus = "Normal"; // Jika suhu antara 18 hingga 28
}
Serial.print("Suhu Air: ");
Serial.println(temperatureC); // Output nilai pH (simulasi)
Serial.print("Status Suhu: ");
Serial.println(temperatureStatus); // Output status pH
// Menentukan status pH berdasarkan tegangan
if (phScale < 7) {
phStatus = "Acidic (Asam)"; // Asam
} else if (phScale > 7) {
phStatus = "Basic (Basa)"; // Basa
} else {
phStatus = "Neutral (Netral)"; // Netral
}
// Menampilkan nilai pH dan statusnya
Serial.print("Nilai pH: ");
Serial.println(phScale); // Output nilai pH (simulasi)
Serial.print("Status pH: ");
Serial.println(phStatus); // Output status pH
// Menampilkan nilai kekeruhan dan statusnya
Serial.print("Nilai Turbidity: ");
Serial.println(turbidityValue); // Output nilai kekeruhan
Serial.print("Turbidity NTU: ");
Serial.println(turbidityNTU); // Output nilai kekeruhan NTU
// Menentukan status kekeruhan berdasarkan NTU
String turbidityStatus = "Clear (BERSIH)";
if (turbidityNTU > 50) {
turbidityStatus = "Turbid (KERUH)";
}
Serial.print("Turbidity Status: ");
Serial.println(turbidityStatus); // Output status kekeruhan
Serial.println("--------------");
// delay(5000);
}
// Fungsi untuk mengirimkan data sensor ke Blynk
void sendData() {
// Mengirim data ke Blynk
int phValue = analogRead(phPin);
float phVoltage = phValue * (3.3 / 4095.0);
float phScale = (phVoltage / 3.3) * 14.0; // Skala pH dari 0 hingga 14
int turbidityValue = analogRead(turbidityPin);
int turbidityNTU = map(turbidityValue, 0, 4095, 0, 100);
sensors.requestTemperatures();
float temperatureC = sensors.getTempCByIndex(0); // Mendapatkan suhu dari DS18B20
String temperatureStatus = "Normal"; // Status default
String phStatus = "Neutral"; // Status default
String turbidityStatus = "Normal"; // Status default
// Menentukan status kekeruhan berdasarkan nilai NTU
if (turbidityNTU < 5) {
turbidityStatus = "Air Jernih"; // Jika NTU lebih kecil dari 5
} else if (turbidityNTU > 30) {
turbidityStatus = "Air Keruh"; // Jika NTU lebih besar dari 30
} else {
turbidityStatus = "Normal"; // Jika NTU antara 5 hingga 30
}
if (temperatureC > 28.0) {
temperatureStatus = "Suhu Terlalu Tinggi"; // Jika suhu lebih besar dari 28
} else if (temperatureC < 18.0) {
temperatureStatus = "Suhu Terlalu Rendah"; // Jika suhu lebih kecil dari 18
} else {
temperatureStatus = "Normal"; // Jika suhu antara 18 hingga 28
}
// Menentukan status pH berdasarkan tegangan
if (phScale < 7) {
phStatus = "Acidic (Asam)"; // Asam
} else if (phScale > 7) {
phStatus = "Basic (Basa)"; // Basa
} else {
phStatus = "Neutral (Netral)"; // Netral
}
// Menampilkan data suhu, pH, dan turbidity di aplikasi Blynk
Blynk.virtualWrite(V2, temperatureC); // V2 untuk suhu
Blynk.virtualWrite(V6, temperatureStatus);
Blynk.virtualWrite(V3, phScale); // V3 untuk pH
Blynk.virtualWrite(V7, phStatus);
Blynk.virtualWrite(V4, turbidityNTU); // V4 untuk turbidity
Blynk.virtualWrite(V8, turbidityStatus);
}
// Fungsi untuk kontrol pemberian pakan otomatis sesuai jadwal
void checkPakanSchedule() {
DateTime now = rtc.now();
// Cek apakah waktu sekarang sesuai dengan jadwal pakan otomatis
if (now.hour() == scheduledHour && now.minute() == scheduledMinute) {
kontrol_katup(4, 1500, 30); // Kontrol pemberian pakan otomatis
}
}
// Fungsi untuk mengupdate jadwal pakan otomatis melalui aplikasi Blynk
BLYNK_WRITE(V5) {
int totalMinutes = param.asInt(); // Mendapatkan nilai dalam menit
scheduledHour = (totalMinutes / 60) % 24; // Jam dihitung dari pembagian dengan 60 dan diubah ke format 24 jam
scheduledMinute = totalMinutes % 60; // Menit dihitung dari sisa pembagian dengan 60
// Menampilkan jam dan menit yang telah dihitung
Serial.print("Jam: ");
Serial.print(scheduledHour); // Menampilkan jam
Serial.print(" Menit: ");
Serial.println(scheduledMinute); // Menampilkan menit
// Menampilkan total menit yang dimasukkan oleh pengguna
Serial.print("Total menit: ");
Serial.println(totalMinutes); // Menampilkan nilai menit
}
// Fungsi untuk kontrol servo (memberi makan)
void kontrol_katup(int jumlahBuka, int jedawaktu, int sudutServo) {
Serial.println("Kasih Makan .....");
// Menyalakan LED untuk indikasi pemberian pakan
digitalWrite(ledPin, HIGH);
// Menggerakkan servo ke sudut yang ditentukan
servo1.write(sudutServo); // Atur posisi servo sesuai sudut
delay(jedawaktu); // Tunggu sebentar
servo1.write(0); // Reset posisi servo
delay(jedawaktu); // Delay reset servo
}
// Fungsi untuk mengatur tombol push yang mengendalikan servo (V0)
BLYNK_WRITE(V0) {
int value = param.asInt(); // Membaca status tombol
if (value == 1) {
// Jika tombol ditekan, buka servo 2 kali
for (int i = 0; i < 2; i++) {
kontrol_katup(1, 500, 45); // Buka servo 1 kali dengan sudut 45 derajat
// delay(500); // Jeda 500ms setelah buka servo pertama
kontrol_katup(1, 500, 0); // Tutup servo (kembali ke posisi 0 derajat)
// delay(500); // Jeda 500ms setelah tutup servo pertama
}
}
}