256 lines
7.3 KiB
C++
256 lines
7.3 KiB
C++
#include <ESP8266WiFi.h>
|
|
#include <PubSubClient.h>
|
|
#include <Servo.h>
|
|
#include <NTPClient.h>
|
|
#include <WiFiUdp.h>
|
|
|
|
// === 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);
|
|
}
|
|
}
|