320 lines
9.0 KiB
C++
320 lines
9.0 KiB
C++
code asli
|
|
|
|
|
|
#include <WiFi.h>
|
|
#include <Firebase_ESP_Client.h>
|
|
#include <OneWire.h>
|
|
#include <DallasTemperature.h>
|
|
#include <time.h>
|
|
#include <Wire.h>
|
|
#include <LiquidCrystal_I2C.h>
|
|
#include <ESP32Servo.h>
|
|
#include <RTClib.h>
|
|
#include <HTTPClient.h>
|
|
|
|
|
|
// 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);
|
|
} |