#include #include #include #include #include // === WiFi & MQTT === const char* ssid = "Fansyah"; const char* password = "12345678"; const char* mqtt_server = "203.194.113.47"; WiFiClient espClient; PubSubClient client(espClient); // === NTP (UTC+7) === WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "pool.ntp.org", 25200); // === Pin === #define RELAY1 D0 #define RELAY2 D4 #define SERVO_PIN D5 Servo myServo; // === Jadwal Pakan === int morningHour = 6, morningMinute = 0; int eveningHour = 17, eveningMinute = 30; bool morningFed = false, eveningFed = false, modeOtomatis = true; // === Servo Timer === bool servoActive = false; unsigned long servoStartTime = 0; const unsigned long SERVO_DURATION = 60000; // === Data pH & Tegangan === float pHValue = 0.0; float voltageValue = 0.0; unsigned long lastDataReceivedTime = 0; // === Threshold Relay === const float pH_THRESHOLD_LOW = 6.5; const float pH_THRESHOLD_HIGH = 7.5; // === Mode Pompa === bool modePompaOtomatis = true; // default otomatis String perintahManual = "netral"; // default netral void setup() { Serial.begin(9600); pinMode(RELAY1, OUTPUT); pinMode(RELAY2, OUTPUT); digitalWrite(RELAY1, HIGH); digitalWrite(RELAY2, HIGH); myServo.attach(SERVO_PIN); myServo.write(0); // Posisi awal servo WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\nWiFi Terhubung: " + WiFi.localIP().toString()); client.setServer(mqtt_server, 1883); client.setCallback(callback); timeClient.begin(); Serial.println("Menunggu data pH dari Arduino..."); } void loop() { if (!client.connected()) reconnectMQTT(); client.loop(); timeClient.update(); checkSchedule(); readSerialData(); checkServo(); sendCurrentTimeToMQTT(); checkpHLevel(); // Periksa dan kontrol relay } void reconnectMQTT() { while (!client.connected()) { Serial.print("Menghubungkan ke MQTT..."); if (client.connect("ESPFeederClient")) { Serial.println(" Terhubung!"); client.subscribe("feeder/jadwal/siang"); client.subscribe("feeder/jadwal/sore"); client.subscribe("feeder/mode"); client.subscribe("feeder/modepompa"); client.subscribe("feeder/manual/kontrol"); client.publish("feeder/status", "ESP Terhubung"); } else { Serial.print(" Gagal ("); Serial.print(client.state()); Serial.println("). Coba lagi 5 detik..."); delay(5000); } } } void callback(char* topic, byte* payload, unsigned int length) { String msg; for (int i = 0; i < length; i++) msg += (char)payload[i]; msg.trim(); if (String(topic) == "feeder/jadwal/siang") { int sep = msg.indexOf(':'); morningHour = msg.substring(0, sep).toInt(); morningMinute = msg.substring(sep + 1).toInt(); Serial.println("[MQTT] Jadwal Siang: " + msg); } else if (String(topic) == "feeder/jadwal/sore") { int sep = msg.indexOf(':'); eveningHour = msg.substring(0, sep).toInt(); eveningMinute = msg.substring(sep + 1).toInt(); Serial.println("[MQTT] Jadwal Sore: " + msg); } else if (String(topic) == "feeder/mode") { modeOtomatis = (msg == "otomatis"); Serial.println("[MQTT] Mode: " + msg); } else if (String(topic) == "feeder/modepompa") { modePompaOtomatis = (msg == "otomatis"); Serial.println("[MQTT] Mode Pompa: " + msg); } else if (String(topic) == "feeder/manual/kontrol") { perintahManual = msg; Serial.println("[MQTT] Perintah Manual Pompa: " + msg); } } void checkSchedule() { int h = timeClient.getHours(); int m = timeClient.getMinutes(); if (modeOtomatis) { if (h == morningHour && m == morningMinute && !morningFed) { triggerFeeding("Pagi"); morningFed = true; eveningFed = false; } if (h == eveningHour && m == eveningMinute && !eveningFed) { triggerFeeding("Sore"); eveningFed = true; morningFed = false; } if (h != morningHour || m != morningMinute) morningFed = false; if (h != eveningHour || m != eveningMinute) eveningFed = false; } } void triggerFeeding(String waktu) { Serial.println(">> MEMBERI PAKAN " + waktu + " (" + timeClient.getFormattedTime() + ")"); myServo.write(90); // Aktifkan servo servoStartTime = millis(); servoActive = true; String notif = "Pakan diberikan - " + waktu + " | " + timeClient.getFormattedTime(); client.publish("feeder/notifikasi", notif.c_str()); Serial.println("[MQTT] " + notif); } void checkServo() { if (servoActive && millis() - servoStartTime >= SERVO_DURATION) { myServo.write(0); // Kembalikan ke posisi awal servoActive = false; Serial.println("[INFO] Servo dimatikan"); } } void readSerialData() { if (Serial.available()) { String in = Serial.readStringUntil('\n'); in.trim(); int sep = in.indexOf(','); if (sep > 0) { float pH = in.substring(0, sep).toFloat(); float volt = in.substring(sep + 1).toFloat(); String t = timeClient.getFormattedTime(); String ph_payload = String(pH, 2) + " | " + t; String volt_payload = String(volt, 3) + " V | " + t; client.publish("feeder/pH", ph_payload.c_str()); client.publish("feeder/tegangan", volt_payload.c_str()); Serial.printf("[DATA] pH: %.2f | V: %.3f | %s\n", pH, volt, t.c_str()); lastDataReceivedTime = millis(); pHValue = pH; voltageValue = volt; } } if (millis() - lastDataReceivedTime > 10000 && lastDataReceivedTime != 0) { String t = timeClient.getFormattedTime(); client.publish("feeder/pH", ("0 | " + t).c_str()); client.publish("feeder/tegangan", ("0 V | " + t).c_str()); Serial.println("[WARNING] Timeout data pH"); lastDataReceivedTime = 0; } } void kirimStatusPompa(String status) { client.publish("feeder/statuspompa", status.c_str()); Serial.println("[MQTT] Status Pompa: " + status); } void checkpHLevel() { String status = "Pompa OFF"; if (modePompaOtomatis) { Serial.print(">> [Otomatis] Pemeriksaan pH: "); Serial.println(pHValue, 2); if (pHValue < pH_THRESHOLD_LOW) { digitalWrite(RELAY1, LOW); digitalWrite(RELAY2, HIGH); status = "Pompa 1 Aktif (Otomatis - pH rendah)"; } else if (pHValue > pH_THRESHOLD_HIGH) { digitalWrite(RELAY1, HIGH); digitalWrite(RELAY2, LOW); status = "Pompa 2 Aktif (Otomatis - pH tinggi)"; } else { digitalWrite(RELAY1, HIGH); digitalWrite(RELAY2, HIGH); status = "Tidak ada Pompa Aktif (Otomatis - pH normal)"; } } else { Serial.print(">> [Manual] Perintah: "); Serial.println(perintahManual); if (perintahManual == "ph_rendah") { digitalWrite(RELAY1, LOW); digitalWrite(RELAY2, HIGH); status = "Pompa 1 Aktif (Manual - pH rendah)"; } else if (perintahManual == "ph_tinggi") { digitalWrite(RELAY1, HIGH); digitalWrite(RELAY2, LOW); status = "Pompa 2 Aktif (Manual - pH tinggi)"; } else { digitalWrite(RELAY1, HIGH); digitalWrite(RELAY2, HIGH); status = "Tidak ada Pompa Aktif (Manual - Netral)"; } } kirimStatusPompa(status); } void sendCurrentTimeToMQTT() { static unsigned long last = 0; if (millis() - last >= 1000) { last = millis(); String t = timeClient.getFormattedTime(); client.publish("feeder/waktu", t.c_str()); Serial.println("[MQTT] Waktu dikirim: " + t); } }