739 lines
26 KiB
C++
739 lines
26 KiB
C++
#include <ESP8266WiFi.h>
|
|
#include <FirebaseESP8266.h>
|
|
#include <DHT.h>
|
|
#include <Wire.h>
|
|
#include <LiquidCrystal_I2C.h>
|
|
#include <NTPClient.h>
|
|
#include <WiFiUdp.h>
|
|
|
|
// Konfigurasi WiFi
|
|
const char* ssid = "Combat2024";
|
|
const char* password = "12345678";
|
|
|
|
// Konfigurasi Firebase
|
|
#define FIREBASE_HOST "https://jago-9a9a6-default-rtdb.firebaseio.com/"
|
|
#define FIREBASE_AUTH "Q3i1jDmc3Xh9UiUTaGUmOw4tVztV5TJ3INU9RoWN"
|
|
|
|
// NTP Client untuk mendapatkan waktu
|
|
WiFiUDP ntpUDP;
|
|
NTPClient timeClient(ntpUDP, "pool.ntp.org");
|
|
|
|
FirebaseData firebaseData;
|
|
FirebaseConfig config;
|
|
FirebaseAuth auth;
|
|
|
|
#define DHTPIN D5
|
|
#define RELAY_TEMP D3 // Relay untuk suhu (kipas)
|
|
#define RELAY_HUMIDITY D6 // Relay untuk kelembapan (lampu)
|
|
#define DHTTYPE DHT11
|
|
|
|
DHT dht(DHTPIN, DHTTYPE);
|
|
LiquidCrystal_I2C lcd(0x27, 16, 2); // Pastikan alamat sesuai dengan modul yang digunakan
|
|
|
|
int ageInWeeks = 1; // Default, nanti diambil dari Firebase
|
|
|
|
// Variabel untuk mode kontrol
|
|
bool autoModeFan = true; // Default: mode otomatis untuk kipas
|
|
bool autoModeLight = true; // Default: mode otomatis untuk lampu
|
|
bool fanStatus = false; // Status kipas (ON/OFF)
|
|
bool lightStatus = false; // Status lampu (ON/OFF)
|
|
|
|
// Variabel untuk rentang suhu dan kelembapan dinamis
|
|
struct Range {
|
|
float min;
|
|
float max;
|
|
float target;
|
|
};
|
|
|
|
Range tempRanges[4] = {
|
|
{33, 35, 34}, // Minggu 1
|
|
{30, 33, 31.5}, // Minggu 2
|
|
{28, 30, 29}, // Minggu 3
|
|
{25, 28, 26.5} // Minggu 4
|
|
};
|
|
|
|
Range humidityRanges[4] = {
|
|
{60, 70, 65}, // Minggu 1
|
|
{60, 65, 62.5}, // Minggu 2
|
|
{60, 65, 62.5}, // Minggu 3
|
|
{55, 60, 57.5} // Minggu 4
|
|
};
|
|
|
|
// Variabel untuk pengaturan tampilan LCD
|
|
unsigned long lastDisplayToggle = 0;
|
|
bool showMainDisplay = true; // Untuk mengganti tampilan LCD
|
|
|
|
// Variabel untuk reconnect
|
|
unsigned long lastReconnectAttempt = 0;
|
|
const unsigned long reconnectInterval = 30000; // 30 detik
|
|
|
|
// Variabel untuk menyimpan status koneksi WiFi sebelumnya
|
|
bool previousWiFiStatus = false;
|
|
|
|
// Fungsi untuk logging ke Firebase dengan timestamp
|
|
void logToFirebase(const String& path, const String& message) {
|
|
String timestamp = getCurrentTimestamp();
|
|
String logMessage = timestamp + ": " + message;
|
|
|
|
// Kirim log ke Firebase, menggunakan setString untuk memperbarui entri
|
|
Firebase.setString(firebaseData, path, logMessage);
|
|
}
|
|
|
|
// Fungsi untuk mendapatkan waktu dari NTP
|
|
String getCurrentTimestamp() {
|
|
timeClient.update();
|
|
return String(timeClient.getEpochTime());
|
|
}
|
|
|
|
// Variabel untuk memeriksa koneksi WiFi
|
|
unsigned long lastWiFiCheck = 0;
|
|
const unsigned long wifiCheckInterval = 2000; // 2 detik
|
|
|
|
// Fungsi untuk memeriksa dan memulihkan koneksi WiFi
|
|
void checkWiFiConnection() {
|
|
if (WiFi.status() != WL_CONNECTED) {
|
|
unsigned long currentMillis = millis();
|
|
|
|
// Coba reconnect setiap interval tertentu
|
|
if (currentMillis - lastReconnectAttempt >= reconnectInterval) {
|
|
logToFirebase("/logs/wifi", "WiFi Disconnected. Attempting to Reconnect");
|
|
|
|
// Matikan WiFi dan nyalakan kembali
|
|
WiFi.disconnect();
|
|
delay(1000);
|
|
|
|
WiFi.begin(ssid, password);
|
|
|
|
lastReconnectAttempt = currentMillis;
|
|
}
|
|
|
|
// Tambahkan log ketika WiFi terputus
|
|
if (previousWiFiStatus) {
|
|
logToFirebase("/logs/wifi", "WiFi Connection Failed");
|
|
previousWiFiStatus = false;
|
|
}
|
|
} else {
|
|
// Jika WiFi terhubung, periksa apakah statusnya berubah
|
|
if (!previousWiFiStatus) {
|
|
logToFirebase("/logs/wifi", "WiFi Connected Successfully");
|
|
Serial.println("\nWiFi connected");
|
|
previousWiFiStatus = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
|
|
// Inisialisasi pin relay
|
|
pinMode(RELAY_TEMP, OUTPUT);
|
|
pinMode(RELAY_HUMIDITY, OUTPUT);
|
|
digitalWrite(RELAY_TEMP, HIGH); // Pastikan relay mulai dalam keadaan OFF
|
|
digitalWrite(RELAY_HUMIDITY, HIGH);
|
|
|
|
// Inisialisasi LCD dengan deteksi kesalahan
|
|
logToFirebase("/logs/lcd", "LCD Initialization Started");
|
|
|
|
// Coba inisialisasi LCD dengan timeout
|
|
bool lcdInitSuccess = false;
|
|
Wire.begin();
|
|
|
|
// Periksa apakah LCD merespons
|
|
Wire.beginTransmission(0x27); // Alamat I2C LCD
|
|
byte error = Wire.endTransmission();
|
|
|
|
if (error == 0) {
|
|
// LCD ditemukan, coba inisialisasi
|
|
lcd.init();
|
|
lcd.backlight();
|
|
lcd.clear();
|
|
lcd.begin(16,2);
|
|
lcd.print("Initializing...");
|
|
lcdInitSuccess = true;
|
|
}
|
|
|
|
if (lcdInitSuccess) {
|
|
logToFirebase("/logs/lcd", "LCD Ready and Initialized");
|
|
Serial.println("LCD initialized successfully");
|
|
} else {
|
|
logToFirebase("/logs/lcd", "LCD Initialization Failed - Check Connections");
|
|
Serial.println("LCD initialization failed! Check wiring (GND, VCC, SDA, SCL)");
|
|
}
|
|
|
|
// Inisialisasi WiFi
|
|
WiFi.mode(WIFI_STA);
|
|
WiFi.begin(ssid, password);
|
|
|
|
// Tunggu koneksi WiFi
|
|
int attempts = 0;
|
|
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
|
|
delay(500);
|
|
Serial.print(".");
|
|
attempts++;
|
|
}
|
|
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
logToFirebase("/logs/wifi", "WiFi Connected Successfully");
|
|
Serial.println("\nWiFi connected");
|
|
|
|
// Inisialisasi NTP Client
|
|
timeClient.begin();
|
|
timeClient.setTimeOffset(25200); // Offset untuk WIB (GMT+7)
|
|
} else {
|
|
logToFirebase("/logs/wifi", "WiFi Connection Failed");
|
|
Serial.println("\nWiFi connection failed");
|
|
}
|
|
|
|
// Inisialisasi DHT
|
|
dht.begin();
|
|
logToFirebase("/logs/sensor", "DHT Sensor Initialized");
|
|
|
|
// Konfigurasi Firebase
|
|
config.host = FIREBASE_HOST;
|
|
config.signer.tokens.legacy_token = FIREBASE_AUTH;
|
|
|
|
Firebase.begin(&config, &auth);
|
|
Firebase.reconnectWiFi(true);
|
|
|
|
// Inisialisasi variabel kontrol di Firebase
|
|
Firebase.setBool(firebaseData, "/control/fan/auto", autoModeFan);
|
|
Firebase.setBool(firebaseData, "/control/light/auto", autoModeLight);
|
|
Firebase.setBool(firebaseData, "/control/fan/status", fanStatus);
|
|
Firebase.setBool(firebaseData, "/control/light/status", lightStatus);
|
|
|
|
// Inisialisasi rentang di Firebase jika belum ada
|
|
for (int i = 0; i < 4; i++) {
|
|
String tempPath = "/ranges/week" + String(i+1) + "/temperature";
|
|
String humidityPath = "/ranges/week" + String(i+1) + "/humidity";
|
|
|
|
// Inisialisasi rentang suhu
|
|
Firebase.setFloat(firebaseData, tempPath + "/min", tempRanges[i].min);
|
|
Firebase.setFloat(firebaseData, tempPath + "/max", tempRanges[i].max);
|
|
Firebase.setFloat(firebaseData, tempPath + "/target", tempRanges[i].target);
|
|
|
|
// Inisialisasi rentang kelembapan
|
|
Firebase.setFloat(firebaseData, humidityPath + "/min", humidityRanges[i].min);
|
|
Firebase.setFloat(firebaseData, humidityPath + "/max", humidityRanges[i].max);
|
|
Firebase.setFloat(firebaseData, humidityPath + "/target", humidityRanges[i].target);
|
|
}
|
|
}
|
|
|
|
void loop() {
|
|
unsigned long currentMillis = millis();
|
|
|
|
// Periksa koneksi WiFi setiap 2 detik
|
|
if (currentMillis - lastWiFiCheck >= wifiCheckInterval) {
|
|
checkWiFiConnection();
|
|
lastWiFiCheck = currentMillis;
|
|
}
|
|
|
|
// Periksa koneksi LCD secara berkala
|
|
checkLCDConnection();
|
|
|
|
// Update NTP Client
|
|
timeClient.update();
|
|
|
|
// Baca sensor suhu dan kelembapan
|
|
float temperature = dht.readTemperature();
|
|
float humidity = dht.readHumidity();
|
|
|
|
// Periksa pembacaan sensor
|
|
if (isnan(temperature) || isnan(humidity)) {
|
|
logToFirebase("/logs/sensor", "Failed to Read from DHT Sensor");
|
|
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("Sensor Error!");
|
|
lcd.setCursor(0, 1);
|
|
lcd.print("Check DHT sensor");
|
|
delay(2000);
|
|
return;
|
|
} else {
|
|
// Log pembacaan sensor berhasil (setiap 2 detik)
|
|
static unsigned long lastSensorLog = 0;
|
|
if (millis() - lastSensorLog > 2000) { // Log setiap 2 detik
|
|
logToFirebase("/logs/sensor", "DHT Sensor Reading Successful");
|
|
lastSensorLog = millis();
|
|
}
|
|
}
|
|
|
|
// Ambil umur ayam dari Firebase
|
|
if (Firebase.getInt(firebaseData, "/chicken_age")) {
|
|
ageInWeeks = firebaseData.intData();
|
|
Serial.print("Age: ");
|
|
Serial.println(ageInWeeks);
|
|
} else {
|
|
Serial.print("Failed to get age from Firebase: ");
|
|
Serial.println(firebaseData.errorReason());
|
|
|
|
// Inisialisasi jika data belum ada
|
|
if (firebaseData.errorReason() == "path not exist") {
|
|
Firebase.setInt(firebaseData, "/chicken_age", 1);
|
|
logToFirebase("/logs/system", "Initialized /chicken_age with default value 1");
|
|
}
|
|
}
|
|
|
|
// Periksa mode kontrol (manual atau otomatis)
|
|
checkControlMode();
|
|
|
|
// Ganti tampilan LCD setiap 5 detik
|
|
if (currentMillis - lastDisplayToggle >= 5000) {
|
|
showMainDisplay = !showMainDisplay;
|
|
lastDisplayToggle = currentMillis;
|
|
}
|
|
|
|
// Update tampilan LCD berdasarkan mode
|
|
if (showMainDisplay) {
|
|
updateMainDisplay(temperature, humidity);
|
|
} else {
|
|
updateModeDisplay();
|
|
}
|
|
|
|
// Kontrol suhu dan kelembapan berdasarkan mode
|
|
if (autoModeFan) {
|
|
// Mode otomatis untuk kipas (menggunakan fuzzy logic)
|
|
controlTemperature(temperature);
|
|
} else {
|
|
// Mode manual untuk kipas
|
|
if (fanStatus) {
|
|
digitalWrite(RELAY_TEMP, LOW); // Kipas ON
|
|
} else {
|
|
digitalWrite(RELAY_TEMP, HIGH); // Kipas OFF
|
|
}
|
|
}
|
|
|
|
if (autoModeLight) {
|
|
// Mode otomatis untuk lampu (menggunakan fuzzy logic)
|
|
controlHumidity(humidity, temperature);
|
|
} else {
|
|
// Mode manual untuk lampu
|
|
if (lightStatus) {
|
|
digitalWrite(RELAY_HUMIDITY, LOW); // Lampu ON
|
|
} else {
|
|
digitalWrite(RELAY_HUMIDITY, HIGH); // Lampu OFF
|
|
}
|
|
}
|
|
|
|
// Kirim data ke Firebase
|
|
Firebase.setFloat(firebaseData, "/sensor/temperature", temperature);
|
|
Firebase.setFloat(firebaseData, "/sensor/Humidity", humidity);
|
|
Firebase.setBool(firebaseData, "/relay/Kipas", digitalRead(RELAY_TEMP) == LOW);
|
|
Firebase.setBool(firebaseData, "/relay/Lampu", digitalRead(RELAY_HUMIDITY) == LOW);
|
|
|
|
// Perbarui rentang dari Firebase
|
|
updateRangesFromFirebase();
|
|
|
|
delay(1000); // Tunggu 1 detik antara siklus
|
|
}
|
|
|
|
// Fungsi untuk tampilan utama (Temp, Hum, Age)
|
|
void updateMainDisplay(float temperature, float humidity) {
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("Temp:");
|
|
lcd.print(temperature, 1);
|
|
lcd.print((char)223); // Simbol derajat
|
|
lcd.print("C");
|
|
|
|
lcd.setCursor(0, 1);
|
|
lcd.print("Hum:");
|
|
lcd.print(humidity, 1);
|
|
lcd.print("% Age:");
|
|
lcd.print(ageInWeeks);
|
|
lcd.print("w");
|
|
}
|
|
|
|
// Fungsi untuk tampilan mode (Auto/Manual)
|
|
void updateModeDisplay() {
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("Fan: ");
|
|
lcd.print(autoModeFan ? "AUTO" : "MANUAL");
|
|
lcd.print(" ");
|
|
lcd.print(digitalRead(RELAY_TEMP) == LOW ? "ON" : "OFF");
|
|
|
|
lcd.setCursor(0, 1);
|
|
lcd.print("Light: ");
|
|
lcd.print(autoModeLight ? "AUTO" : "MANUAL");
|
|
lcd.print(" ");
|
|
lcd.print(digitalRead(RELAY_HUMIDITY) == LOW ? "ON" : "OFF");
|
|
}
|
|
|
|
// Definisi fungsi fuzzy logic untuk kontrol suhu
|
|
void controlTemperature(float temp) {
|
|
float minTemp = tempRanges[ageInWeeks-1].min;
|
|
float maxTemp = tempRanges[ageInWeeks-1].max;
|
|
float targetTemp = tempRanges[ageInWeeks-1].target;
|
|
|
|
// Menghitung derajat keanggotaan untuk berbagai kondisi suhu
|
|
float veryLow = 0; // Suhu sangat rendah (kondisi kritis)
|
|
float tooLow = 0; // Suhu terlalu rendah
|
|
float optimal = 0; // Suhu optimal
|
|
float tooHigh = 0; // Suhu terlalu tinggi
|
|
float veryHigh = 0; // Suhu sangat tinggi (kondisi kritis)
|
|
|
|
// Keanggotaan "sangat rendah" - kondisi kritis
|
|
if (temp < minTemp - 2) {
|
|
veryLow = 1.0; // Sangat rendah, kondisi kritis
|
|
} else if (temp >= minTemp - 2 && temp < minTemp - 1) {
|
|
veryLow = (minTemp - 1 - temp); // Agak sangat rendah
|
|
veryLow = constrain(veryLow, 0, 1);
|
|
}
|
|
|
|
// Keanggotaan "terlalu rendah"
|
|
if (temp >= minTemp - 1.5 && temp < minTemp) {
|
|
tooLow = (minTemp - temp) / 1.5; // Agak rendah
|
|
tooLow = constrain(tooLow, 0, 1);
|
|
}
|
|
|
|
// Keanggotaan "optimal"
|
|
if (temp >= minTemp && temp <= maxTemp) {
|
|
// Semakin dekat ke targetTemp, semakin optimal
|
|
optimal = 1.0 - abs(temp - targetTemp) / ((maxTemp - minTemp) / 2.0);
|
|
optimal = constrain(optimal, 0, 1);
|
|
}
|
|
|
|
// Keanggotaan "terlalu tinggi"
|
|
if (temp > maxTemp && temp <= maxTemp + 1.5) {
|
|
tooHigh = (temp - maxTemp) / 1.5; // Agak tinggi
|
|
tooHigh = constrain(tooHigh, 0, 1);
|
|
}
|
|
|
|
// Keanggotaan "sangat tinggi" - kondisi kritis
|
|
if (temp > maxTemp + 2) {
|
|
veryHigh = 1.0; // Sangat tinggi, kondisi kritis
|
|
} else if (temp > maxTemp + 1 && temp <= maxTemp + 2) {
|
|
veryHigh = (temp - (maxTemp + 1)) / 1.0; // Agak sangat tinggi
|
|
veryHigh = constrain(veryHigh, 0, 1);
|
|
}
|
|
|
|
// Simpan status suhu untuk digunakan oleh fungsi controlHumidity
|
|
bool isTempVeryCold = (veryLow > 0.3);
|
|
bool isTempTooCold = (tooLow > 0.3);
|
|
bool isTempVeryHot = (veryHigh > 0.3);
|
|
|
|
// Kirim nilai fuzzy ke Firebase untuk monitoring
|
|
Firebase.setFloat(firebaseData, "/fuzzy/temperature/veryLow", veryLow);
|
|
Firebase.setFloat(firebaseData, "/fuzzy/temperature/tooLow", tooLow);
|
|
Firebase.setFloat(firebaseData, "/fuzzy/temperature/optimal", optimal);
|
|
Firebase.setFloat(firebaseData, "/fuzzy/temperature/tooHigh", tooHigh);
|
|
Firebase.setFloat(firebaseData, "/fuzzy/temperature/veryHigh", veryHigh);
|
|
|
|
// Keputusan berdasarkan derajat keanggotaan dan prioritas
|
|
// Prioritas: Sangat tinggi > Sangat rendah > Terlalu tinggi > Optimal > Terlalu rendah
|
|
if (veryHigh > 0.3) { // Jika suhu sangat tinggi (kondisi kritis)
|
|
digitalWrite(RELAY_TEMP, LOW); // Kipas ON pada kecepatan maksimal
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/temperature", "Sangat Tinggi - Kipas ON (Kritis)");
|
|
Firebase.setBool(firebaseData, "/emergency/high_temperature", true);
|
|
|
|
} else if (veryLow > 0.3) { // Jika suhu sangat rendah (kondisi kritis)
|
|
digitalWrite(RELAY_TEMP, HIGH); // Kipas OFF
|
|
// Nyalakan lampu untuk membantu meningkatkan suhu
|
|
// Ini akan di-override oleh controlHumidity jika diperlukan
|
|
digitalWrite(RELAY_HUMIDITY, LOW); // Lampu ON untuk membantu meningkatkan suhu
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/temperature", "Sangat Rendah - Kipas OFF, Lampu ON");
|
|
Firebase.setBool(firebaseData, "/emergency/low_temperature", true);
|
|
Firebase.setBool(firebaseData, "/emergency/high_temperature", false);
|
|
|
|
} else if (tooHigh > 0.3) { // Jika suhu terlalu tinggi
|
|
digitalWrite(RELAY_TEMP, LOW); // Kipas ON
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/temperature", "Tinggi - Kipas ON");
|
|
Firebase.setBool(firebaseData, "/emergency/low_temperature", false);
|
|
Firebase.setBool(firebaseData, "/emergency/high_temperature", false);
|
|
|
|
} else if (optimal > 0.7) { // Jika suhu sangat optimal
|
|
digitalWrite(RELAY_TEMP, HIGH); // Kipas OFF
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/temperature", "Optimal - Kipas OFF");
|
|
Firebase.setBool(firebaseData, "/emergency/low_temperature", false);
|
|
Firebase.setBool(firebaseData, "/emergency/high_temperature", false);
|
|
|
|
} else if (tooLow > 0.3) { // Jika suhu terlalu rendah
|
|
digitalWrite(RELAY_TEMP, HIGH); // Kipas OFF
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/temperature", "Rendah - Kipas OFF");
|
|
Firebase.setBool(firebaseData, "/emergency/low_temperature", true);
|
|
Firebase.setBool(firebaseData, "/emergency/high_temperature", false);
|
|
|
|
} else {
|
|
// Kasus lain, gunakan pendekatan proporsional
|
|
// Jika suhu mendekati batas atas rentang, nyalakan kipas
|
|
float distanceToMax = maxTemp - temp;
|
|
if (distanceToMax < 1.0) {
|
|
digitalWrite(RELAY_TEMP, LOW); // Kipas ON
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/temperature", "Mendekati Tinggi - Kipas ON");
|
|
} else {
|
|
digitalWrite(RELAY_TEMP, HIGH); // Kipas OFF
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/temperature", "Normal - Kipas OFF");
|
|
}
|
|
Firebase.setBool(firebaseData, "/emergency/low_temperature", false);
|
|
Firebase.setBool(firebaseData, "/emergency/high_temperature", false);
|
|
}
|
|
}
|
|
|
|
// Definisi fungsi fuzzy logic untuk kontrol kelembapan
|
|
void controlHumidity(float humidity, float temperature) {
|
|
float minHumidity = humidityRanges[ageInWeeks-1].min;
|
|
float maxHumidity = humidityRanges[ageInWeeks-1].max;
|
|
float targetHumidity = humidityRanges[ageInWeeks-1].target;
|
|
float minTemp = tempRanges[ageInWeeks-1].min;
|
|
float maxTemp = tempRanges[ageInWeeks-1].max;
|
|
|
|
// Periksa apakah suhu terlalu rendah (kondisi darurat)
|
|
bool isTemperatureEmergency = false;
|
|
if (temperature < minTemp - 1.5) {
|
|
isTemperatureEmergency = true;
|
|
}
|
|
|
|
// Menghitung derajat keanggotaan untuk berbagai kondisi kelembapan
|
|
float veryLow = 0; // Kelembapan sangat rendah (kondisi kritis)
|
|
float tooLow = 0; // Kelembapan terlalu rendah
|
|
float optimal = 0; // Kelembapan optimal
|
|
float tooHigh = 0; // Kelembapan terlalu tinggi
|
|
float veryHigh = 0; // Kelembapan sangat tinggi (kondisi kritis)
|
|
|
|
// Keanggotaan "sangat rendah" - kondisi kritis
|
|
if (humidity < minHumidity - 5) {
|
|
veryLow = 1.0; // Sangat rendah, kondisi kritis
|
|
} else if (humidity >= minHumidity - 5 && humidity < minHumidity - 3) {
|
|
veryLow = (minHumidity - 3 - humidity) / 2.0; // Agak sangat rendah
|
|
veryLow = constrain(veryLow, 0, 1);
|
|
}
|
|
|
|
// Keanggotaan "terlalu rendah"
|
|
if (humidity >= minHumidity - 3 && humidity < minHumidity) {
|
|
tooLow = (minHumidity - humidity) / 3.0; // Agak rendah
|
|
tooLow = constrain(tooLow, 0, 1);
|
|
}
|
|
|
|
// Keanggotaan "optimal"
|
|
if (humidity >= minHumidity && humidity <= maxHumidity) {
|
|
// Semakin dekat ke targetHumidity, semakin optimal
|
|
optimal = 1.0 - abs(humidity - targetHumidity) / ((maxHumidity - minHumidity) / 2.0);
|
|
optimal = constrain(optimal, 0, 1);
|
|
}
|
|
|
|
// Keanggotaan "terlalu tinggi"
|
|
if (humidity > maxHumidity && humidity <= maxHumidity + 3) {
|
|
tooHigh = (humidity - maxHumidity) / 3.0; // Agak tinggi
|
|
tooHigh = constrain(tooHigh, 0, 1);
|
|
}
|
|
|
|
// Keanggotaan "sangat tinggi" - kondisi kritis
|
|
if (humidity > maxHumidity + 5) {
|
|
veryHigh = 1.0; // Sangat tinggi, kondisi kritis
|
|
} else if (humidity > maxHumidity + 3 && humidity <= maxHumidity + 5) {
|
|
veryHigh = (humidity - (maxHumidity + 3)) / 2.0; // Agak sangat tinggi
|
|
veryHigh = constrain(veryHigh, 0, 1);
|
|
}
|
|
|
|
// Kirim nilai fuzzy ke Firebase untuk monitoring
|
|
Firebase.setFloat(firebaseData, "/fuzzy/humidity/veryLow", veryLow);
|
|
Firebase.setFloat(firebaseData, "/fuzzy/humidity/tooLow", tooLow);
|
|
Firebase.setFloat(firebaseData, "/fuzzy/humidity/optimal", optimal);
|
|
Firebase.setFloat(firebaseData, "/fuzzy/humidity/tooHigh", tooHigh);
|
|
Firebase.setFloat(firebaseData, "/fuzzy/humidity/veryHigh", veryHigh);
|
|
|
|
// Keputusan berdasarkan derajat keanggotaan dan prioritas
|
|
// Jika suhu dalam kondisi darurat, prioritaskan pemanasan
|
|
if (isTemperatureEmergency) {
|
|
digitalWrite(RELAY_HUMIDITY, LOW); // Lampu ON untuk membantu meningkatkan suhu
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/humidity", "Suhu Darurat - Lampu ON untuk Pemanasan");
|
|
Firebase.setBool(firebaseData, "/emergency/low_humidity", false); // Reset emergency flag
|
|
Firebase.setBool(firebaseData, "/emergency/high_humidity", false); // Reset emergency flag
|
|
return; // Keluar dari fungsi, prioritaskan penanganan suhu
|
|
}
|
|
|
|
// Prioritas: Kelembapan sangat tinggi > Kelembapan sangat rendah > Terlalu tinggi > Optimal > Terlalu rendah
|
|
if (veryHigh > 0.3) { // Jika kelembapan sangat tinggi (kondisi kritis)
|
|
digitalWrite(RELAY_HUMIDITY, LOW); // Lampu ON untuk mengurangi kelembapan
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/humidity", "Sangat Tinggi - Lampu ON (Kritis)");
|
|
Firebase.setBool(firebaseData, "/emergency/high_humidity", true);
|
|
Firebase.setBool(firebaseData, "/emergency/low_humidity", false);
|
|
|
|
} else if (veryLow > 0.3) { // Jika kelembapan sangat rendah (kondisi kritis)
|
|
digitalWrite(RELAY_HUMIDITY, HIGH); // Lampu OFF untuk mempertahankan kelembapan
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/humidity", "Sangat Rendah - Lampu OFF");
|
|
Firebase.setBool(firebaseData, "/emergency/low_humidity", true);
|
|
Firebase.setBool(firebaseData, "/emergency/high_humidity", false);
|
|
|
|
} else if (tooHigh > 0.4) { // Jika kelembapan terlalu tinggi
|
|
digitalWrite(RELAY_HUMIDITY, LOW); // Lampu ON untuk mengurangi kelembapan
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/humidity", "Tinggi - Lampu ON");
|
|
Firebase.setBool(firebaseData, "/emergency/low_humidity", false);
|
|
Firebase.setBool(firebaseData, "/emergency/high_humidity", false);
|
|
|
|
} else if (optimal > 0.6) { // Jika kelembapan optimal
|
|
// Periksa juga suhu - jika suhu rendah, nyalakan lampu meskipun kelembapan optimal
|
|
if (temperature < minTemp) {
|
|
digitalWrite(RELAY_HUMIDITY, LOW); // Lampu ON untuk membantu meningkatkan suhu
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/humidity", "Optimal, Suhu Rendah - Lampu ON");
|
|
}
|
|
// Jika kelembapan mendekati batas atas rentang optimal, nyalakan lampu
|
|
else if (humidity > targetHumidity + ((maxHumidity - minHumidity) / 4)) {
|
|
digitalWrite(RELAY_HUMIDITY, LOW); // Lampu ON
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/humidity", "Optimal Tinggi - Lampu ON");
|
|
} else {
|
|
digitalWrite(RELAY_HUMIDITY, HIGH); // Lampu OFF
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/humidity", "Optimal - Lampu OFF");
|
|
}
|
|
Firebase.setBool(firebaseData, "/emergency/low_humidity", false);
|
|
Firebase.setBool(firebaseData, "/emergency/high_humidity", false);
|
|
|
|
} else if (tooLow > 0.3) { // Jika kelembapan terlalu rendah
|
|
// Periksa juga suhu - jika suhu sangat rendah, prioritaskan pemanasan
|
|
if (temperature < minTemp - 1) {
|
|
digitalWrite(RELAY_HUMIDITY, LOW); // Lampu ON untuk membantu meningkatkan suhu
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/humidity", "Rendah, Suhu Sangat Rendah - Lampu ON");
|
|
} else {
|
|
digitalWrite(RELAY_HUMIDITY, HIGH); // Lampu OFF untuk mempertahankan kelembapan
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/humidity", "Rendah - Lampu OFF");
|
|
}
|
|
Firebase.setBool(firebaseData, "/emergency/low_humidity", true);
|
|
Firebase.setBool(firebaseData, "/emergency/high_humidity", false);
|
|
|
|
} else {
|
|
// Kasus lain, gunakan pendekatan proporsional
|
|
// Periksa juga suhu - jika suhu rendah, nyalakan lampu
|
|
if (temperature < minTemp) {
|
|
digitalWrite(RELAY_HUMIDITY, LOW); // Lampu ON untuk membantu meningkatkan suhu
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/humidity", "Normal, Suhu Rendah - Lampu ON");
|
|
}
|
|
// Jika kelembapan mendekati batas atas, nyalakan lampu
|
|
else if (humidity > maxHumidity - 1.5) {
|
|
digitalWrite(RELAY_HUMIDITY, LOW); // Lampu ON
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/humidity", "Mendekati Tinggi - Lampu ON");
|
|
} else {
|
|
digitalWrite(RELAY_HUMIDITY, HIGH); // Lampu OFF
|
|
// Log status ke Firebase
|
|
Firebase.setString(firebaseData, "/status/humidity", "Normal - Lampu OFF");
|
|
}
|
|
Firebase.setBool(firebaseData, "/emergency/low_humidity", false);
|
|
Firebase.setBool(firebaseData, "/emergency/high_humidity", false);
|
|
}
|
|
}
|
|
|
|
// Fungsi untuk memeriksa mode kontrol dari Firebase
|
|
void checkControlMode() {
|
|
// Periksa mode kipas (otomatis/manual)
|
|
if (Firebase.getBool(firebaseData, "/control/fan/auto")) {
|
|
bool newAutoModeFan = firebaseData.boolData();
|
|
if (newAutoModeFan != autoModeFan) {
|
|
autoModeFan = newAutoModeFan;
|
|
}
|
|
}
|
|
|
|
// Periksa mode lampu (otomatis/manual)
|
|
if (Firebase.getBool(firebaseData, "/control/light/auto")) {
|
|
bool newAutoModeLight = firebaseData.boolData();
|
|
if (newAutoModeLight != autoModeLight) {
|
|
autoModeLight = newAutoModeLight;
|
|
}
|
|
}
|
|
|
|
// Jika dalam mode manual, ambil status dari Firebase
|
|
if (!autoModeFan) {
|
|
if (Firebase.getBool(firebaseData, "/control/fan/status")) {
|
|
bool newFanStatus = firebaseData.boolData();
|
|
if (newFanStatus != fanStatus) {
|
|
fanStatus = newFanStatus;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!autoModeLight) {
|
|
if (Firebase.getBool(firebaseData, "/control/light/status")) {
|
|
bool newLightStatus = firebaseData.boolData();
|
|
if (newLightStatus != lightStatus) {
|
|
lightStatus = newLightStatus;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tambahkan fungsi untuk memeriksa LCD secara berkala
|
|
void checkLCDConnection() {
|
|
static unsigned long lastLCDCheck = 0;
|
|
static bool lastLCDStatus = true;
|
|
|
|
// Periksa LCD setiap 30 detik
|
|
if (millis() - lastLCDCheck > 30000) {
|
|
Wire.beginTransmission(0x27); // Alamat I2C LCD
|
|
byte error = Wire.endTransmission();
|
|
|
|
bool currentLCDStatus = (error == 0);
|
|
|
|
// Jika status berubah, kirim log
|
|
if (currentLCDStatus != lastLCDStatus) {
|
|
if (currentLCDStatus) {
|
|
// LCD terdeteksi kembali, jalankan proses inisialisasi lengkap
|
|
logToFirebase("/logs/lcd", "LCD Connection Restored");
|
|
logToFirebase("/logs/lcd", "LCD Initialization Started");
|
|
|
|
// Coba inisialisasi LCD
|
|
lcd.init(); // Panggil init() tanpa memeriksa nilai kembali
|
|
lcd.backlight();
|
|
lcd.clear();
|
|
lcd.begin(16,2);
|
|
lcd.print("Reconnected...");
|
|
|
|
// Anggap inisialisasi berhasil jika kita sampai di sini
|
|
logToFirebase("/logs/lcd", "LCD Ready and Initialized");
|
|
Serial.println("LCD reinitialized successfully");
|
|
} else {
|
|
logToFirebase("/logs/lcd", "LCD Connection Lost - Check Wiring");
|
|
}
|
|
lastLCDStatus = currentLCDStatus;
|
|
}
|
|
|
|
lastLCDCheck = millis();
|
|
}
|
|
}
|
|
|
|
// Fungsi untuk memperbarui rentang dari Firebase
|
|
void updateRangesFromFirebase() {
|
|
static unsigned long lastUpdate = 0;
|
|
if (millis() - lastUpdate > 5000) { // Update setiap 5 detik
|
|
String path = "/ranges/week" + String(ageInWeeks);
|
|
|
|
// Ambil rentang suhu
|
|
if (Firebase.getFloat(firebaseData, path + "/temperature/min")) {
|
|
tempRanges[ageInWeeks-1].min = firebaseData.floatData();
|
|
}
|
|
if (Firebase.getFloat(firebaseData, path + "/temperature/max")) {
|
|
tempRanges[ageInWeeks-1].max = firebaseData.floatData();
|
|
}
|
|
if (Firebase.getFloat(firebaseData, path + "/temperature/target")) {
|
|
tempRanges[ageInWeeks-1].target = firebaseData.floatData();
|
|
}
|
|
|
|
// Ambil rentang kelembapan
|
|
if (Firebase.getFloat(firebaseData, path + "/humidity/min")) {
|
|
humidityRanges[ageInWeeks-1].min = firebaseData.floatData();
|
|
}
|
|
if (Firebase.getFloat(firebaseData, path + "/humidity/max")) {
|
|
humidityRanges[ageInWeeks-1].max = firebaseData.floatData();
|
|
}
|
|
if (Firebase.getFloat(firebaseData, path + "/humidity/target")) {
|
|
humidityRanges[ageInWeeks-1].target = firebaseData.floatData();
|
|
}
|
|
|
|
lastUpdate = millis();
|
|
}
|
|
} |