code asli #include #include #include #include #include #include #include #include #include #include // WiFi credentials const char* ssid = "HIDROFISH"; const char* password = "ikan1234"; // Firebase config #define API_KEY "AIzaSyCIJ5xVWDHHuhLGKV-OFYlLXBvjrKtI7ic" #define DATABASE_URL "https://hydrofish-e00a3-default-rtdb.firebaseio.com/" #define LEGACY_TOKEN "m2l1lmVvUrVTfUmwfkN5bVNKzifh2nM8DNLSd3c2" // Pin definitions #define TDS_PIN 34 #define TURBIDITY_PIN 33 #define PH_PIN 35 #define TRIG_PIN 26 #define ECHO_PIN 25 #define DS18B20_PIN 32 #define BUZZER_PIN 15 #define LED_PIN 2 #define SERVO_PIN 19 // Objects RTC_DS3231 rtc; FirebaseData fbdo; FirebaseAuth auth; FirebaseConfig config; OneWire oneWire(DS18B20_PIN); DallasTemperature sensors(&oneWire); LiquidCrystal_I2C lcd(0x27, 16, 2); Servo feederServo; // Variables bool firebaseConnected = false; int lcdScreen = 0; String pagiTime = "08:00"; String soreTime = "13:00"; String malemTime = "19:00"; int jumlahTakar = 1; String lastFeedingTime = ""; unsigned long previousMillis = 0; const long interval = 5000; float ph_slope = -6.548; float ph_offset = 25.69; unsigned long lastHistorySentMillis = 0; int intervalHistoryMinutes = 5; // default 5 menit // ===================== SETUP ===================== void setup() { Serial.begin(115200); Wire.begin(22, 21); sensors.begin(); lcd.init(); lcd.backlight(); pinMode(LED_PIN, OUTPUT); pinMode(TRIG_PIN, OUTPUT); pinMode(ECHO_PIN, INPUT); pinMode(BUZZER_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); analogReadResolution(12); feederServo.attach(SERVO_PIN); feederServo.write(0); setupWiFi(); if (!rtc.begin()) { Serial.println("RTC tidak ditemukan!"); while (1); } if (rtc.lostPower()) { Serial.println("RTC kehilangan daya, set waktu ke waktu compile."); rtc.adjust(DateTime(F(_DATE), F(TIME_))); } configTime(7 * 3600, 0, "pool.ntp.org", "time.nist.gov"); setupFirebase(); } // ===================== WIFI SETUP ===================== void setupWiFi() { Serial.print("Menghubungkan ke WiFi..."); WiFi.begin(ssid, password); unsigned long startTime = millis(); while (WiFi.status() != WL_CONNECTED && millis() - startTime < 10000) { delay(500); Serial.print("."); } if (WiFi.status() == WL_CONNECTED) { Serial.println("\nWiFi terhubung!"); Serial.print("IP: "); Serial.println(WiFi.localIP()); } else { Serial.println("\nGagal terhubung ke WiFi."); } } // ===================== FIREBASE SETUP ===================== void setupFirebase() { config.api_key = API_KEY; config.database_url = DATABASE_URL; config.signer.tokens.legacy_token = LEGACY_TOKEN; Firebase.begin(&config, &auth); Firebase.reconnectWiFi(true); } void getIntervalHistory() { if (WiFi.status() != WL_CONNECTED) return; if (Firebase.RTDB.getInt(&fbdo, "/setting/interval_history")) { intervalHistoryMinutes = fbdo.intData(); Serial.print("Interval history diambil dari Firebase: "); Serial.print(intervalHistoryMinutes); Serial.println(" menit"); } else { Serial.println("Gagal ambil interval_history: " + fbdo.errorReason()); } } void kirimHistoryKeServer(float ph, float suhu, float tds, float tinggi, String turbidity) { if (WiFi.status() != WL_CONNECTED) return; HTTPClient http; http.begin("https://hydrofish-api-2ywj.vercel.app/api/kirimdata"); http.addHeader("Content-Type", "application/json"); // Dapatkan waktu UTC ISO 8601 time_t now; struct tm timeinfo; if (!getLocalTime(&timeinfo)) { Serial.println("Gagal mendapatkan waktu"); return; } char timestamp[30]; strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S.000Z", &timeinfo); String json = "{"; json += "\"ph_air\":" + String(ph, 2) + ","; json += "\"suhu_air\":" + String(suhu, 2) + ","; json += "\"tds\":" + String(tds, 2) + ","; json += "\"timestamp\":\"" + String(timestamp) + "\","; json += "\"tinggi_air\":" + String(tinggi, 1) + ","; json += "\"turbidity\":\"" + turbidity + "\""; json += "}"; int httpResponseCode = http.POST(json); if (httpResponseCode > 0) { Serial.print("Data history terkirim: "); Serial.println(httpResponseCode); } else { Serial.print("Gagal kirim data history: "); Serial.println(httpResponseCode); } http.end(); } // ===================== WAKTU ===================== String getRTCTime() { DateTime now = rtc.now(); char buffer[6]; sprintf(buffer, "%02d:%02d", now.hour(), now.minute()); return String(buffer); } String getCurrentTime() { struct tm timeinfo; if (WiFi.status() == WL_CONNECTED && getLocalTime(&timeinfo)) { char buffer[6]; sprintf(buffer, "%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min); return String(buffer); } else { return getRTCTime(); // fallback ke RTC } } // ===================== SENSOR ===================== float konversiKeNTU(float voltage) { float ntu = -1120.4 * voltage * voltage + 5742.3 * voltage - 4352.9; return (ntu < 0) ? 0 : ntu; } String bacaKekeruhan(float voltage) { return (konversiKeNTU(voltage) < 1500.0) ? "Keruh" : "Jernih"; } void cekKekeruhan(float voltage) { digitalWrite(BUZZER_PIN, (konversiKeNTU(voltage) < 1500.0) ? HIGH : LOW); } void updateLCD(float tds, float turbidity, float phV, float ph, float tinggi, float suhu) { lcd.clear(); switch (lcdScreen) { case 0: lcd.setCursor(0, 0); lcd.print("Hydrofish"); lcd.setCursor(0, 1); lcd.print("Monitoring..."); break; case 1: lcd.setCursor(0, 0); lcd.print("pH Air: "); lcd.print(ph, 2); break; case 2: lcd.setCursor(0, 0); lcd.print("Suhu: "); lcd.print(suhu, 1); lcd.print((char)223); lcd.print("C"); break; case 3: lcd.setCursor(0, 0); lcd.print("TDS: "); lcd.print(tds, 2); lcd.print(" ppm"); break; case 4: lcd.setCursor(0, 0); lcd.print("Tinggi: "); lcd.print(tinggi, 1); lcd.print(" cm"); break; case 5: lcd.setCursor(0, 0); lcd.print("Kekeruhan: "); lcd.setCursor(0, 1); lcd.print(bacaKekeruhan(turbidity)); break; } lcdScreen = (lcdScreen + 1) % 6; } void kirimKeFirebase(float tds, String status, float phV, float ph, float tinggi, float suhu) { if (WiFi.status() != WL_CONNECTED || !Firebase.ready()) return; FirebaseJson json; json.set("tds", tds); json.set("turbidity", status); json.set("ph_voltage", phV); json.set("ph", ph); json.set("tinggi_air", tinggi); json.set("suhu", suhu); if (Firebase.RTDB.setJSON(&fbdo, "/sensor_data", &json)) { Serial.println("Data terkirim ke Firebase"); } else { Serial.println("Gagal kirim: " + fbdo.errorReason()); } } void getFeedingSchedule() { if (WiFi.status() != WL_CONNECTED) return; if (Firebase.RTDB.getString(&fbdo, "/jadwal_pakan/pagi")) pagiTime = fbdo.stringData(); if (Firebase.RTDB.getString(&fbdo, "/jadwal_pakan/sore")) soreTime = fbdo.stringData(); if (Firebase.RTDB.getString(&fbdo, "/jadwal_pakan/malem")) malemTime = fbdo.stringData(); if (Firebase.RTDB.getInt(&fbdo, "/jadwal_pakan/jumlah_takar")) jumlahTakar = fbdo.intData(); } void sendFeedingStatus(bool status) { if (WiFi.status() != WL_CONNECTED) return; FirebaseJson json; json.set("status_pakan", status); Firebase.RTDB.setJSON(&fbdo, "/sensor_data", &json); } void performFeeding() { for (int i = 0; i < jumlahTakar; i++) { feederServo.write(180); delay(2000); feederServo.write(0); delay(2000); } } // ===================== LOOP ===================== void loop() { if (!firebaseConnected && WiFi.status() == WL_CONNECTED && Firebase.ready()) { firebaseConnected = true; digitalWrite(LED_PIN, HIGH); Serial.println("Firebase connected."); } String currentTime = getCurrentTime(); getFeedingSchedule(); getIntervalHistory(); // Ambil interval dari Firebase if ((currentTime == pagiTime || currentTime == soreTime || currentTime == malemTime) && lastFeedingTime != currentTime) { performFeeding(); sendFeedingStatus(true); lastFeedingTime = currentTime; } float tdsV = (analogRead(TDS_PIN) / 4095.0) * 3.3; float turbV = (analogRead(TURBIDITY_PIN) / 4095.0) * 3.3; float phV = (analogRead(PH_PIN) / 4095.0) * 3.3; float ph = ph_slope * phV + ph_offset; digitalWrite(TRIG_PIN, LOW); delayMicroseconds(2); digitalWrite(TRIG_PIN, HIGH); delayMicroseconds(10); digitalWrite(TRIG_PIN, LOW); long duration = pulseIn(ECHO_PIN, HIGH); float tinggiAir = duration * 0.034 / 2; sensors.requestTemperatures(); float suhu = sensors.getTempCByIndex(0); cekKekeruhan(turbV); updateLCD(tdsV, turbV, phV, ph, tinggiAir, suhu); Serial.print("Jam Sekarang: "); Serial.println(currentTime); Serial.print("pH Tegangan: "); Serial.print(phV, 3); Serial.print(" V | pH: "); Serial.println(ph, 2); if (millis() - previousMillis >= interval) { previousMillis = millis(); kirimKeFirebase(tdsV, bacaKekeruhan(turbV), phV, ph, tinggiAir, suhu); } if (millis() - lastHistorySentMillis >= intervalHistoryMinutes * 60000UL) { lastHistorySentMillis = millis(); kirimHistoryKeServer(ph, suhu, tdsV, tinggiAir, bacaKekeruhan(turbV)); } delay(500); }