TKK_E32231503/esp32_pengering_ikan_railwa...

1147 lines
34 KiB
C++

#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <LiquidCrystal_PCF8574.h>
#include <DHT.h>
#include "HX711.h"
// =====================================================
// WIFI
// =====================================================
const char* ssid = "Bemby 1";
const char* password = "BISMILLAH88";
// =====================================================
// MQTT
// =====================================================
const char* mqtt_server = "broker.hivemq.com";
const int mqtt_port = 1883;
WiFiClient espClient;
PubSubClient client(espClient);
// =====================================================
// MQTT TOPIC (Sesuai dengan Backend Railway)
// =====================================================
const char* topicData = "novil/pengering/data";
const char* topicStatus = "novil/pengering/status";
const char* topicControl = "novil/pengering/control";
const char* topicButton = "novil/pengering/button";
// =====================================================
// LCD
// =====================================================
LiquidCrystal_PCF8574 lcd(0x27);
// =====================================================
// BUTTON
// =====================================================
#define BUTTON_PIN 18
bool lastButtonState = HIGH;
// =====================================================
// DHT22
// =====================================================
#define DHTPIN 4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// =====================================================
// HX711
// =====================================================
#define DT1 25
#define SCK1 26
#define DT2 33 // Sesuai dengan kode test yang working
#define SCK2 32 // Sesuai dengan kode test yang working
HX711 scale1;
HX711 scale2;
// =====================================================
// RELAY
// =====================================================
#define RELAY_HEATER1 13 // RELAY1 - Heater 1
#define RELAY_HEATER2 12 // RELAY2 - Heater 2
#define RELAY_FAN 14 // RELAY3 - Fan
#define RELAY_EXHAUST 27 // RELAY4 - Exhaust
// =====================================================
// KALIBRASI LOADCELL
// =====================================================
float calibration_factor1 = 709.0; // Sesuai dengan kode test yang working
float calibration_factor2 = 709.0; // Sesuai dengan kode test yang working
// =====================================================
// VARIABEL
// =====================================================
float berat_awal = 0;
float berat_trigger = 0;
bool modePengeringan = false;
bool pengeringanSelesai = false;
bool beratTersimpan = false;
unsigned long waktuMulaiBerat = 0;
// =====================================================
// FUNCTION
// =====================================================
void tampilReady();
void tampilSelesai();
void kirimStatusRelay(float suhu, float berat);
void kirimDataMQTT(float suhu, float berat);
// =====================================================
// WIFI CONNECT
// =====================================================
void setup_wifi() {
Serial.println();
Serial.println("Starting WiFi connection...");
// Tampilkan status connecting di LCD
lcd.clear();
delay(50);
lcd.setCursor(0, 0);
lcd.print("Connecting WiFi");
lcd.setCursor(0, 1);
lcd.print("Please wait");
Serial.print("Connecting to: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
int attempts = 0;
int maxAttempts = 20;
// Loop dengan update LCD setiap detik untuk mencegah stuck
while (WiFi.status() != WL_CONNECTED && attempts < maxAttempts) {
delay(500);
Serial.print(".");
// Update LCD setiap 2 attempts (1 detik)
if (attempts % 2 == 0) {
lcd.setCursor(0, 1);
lcd.print(" "); // Clear line
lcd.setCursor(0, 1);
lcd.print("Wait ");
lcd.print(attempts / 2);
lcd.print("s/");
lcd.print(maxAttempts / 2);
lcd.print("s");
}
attempts++;
}
Serial.println(); // New line after dots
if (WiFi.status() == WL_CONNECTED) {
Serial.println("WiFi Connected");
Serial.print("IP Address : ");
Serial.println(WiFi.localIP());
// Tampilkan WiFi connected di LCD
lcd.clear();
delay(50);
lcd.setCursor(0, 0);
lcd.print("WiFi Connected!");
lcd.setCursor(0, 1);
lcd.print(WiFi.localIP());
delay(2000);
} else {
Serial.println("WiFi Connection Failed!");
// Tampilkan error di LCD
lcd.clear();
delay(50);
lcd.setCursor(0, 0);
lcd.print("WiFi Failed!");
lcd.setCursor(0, 1);
lcd.print("Check settings");
delay(3000);
}
}
// =====================================================
// MQTT CALLBACK
// =====================================================
void callback(char* topic, byte* payload, unsigned int length) {
String message = "";
for (int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.print("MQTT Message [");
Serial.print(topic);
Serial.print("] : ");
Serial.println(message);
if (String(topic) == topicControl) {
// =================================================
// HEATER 1
// =================================================
if (message == "HEATER_ON") {
digitalWrite(RELAY_HEATER1, LOW);
digitalWrite(RELAY_HEATER2, LOW);
Serial.println("HEATER 1 & 2 ON");
}
else if (message == "HEATER_OFF") {
digitalWrite(RELAY_HEATER1, HIGH);
digitalWrite(RELAY_HEATER2, HIGH);
Serial.println("HEATER 1 & 2 OFF");
}
// =================================================
// FAN
// =================================================
else if (message == "FAN_ON") {
digitalWrite(RELAY_FAN, LOW);
Serial.println("FAN ON");
}
else if (message == "FAN_OFF") {
digitalWrite(RELAY_FAN, HIGH);
Serial.println("FAN OFF");
}
// =================================================
// LAMP (tidak digunakan, untuk kompatibilitas)
// =================================================
else if (message == "LAMP_ON") {
Serial.println("LAMP ON (tidak tersedia)");
}
else if (message == "LAMP_OFF") {
Serial.println("LAMP OFF (tidak tersedia)");
}
// =================================================
// EXHAUST
// =================================================
else if (message == "EXHAUST_ON") {
digitalWrite(RELAY_EXHAUST, LOW);
Serial.println("EXHAUST ON");
}
else if (message == "EXHAUST_OFF") {
digitalWrite(RELAY_EXHAUST, HIGH);
Serial.println("EXHAUST OFF");
}
// =================================================
// START MQTT (dari Flutter via Backend)
// =================================================
else if (message == "START") {
if (modePengeringan || pengeringanSelesai) {
Serial.println("START DIABAIKAN - Sistem sedang berjalan atau selesai");
client.publish(topicStatus, "TIDAK BISA START SEKARANG");
return;
}
Serial.println("START MQTT (dari aplikasi)");
// Tare HX711 saat mulai pengeringan dengan timeout
Serial.println("Taring scales...");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Calibrating...");
lcd.setCursor(0, 1);
lcd.print("Please wait");
// Cek apakah scale ready sebelum tare
bool scale1Ready = scale1.is_ready();
bool scale2Ready = scale2.is_ready();
Serial.print("Scale 1 ready: ");
Serial.println(scale1Ready ? "YES" : "NO");
Serial.print("Scale 2 ready: ");
Serial.println(scale2Ready ? "YES" : "NO");
if (scale1Ready && scale2Ready) {
// Tare dengan tracking waktu
unsigned long tareStart = millis();
scale1.tare();
unsigned long tare1Time = millis() - tareStart;
Serial.print("Scale 1 tared in ");
Serial.print(tare1Time);
Serial.println("ms");
delay(100);
tareStart = millis();
scale2.tare();
unsigned long tare2Time = millis() - tareStart;
Serial.print("Scale 2 tared in ");
Serial.print(tare2Time);
Serial.println("ms");
delay(100);
Serial.println("Scales tare complete");
} else {
Serial.println("Scale(s) NOT READY - skipping tare");
if (!scale1Ready) Serial.println("Scale 1 not responding");
if (!scale2Ready) Serial.println("Scale 2 not responding");
client.publish(topicStatus, "WARNING: Scale not ready");
}
modePengeringan = true;
pengeringanSelesai = false;
beratTersimpan = false;
berat_awal = 0;
berat_trigger = 0;
waktuMulaiBerat = 0;
lcd.clear();
client.publish(topicStatus, "PENGERINGAN DIMULAI");
}
// =================================================
// RESET MQTT (dari Flutter via Backend)
// =================================================
else if (message == "RESET") {
Serial.println("RESET MQTT (dari aplikasi)");
modePengeringan = false;
pengeringanSelesai = false;
beratTersimpan = false;
berat_awal = 0;
berat_trigger = 0;
waktuMulaiBerat = 0;
// Matikan semua relay
digitalWrite(RELAY_HEATER1, HIGH);
digitalWrite(RELAY_HEATER2, HIGH);
digitalWrite(RELAY_FAN, HIGH);
digitalWrite(RELAY_EXHAUST, HIGH);
lcd.clear();
tampilReady();
client.publish(topicStatus, "PENGERINGAN SIAP");
}
// =================================================
// TARE COMMAND (Kalibrasi ulang load cell ke 0)
// =================================================
else if (message == "TARE") {
Serial.println("TARE COMMAND received");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Taring...");
lcd.setCursor(0, 1);
lcd.print("Remove all load");
delay(2000);
bool scale1Ready = scale1.wait_ready_timeout(1000);
bool scale2Ready = scale2.wait_ready_timeout(1000);
if (scale1Ready && scale2Ready) {
scale1.tare(10);
delay(100);
scale2.tare(10);
delay(100);
Serial.println("Manual tare successful");
client.publish(topicStatus, "TARE COMPLETED");
lcd.setCursor(0, 1);
lcd.print("Done! ");
delay(1000);
} else {
Serial.println("TARE FAILED - scales not ready");
client.publish(topicStatus, "TARE FAILED");
lcd.setCursor(0, 1);
lcd.print("Failed! ");
delay(2000);
}
lcd.clear();
tampilReady();
}
}
}
// =====================================================
// MQTT CONNECT
// =====================================================
void reconnect() {
int attempts = 0;
int maxAttempts = 3;
while (!client.connected() && attempts < maxAttempts) {
Serial.print("Connecting MQTT (attempt ");
Serial.print(attempts + 1);
Serial.print("/");
Serial.print(maxAttempts);
Serial.println(")...");
// Tampilkan status di LCD dengan update setiap attempt
if (modePengeringan == false && pengeringanSelesai == false) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Connecting MQTT");
lcd.setCursor(0, 1);
lcd.print("Attempt ");
lcd.print(attempts + 1);
lcd.print("/");
lcd.print(maxAttempts);
}
// Generate unique client ID
String clientId = "ESP32_PENGERING_";
clientId += String(random(0xffff), HEX);
if (client.connect(clientId.c_str())) {
Serial.println("MQTT Connected");
Serial.print("Client ID: ");
Serial.println(clientId);
// Subscribe ke topic control
client.subscribe(topicControl);
Serial.print("Subscribed to: ");
Serial.println(topicControl);
// Publish status connected
client.publish(topicStatus, "ESP32 CONNECTED");
// Tampilkan MQTT connected di LCD
if (modePengeringan == false && pengeringanSelesai == false) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("MQTT Connected!");
lcd.setCursor(0, 1);
lcd.print("System Ready");
delay(2000);
}
return; // Keluar dari fungsi setelah berhasil connect
}
else {
Serial.print("MQTT Failed, rc=");
Serial.print(client.state());
Serial.println(" retrying...");
// Update LCD menunjukkan gagal
if (modePengeringan == false && pengeringanSelesai == false) {
lcd.setCursor(0, 1);
lcd.print("Failed! Retry...");
}
attempts++;
if (attempts < maxAttempts) {
delay(2000); // Tunggu sebelum retry
}
}
}
// Jika gagal setelah semua attempts
if (!client.connected()) {
Serial.println("MQTT Connection failed after all attempts");
if (modePengeringan == false && pengeringanSelesai == false) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("MQTT Failed!");
lcd.setCursor(0, 1);
lcd.print("Check network");
delay(3000);
}
}
}
// =====================================================
// SETUP
// =====================================================
void setup() {
Serial.begin(115200);
delay(1000); // Beri waktu serial monitor untuk ready
Serial.println();
Serial.println("=================================");
Serial.println("ESP32 Pengering Ikan - Railway");
Serial.println("=================================");
// Setup I2C untuk LCD
Serial.println("Initializing I2C...");
Wire.begin(21, 22);
delay(100);
// Setup LCD dengan error handling
Serial.println("Initializing LCD...");
lcd.begin(16, 2);
delay(100);
lcd.setBacklight(255);
delay(100);
lcd.clear();
delay(100);
// Test LCD - tampilkan pesan booting
lcd.setCursor(0, 0);
lcd.print("ESP32 Pengering");
lcd.setCursor(0, 1);
lcd.print("Initializing...");
delay(2000);
Serial.println("LCD initialized successfully");
// Setup WiFi
setup_wifi();
// Setup MQTT
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
client.setKeepAlive(60);
// Setup Button
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Setup DHT22
Serial.println("Initializing DHT22...");
dht.begin();
delay(100);
// Setup HX711
Serial.println("Initializing HX711...");
// Inisialisasi HX711 (style kode test yang working)
scale1.begin(DT1, SCK1);
Serial.println("HX711 #1 begin OK");
scale2.begin(DT2, SCK2);
Serial.println("HX711 #2 begin OK");
scale1.set_scale(calibration_factor1);
scale2.set_scale(calibration_factor2);
Serial.println("Calibration factor applied");
// Update LCD
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Checking scales");
lcd.setCursor(0, 1);
lcd.print("Please wait...");
// Menunggu HX711 siap dengan timeout 10 detik (style kode test)
Serial.println("Menunggu HX711 siap...");
unsigned long startTime = millis();
bool scale1Ready = false;
bool scale2Ready = false;
while ((!scale1.is_ready() || !scale2.is_ready()) &&
millis() - startTime < 10000) {
scale1Ready = scale1.is_ready();
scale2Ready = scale2.is_ready();
Serial.print("HX1: ");
Serial.print(scale1Ready ? "1" : "0");
Serial.print(" | HX2: ");
Serial.println(scale2Ready ? "1" : "0");
// Update LCD dengan status
lcd.setCursor(0, 1);
lcd.print("HX1:");
lcd.print(scale1Ready ? "OK" : "..");
lcd.print(" HX2:");
lcd.print(scale2Ready ? "OK" : "..");
lcd.print(" ");
delay(500);
// Jika keduanya sudah ready, keluar dari loop
if (scale1Ready && scale2Ready) break;
}
Serial.println();
// Check final status
scale1Ready = scale1.is_ready();
scale2Ready = scale2.is_ready();
if (!scale1Ready) {
Serial.println("ERROR: HX711 #1 tidak terdeteksi!");
} else {
Serial.println("HX711 #1 siap");
}
if (!scale2Ready) {
Serial.println("ERROR: HX711 #2 tidak terdeteksi!");
} else {
Serial.println("HX711 #2 siap");
}
// Tare hanya jika siap (style kode test)
if (scale1Ready) {
Serial.println("Tare HX711 #1...");
lcd.setCursor(0, 1);
lcd.print("Taring scale 1..");
scale1.tare();
delay(100);
}
if (scale2Ready) {
Serial.println("Tare HX711 #2...");
lcd.setCursor(0, 1);
lcd.print("Taring scale 2..");
scale2.tare();
delay(100);
}
if (scale1Ready || scale2Ready) {
lcd.setCursor(0, 1);
lcd.print("Tare complete! ");
delay(1000);
} else {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Scale Warning!");
lcd.setCursor(0, 1);
lcd.print("Check wiring");
delay(3000);
}
Serial.println("HX711 Setup selesai");
// Setup Relay (Active LOW)
pinMode(RELAY_HEATER1, OUTPUT);
pinMode(RELAY_HEATER2, OUTPUT);
pinMode(RELAY_FAN, OUTPUT);
pinMode(RELAY_EXHAUST, OUTPUT);
// Matikan semua relay
digitalWrite(RELAY_HEATER1, HIGH);
digitalWrite(RELAY_HEATER2, HIGH);
digitalWrite(RELAY_FAN, HIGH);
digitalWrite(RELAY_EXHAUST, HIGH);
// Tampilkan ready
lcd.clear();
delay(100);
tampilReady();
delay(500);
Serial.println("Setup Complete!");
Serial.println("=================================");
}
// =====================================================
// LOOP
// =====================================================
void loop() {
// =====================================================
// MQTT Connection Check
// =====================================================
if (!client.connected()) {
reconnect();
}
client.loop();
// =====================================================
// BUTTON
// =====================================================
bool buttonState = digitalRead(BUTTON_PIN);
if (lastButtonState == HIGH && buttonState == LOW) {
Serial.println("BUTTON PRESSED");
client.publish(topicButton, "BUTTON PRESSED");
// =================================================
// STATE: READY TO DRY
// Tekan button → mulai pengeringan
// =================================================
if (!modePengeringan && !pengeringanSelesai) {
Serial.println("START PENGERINGAN");
// Tare HX711 saat mulai pengeringan (style kode test)
Serial.println("Taring scales...");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Zeroing...");
lcd.setCursor(0, 1);
lcd.print("Remove all load");
delay(2000); // Kasih waktu user untuk pastikan rak kosong
// Cek apakah scale ready sebelum tare
bool scale1Ready = scale1.is_ready();
bool scale2Ready = scale2.is_ready();
Serial.print("Scale 1 ready: ");
Serial.println(scale1Ready ? "YES" : "NO");
Serial.print("Scale 2 ready: ");
Serial.println(scale2Ready ? "YES" : "NO");
bool tareSuccess = false;
if (scale1Ready && scale2Ready) {
lcd.setCursor(0, 1);
lcd.print("Calibrating... ");
scale1.tare();
Serial.println("Scale 1 tared");
delay(100);
scale2.tare();
Serial.println("Scale 2 tared");
delay(100);
lcd.setCursor(0, 1);
lcd.print("Done! Put fish ");
delay(1000);
tareSuccess = true;
} else {
if (!scale1Ready) Serial.println("Scale 1 not ready!");
if (!scale2Ready) Serial.println("Scale 2 not ready!");
}
if (!tareSuccess) {
Serial.println("WARNING: Tare FAILED");
lcd.setCursor(0, 1);
lcd.print("Scale Error! ");
delay(2000);
// Batal start jika scale tidak ready
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Cannot Start!");
lcd.setCursor(0, 1);
lcd.print("Check scales");
delay(3000);
lcd.clear();
tampilReady();
lastButtonState = buttonState;
return; // Keluar dari loop, jangan lanjut start
}
Serial.println("Scales tare complete");
// Publish ke topicButton dan topicControl
client.publish(topicButton, "START_BUTTON");
client.publish(topicControl, "START");
modePengeringan = true;
pengeringanSelesai = false;
beratTersimpan = false;
berat_awal = 0;
berat_trigger = 0;
waktuMulaiBerat = 0;
lcd.clear();
client.publish(topicStatus, "PENGERINGAN DIMULAI");
}
delay(300);
}
lastButtonState = buttonState;
// =====================================================
// MODE READY — belum ada button press
// =====================================================
if (!modePengeringan) {
tampilReady();
delay(500);
return;
}
// =====================================================
// MODE PENGERINGAN
// =====================================================
// =====================================================
// DHT22
// =====================================================
float suhu = dht.readTemperature();
if (isnan(suhu)) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("DHT ERROR");
Serial.println("DHT ERROR");
client.publish(topicStatus, "DHT ERROR");
delay(1000);
return;
}
// =====================================================
// HX711
// =====================================================
float berat1 = 0;
float berat2 = 0;
bool scale1Valid = false;
bool scale2Valid = false;
// Cek dan baca scale 1 (style kode test)
if (scale1.is_ready()) {
berat1 = scale1.get_units(5);
scale1Valid = true;
} else {
Serial.println("WARNING: Scale 1 not ready!");
}
// Cek dan baca scale 2 (style kode test)
if (scale2.is_ready()) {
berat2 = scale2.get_units(5);
scale2Valid = true;
} else {
Serial.println("WARNING: Scale 2 not ready!");
}
// Jika kedua scale tidak ready, skip loop ini
if (!scale1Valid && !scale2Valid) {
Serial.println("ERROR: Kedua load cell tidak ready!");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Scale Error!");
lcd.setCursor(0, 1);
lcd.print("Check wiring");
client.publish(topicStatus, "LOAD CELL ERROR");
delay(2000);
return;
}
// =====================================================
// ANTI NOISE
// =====================================================
if (abs(berat1) < 0.5) berat1 = 0;
if (abs(berat2) < 0.5) berat2 = 0;
// =====================================================
// TOTAL BERAT (Jumlah dari kedua load cell)
// =====================================================
float berat = berat1 + berat2;
// =====================================================
// DETEKSI IKAN
// =====================================================
if (!beratTersimpan && berat > 50 && waktuMulaiBerat == 0) {
waktuMulaiBerat = millis();
Serial.println("IKAN TERDETEKSI");
client.publish(topicStatus, "SCAN BERAT...");
}
// =====================================================
// SIMPAN BERAT AWAL
// =====================================================
if (!beratTersimpan &&
waktuMulaiBerat > 0 &&
millis() - waktuMulaiBerat >= 5000) {
berat_awal = berat;
float berat_daging = berat_awal * 0.3;
float berat_air_awal = berat_awal * 0.7;
float sisa_air = berat_air_awal * 0.4;
berat_trigger = berat_daging + sisa_air;
beratTersimpan = true;
Serial.println("==================================");
Serial.println("BERAT AWAL TERSIMPAN");
Serial.print("Berat Awal : ");
Serial.print(berat_awal, 2);
Serial.println(" g");
Serial.print("Berat Daging : ");
Serial.print(berat_daging, 2);
Serial.println(" g (30%)");
Serial.print("Berat Air Awal : ");
Serial.print(berat_air_awal, 2);
Serial.println(" g (70%)");
Serial.print("Sisa Air Target : ");
Serial.print(sisa_air, 2);
Serial.println(" g (40% dari air)");
Serial.print("Target Akhir : ");
Serial.print(berat_trigger, 2);
Serial.println(" g");
Serial.println("==================================");
String msg = "PENGERINGAN BERJALAN - Awal:";
msg += String(berat_awal, 0);
msg += "g Target:";
msg += String(berat_trigger, 0);
msg += "g";
client.publish(topicStatus, msg.c_str());
}
// =====================================================
// TARGET TERCAPAI → SELESAI
// =====================================================
if (beratTersimpan && berat <= berat_trigger) {
static unsigned long targetReachedTime = 0;
static bool targetReachedFlag = false;
// Pertama kali target tercapai
if (!targetReachedFlag) {
targetReachedTime = millis();
targetReachedFlag = true;
Serial.println("==================================");
Serial.println("TARGET TERCAPAI - Verifikasi 10 detik...");
Serial.print("Berat Sekarang: ");
Serial.print(berat, 2);
Serial.println(" g");
Serial.print("Target: ");
Serial.print(berat_trigger, 2);
Serial.println(" g");
Serial.println("==================================");
client.publish(topicStatus, "Verifikasi target...");
}
// Tunggu 10 detik untuk memastikan berat stabil di bawah target
if (millis() - targetReachedTime >= 10000) {
// Setelah 10 detik masih di bawah target, baru selesai
modePengeringan = false;
pengeringanSelesai = true;
targetReachedFlag = false; // Reset flag
// Matikan heater 1, 2 dan fan, nyalakan exhaust untuk cooling
digitalWrite(RELAY_HEATER1, HIGH);
digitalWrite(RELAY_HEATER2, HIGH);
digitalWrite(RELAY_FAN, HIGH);
digitalWrite(RELAY_EXHAUST, LOW); // EXHAUST ON
Serial.println("==================================");
Serial.println("PENGERINGAN SELESAI");
Serial.print("Berat Awal : ");
Serial.print(berat_awal, 2);
Serial.println(" g");
Serial.print("Berat Akhir : ");
Serial.print(berat, 2);
Serial.println(" g");
Serial.print("Target : ");
Serial.print(berat_trigger, 2);
Serial.println(" g");
Serial.print("Susut : ");
Serial.print(berat_awal - berat, 2);
Serial.println(" g");
Serial.print("Persen Susut: ");
Serial.print(((berat_awal - berat) / berat_awal) * 100, 1);
Serial.println(" %");
Serial.println("==================================");
client.publish(topicStatus, "PENGERINGAN SELESAI");
// Kirim data dengan status COMPLETED untuk trigger notifikasi
kirimStatusRelay(suhu, berat);
// Tampilkan selesai selama 5 detik
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("PENGERINGAN");
lcd.setCursor(0, 1);
lcd.print("SELESAI");
delay(5000);
// Exhaust jalan 20 detik untuk buang panas
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Cooling down...");
lcd.setCursor(0, 1);
lcd.print("20 seconds");
// Countdown 20 detik
for (int i = 20; i > 0; i--) {
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(String(i) + " seconds left");
delay(1000);
// Tetap kirim MQTT status relay saat cooling
kirimStatusRelay(suhu, berat);
client.loop();
}
// Matikan exhaust dan kembali ke ready
digitalWrite(RELAY_EXHAUST, HIGH); // EXHAUST OFF
// Reset variabel
pengeringanSelesai = false;
beratTersimpan = false;
berat_awal = 0;
berat_trigger = 0;
waktuMulaiBerat = 0;
lcd.clear();
tampilReady();
client.publish(topicStatus, "PENGERINGAN SIAP");
// Kirim status relay setelah reset
kirimStatusRelay(suhu, berat);
Serial.println("Sistem kembali ke READY");
delay(500);
return;
}
} else {
// Berat naik lagi atau di atas target, reset flag verifikasi
static bool targetReachedFlag = false;
targetReachedFlag = false;
}
// =====================================================
// RELAY CONTROL berdasarkan status dan suhu
// Relay HANYA ON setelah berat_awal & target tersimpan
// =====================================================
if (beratTersimpan) {
// Debug: Print status berat setiap 5 detik
static unsigned long lastDebug = 0;
if (millis() - lastDebug > 5000) {
Serial.println("--- STATUS PENGERINGAN ---");
Serial.print("Berat Sekarang : ");
Serial.print(berat, 2);
Serial.println(" g");
Serial.print("Target : ");
Serial.print(berat_trigger, 2);
Serial.println(" g");
Serial.print("Selisih : ");
Serial.print(berat - berat_trigger, 2);
Serial.println(" g");
Serial.print("Kondisi : ");
if (berat <= berat_trigger) {
Serial.println("TARGET TERCAPAI ✓");
} else {
Serial.println("MASIH MENGERINGKAN...");
}
Serial.println("-------------------------");
lastDebug = millis();
}
// Berat sudah tersimpan, mulai pengeringan
// FAN selalu ON saat pengeringan
digitalWrite(RELAY_FAN, LOW); // FAN ON
// Kontrol HEATER dan EXHAUST berdasarkan suhu
if (suhu >= 55.0) {
// Suhu sudah tinggi (≥55°C)
digitalWrite(RELAY_HEATER1, HIGH); // HEATER 1 OFF
digitalWrite(RELAY_HEATER2, HIGH); // HEATER 2 OFF
digitalWrite(RELAY_EXHAUST, LOW); // EXHAUST ON untuk buang panas
// Serial.println("Suhu >= 55C: HEATER OFF, EXHAUST ON");
} else {
// Suhu masih rendah (<55°C)
digitalWrite(RELAY_HEATER1, LOW); // HEATER 1 ON
digitalWrite(RELAY_HEATER2, LOW); // HEATER 2 ON
digitalWrite(RELAY_EXHAUST, HIGH); // EXHAUST OFF
// Serial.println("Suhu < 55C: HEATER ON, EXHAUST OFF");
}
} else {
// Masih scanning berat, relay tetap OFF
digitalWrite(RELAY_HEATER1, HIGH); // HEATER 1 OFF
digitalWrite(RELAY_HEATER2, HIGH); // HEATER 2 OFF
digitalWrite(RELAY_FAN, HIGH); // FAN OFF
digitalWrite(RELAY_EXHAUST, HIGH); // EXHAUST OFF
}
// =====================================================
// LCD BARIS 1
// =====================================================
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print("S:");
lcd.print(suhu, 1);
lcd.print("C");
lcd.setCursor(9, 0);
lcd.print("B:");
lcd.print(berat, 0);
lcd.print("g");
// =====================================================
// LCD BARIS 2
// =====================================================
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
if (!beratTersimpan) {
lcd.print("Scan Berat...");
}
else {
lcd.print("T:");
lcd.print(berat_trigger, 0);
lcd.print("g");
}
// =====================================================
// MQTT DATA (kirim ke Backend Railway)
// =====================================================
kirimStatusRelay(suhu, berat);
delay(1000);
}
// =====================================================
// MQTT DATA dengan Status Relay (Format JSON untuk Backend Railway)
// =====================================================
void kirimStatusRelay(float suhu, float berat) {
// Baca status relay saat ini
bool heater1_on = (digitalRead(RELAY_HEATER1) == LOW);
bool heater2_on = (digitalRead(RELAY_HEATER2) == LOW);
bool fan_on = (digitalRead(RELAY_FAN) == LOW);
bool exhaust_on = (digitalRead(RELAY_EXHAUST) == LOW);
// Tentukan status string
String statusString = "UNKNOWN";
if (pengeringanSelesai) {
statusString = "SELESAI";
} else if (modePengeringan && beratTersimpan) {
statusString = "BERJALAN";
} else if (modePengeringan && !beratTersimpan) {
statusString = "SCANNING";
} else {
statusString = "READY";
}
// Format JSON dengan status relay DAN status
String payload = "{";
payload += "\"suhu\":" + String(suhu, 1) + ",";
payload += "\"berat\":" + String(berat, 0) + ",";
payload += "\"target\":" + String(berat_trigger, 0) + ",";
payload += "\"relay1\":" + String(heater1_on ? "true" : "false") + ",";
payload += "\"relay2\":" + String(heater2_on ? "true" : "false") + ",";
payload += "\"relay3\":" + String(fan_on ? "true" : "false") + ",";
payload += "\"relay4\":" + String(exhaust_on ? "true" : "false") + ",";
payload += "\"status\":\"" + statusString + "\"";
payload += "}";
// Publish ke topic data
bool success = client.publish(topicData, payload.c_str());
if (success) {
Serial.print("MQTT Data Sent: ");
Serial.println(payload);
} else {
Serial.println("MQTT Data Failed to Send");
}
}
// =====================================================
// MQTT DATA (Backward compatibility - deprecated)
// =====================================================
void kirimDataMQTT(float suhu, float berat) {
kirimStatusRelay(suhu, berat);
}
// =====================================================
// READY LCD
// =====================================================
void tampilReady() {
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print("Ready to Dry");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("Press Button");
}
// =====================================================
// SELESAI LCD
// =====================================================
void tampilSelesai() {
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print("PENGERINGAN");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("SELESAI");
}