TKK_E32221196/Code Arduino My Fish/myfish.ino

308 lines
8.4 KiB
C++

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <time.h>
#include <ESP32Servo.h>
// WiFi credentials
const char* ssid = "KOPI";
const char* password = "digoreng123";
// Firebase configuration
const String firebaseHost = "https://my-fish-7db48-default-rtdb.firebaseio.com/";
const String apiKey = "AIzaSyCGMJfu0R3LmJp6IIxoTyspgJpC-0slcCE";
// Pakan sensor & actuator
#define TRIG_PAKAN 12
#define ECHO_PAKAN 13
#define SERVO_PIN 15
#define BUTTON_PIN 4
#define LED_PIN 2
#define POMPA_RELAY 19
#define PEMBERSIH_RELAY 18
Servo servoPakan;
// Flags
bool kontrolPakan = false;
bool kontrolPembersih = false;
bool statusPembersihManual = false;
bool pompaOverride = false;
// Timing
unsigned long lastSensorSend = 0;
unsigned long intervalSensor = 60000;
unsigned long lastCleaner = 0;
unsigned long intervalPembersih = 60 * 60000;
unsigned long lastJadwalUpdate = 0;
unsigned long lastBlink = 0;
bool ledState = false;
unsigned long waktuPembersihBerikutnya = 0;
int intervalSetelahPakan = 3 * 60 * 1000;
// Settings
const float tinggiPakanMax = 7.0; // cm
// Jadwal pakan
#define MAX_JADWAL 10
struct JadwalPakan {
int jam;
int menit;
int takar;
};
JadwalPakan jadwalPakan[MAX_JADWAL];
int jumlahJadwal = 0;
int lastFeedingDay[MAX_JADWAL];
void setup() {
Serial.begin(115200);
pinMode(TRIG_PAKAN, OUTPUT);
pinMode(ECHO_PAKAN, INPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
pinMode(POMPA_RELAY, OUTPUT);
pinMode(PEMBERSIH_RELAY, OUTPUT);
digitalWrite(POMPA_RELAY, HIGH);
digitalWrite(PEMBERSIH_RELAY, HIGH);
servoPakan.attach(SERVO_PIN);
connectToWiFi();
setupNTP();
ambilJadwalPakanFirebase();
ambilSettingFirebase();
}
void loop() {
if (WiFi.status() != WL_CONNECTED) {
if (millis() - lastBlink >= 500) {
ledState = !ledState;
digitalWrite(LED_PIN, ledState);
lastBlink = millis();
}
} else {
digitalWrite(LED_PIN, HIGH);
}
ambilSettingFirebase();
ambilKontrolFirebase();
cekDanEksekusiJadwal();
if (millis() - lastSensorSend >= intervalSensor) {
float jarak = bacaUltrasonik(TRIG_PAKAN, ECHO_PAKAN);
if (jarak != -1) {
float tinggiPakan = hitungTinggiPakan(jarak);
Serial.printf("Tinggi Pakan: %.2f cm\n", tinggiPakan);
kirimKeFirebase(tinggiPakan);
} else {
Serial.println("Gagal membaca sensor ultrasonik.");
}
lastSensorSend = millis();
}
if (millis() - lastCleaner >= intervalPembersih) {
Serial.println("Pembersih otomatis aktif...");
pompaOverride = true;
digitalWrite(PEMBERSIH_RELAY, LOW);
digitalWrite(POMPA_RELAY, LOW);
delay(15000);
digitalWrite(PEMBERSIH_RELAY, HIGH);
digitalWrite(POMPA_RELAY, HIGH);
pompaOverride = false;
lastCleaner = millis();
}
if (millis() >= waktuPembersihBerikutnya && waktuPembersihBerikutnya > 0) {
Serial.println("Pembersih otomatis setelah pakan aktif...");
pompaOverride = true;
digitalWrite(PEMBERSIH_RELAY, LOW);
digitalWrite(POMPA_RELAY, LOW);
delay(15000);
digitalWrite(PEMBERSIH_RELAY, HIGH);
digitalWrite(POMPA_RELAY, HIGH);
pompaOverride = false;
waktuPembersihBerikutnya = 0;
}
static bool lastFirebasePembersih = false;
if (kontrolPembersih != lastFirebasePembersih) {
lastFirebasePembersih = kontrolPembersih;
digitalWrite(PEMBERSIH_RELAY, kontrolPembersih ? LOW : HIGH);
digitalWrite(POMPA_RELAY, kontrolPembersih ? LOW : HIGH);
pompaOverride = kontrolPembersih;
Serial.println(kontrolPembersih ? "Pembersih manual ON" : "OFF");
}
if (kontrolPakan) {
tuangPakan(5);
delay(1000);
}
static bool lastButtonState = HIGH;
bool currentButtonState = digitalRead(BUTTON_PIN);
if (lastButtonState == HIGH && currentButtonState == LOW) {
statusPembersihManual = !statusPembersihManual;
digitalWrite(PEMBERSIH_RELAY, statusPembersihManual ? LOW : HIGH);
digitalWrite(POMPA_RELAY, statusPembersihManual ? LOW : HIGH);
pompaOverride = statusPembersihManual;
Serial.println(statusPembersihManual ? "Tombol Pembersih: ON" : "OFF");
}
lastButtonState = currentButtonState;
if (millis() - lastJadwalUpdate > 100000) {
ambilJadwalPakanFirebase();
lastJadwalUpdate = millis();
}
delay(100);
}
// ======================== FUNGSI =============================
void connectToWiFi() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi Terhubung");
}
void setupNTP() {
configTime(7 * 3600, 0, "pool.ntp.org", "time.nist.gov");
}
float bacaUltrasonik(int trigPin, int echoPin) {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH, 30000);
if (duration == 0) return -1; // Tidak terbaca
return duration * 0.034 / 2;
}
float hitungTinggiPakan(float jarak) {
if (jarak <= 0.0 || jarak > tinggiPakanMax) return 0.0;
return tinggiPakanMax - jarak;
}
void kirimKeFirebase(float tinggiPakan) {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
String url = firebaseHost + "sensor_data.json?auth=" + apiKey;
String payload = "{\"tinggi_pakan\":" + String(tinggiPakan, 2) + "}";
http.begin(url);
http.addHeader("Content-Type", "application/json");
http.PUT(payload);
http.end();
}
}
void ambilKontrolFirebase() {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
String url = firebaseHost + "kontrol_alat.json?auth=" + apiKey;
http.begin(url);
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
String response = http.getString();
DynamicJsonDocument doc(512);
deserializeJson(doc, response);
kontrolPakan = doc["pakan"];
kontrolPembersih = doc["pembersih"];
}
http.end();
}
}
void ambilSettingFirebase() {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
String url = firebaseHost + "setting.json?auth=" + apiKey;
http.begin(url);
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
String response = http.getString();
DynamicJsonDocument doc(512);
deserializeJson(doc, response);
intervalSensor = doc["interval_data"] | 60000;
intervalPembersih = (doc["interval_pembersih"] | 60) * 60000;
}
http.end();
}
}
void ambilJadwalPakanFirebase() {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
String url = firebaseHost + "jadwal_pakan.json?auth=" + apiKey;
http.begin(url);
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
String response = http.getString();
DynamicJsonDocument doc(2048);
if (!deserializeJson(doc, response)) {
jumlahJadwal = 0;
struct tm timeinfo;
getLocalTime(&timeinfo);
int nowJam = timeinfo.tm_hour;
int nowMenit = timeinfo.tm_min;
int today = timeinfo.tm_yday;
for (JsonPair kv : doc.as<JsonObject>()) {
if (jumlahJadwal < MAX_JADWAL) {
String jamStr = kv.value()["jam"].as<String>();
int takar = kv.value()["takar"].as<int>();
int jam = 0, menit = 0;
sscanf(jamStr.c_str(), "%d:%d", &jam, &menit);
jadwalPakan[jumlahJadwal] = {jam, menit, takar};
lastFeedingDay[jumlahJadwal] = (jam < nowJam || (jam == nowJam && menit < nowMenit)) ? today : -1;
jumlahJadwal++;
}
}
for (JsonPair kv : doc.as<JsonObject>()) {
if (kv.value().containsKey("interval_set")) {
intervalSetelahPakan = kv.value()["interval_set"].as<int>() * 60 * 1000;
break;
}
}
}
}
http.end();
}
}
void cekDanEksekusiJadwal() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) return;
int nowJam = timeinfo.tm_hour;
int nowMenit = timeinfo.tm_min;
int today = timeinfo.tm_yday;
for (int i = 0; i < jumlahJadwal; i++) {
if (jadwalPakan[i].jam == nowJam && jadwalPakan[i].menit == nowMenit) {
if (lastFeedingDay[i] != today) {
Serial.printf("Tuang pakan (jadwal ke-%d): %02d:%02d, takar %d\n", i + 1, nowJam, nowMenit, jadwalPakan[i].takar);
tuangPakan(jadwalPakan[i].takar);
lastFeedingDay[i] = today;
waktuPembersihBerikutnya = millis() + intervalSetelahPakan;
delay(1000);
}
}
}
}
void tuangPakan(int takar) {
for (int i = 0; i < takar; i++) {
servoPakan.write(0);
delay(500);
servoPakan.write(90);
delay(500);
}
kontrolPakan = false;
}