#include <Wire.h>
#include <Adafruit_PCD8544.h>
#include <Adafruit_SHT31.h>
#include <RTClib.h>
#include <EEPROM.h>
#include <avr/wdt.h>
// Pin tanımlamaları
#define RELAY1 5 // Isıtıcı
#define RELAY2 6 // Nemlendirici
#define RELAY3 7 // Fan
#define RELAY4 8 // Motor
#define ROW1 22 // Tuş takımı satır 1
#define ROW2 24 // Tuş takımı satır 2
#define ROW3 26 // Tuş takımı satır 3
#define ROW4 28 // Tuş takımı satır 4
#define COL1 30 // Tuş takımı sütun 1
#define COL2 32 // Tuş takımı sütun 2
#define COL3 34 // Tuş takımı sütun 3
#define COL4 36 // Tuş takımı sütun 4
#define BACKLIGHT_PIN A5 // LCD Arka ışık
// SHT31 Sensör adresleri
#define SHT31_1_ADDR 0x44 // İlk sensör I2C adresi
#define SHT31_2_ADDR 0x45 // İkinci sensör I2C adresi
// EEPROM adres haritası - Güncellendi: Her sensör için ayrı offset adresleri
#define EEPROM_TYPE 0 // Kuluçka tipi (1 byte)
#define EEPROM_START_TIME 1 // Başlangıç zamanı (4 byte)
#define EEPROM_TEMP 5 // Hedef sıcaklık (4 byte)
#define EEPROM_HUM 9 // Hedef nem (4 byte)
#define EEPROM_ERROR_FLAG 13 // Hata durumu (1 byte)
#define EEPROM_TEMP_OFFSET1 14 // Sensör 1 Sıcaklık offset (4 byte)
#define EEPROM_HUM_OFFSET1 18 // Sensör 1 Nem offset (4 byte)
#define EEPROM_TEMP_OFFSET2 22 // Sensör 2 Sıcaklık offset (4 byte)
#define EEPROM_HUM_OFFSET2 26 // Sensör 2 Nem offset (4 byte)
#define EEPROM_MOTOR_DURATION 30 // Motor çalışma süresi (4 byte)
#define EEPROM_MOTOR_INTERVAL 34 // Motor çalışma aralığı (4 byte)
#define EEPROM_CHECKSUM 38 // Veri doğrulama (1 byte)
#define EEPROM_LAST_POWER_TIME 42 // Son güç kesintisi zamanı (4 byte)
// Zaman yedekleme adresleri - YENİ
#define EEPROM_START_TIME_BACKUP1 100 // Başlangıç zamanı yedek 1 (4 byte)
#define EEPROM_START_TIME_BACKUP2 104 // Başlangıç zamanı yedek 2 (4 byte)
// Menü durumları
#define MENU_MAIN 0 // Ana menü
#define MENU_SETTINGS 1 // Ayarlar menüsü
#define MENU_MOTOR 2 // Motor ayarları menüsü
#define MENU_SENSORS 3 // Sensör bilgileri menüsü
#define MENU_CALIBRATION 4 // Kalibrasyon menüsü
// Kontrol parametreleri
#define TEMP_HYSTERESIS 0.3 // Sıcaklık kontrolü için histerezis (0.3°C)
#define HUM_HYSTERESIS 2.0 // Nem kontrolü için histerezis (%2)
#define DEBOUNCE_DELAY 250 // Tuş kontrolü için debounce süresi (250ms)
// Zaman sabitleri
const unsigned long MOTOR_INTERVAL_DEFAULT = 7200000; // Motor dönüş aralığı (2 saat)
const unsigned long MOTOR_DURATION_DEFAULT = 3000; // Motor çalışma süresi (3 saniye)
const unsigned long BACKLIGHT_TIMEOUT = 10000; // Arka ışık kapanma süresi (10 saniye)
const unsigned long MESSAGE_DURATION = 2000; // Mesaj gösterim süresi (2 saniye)
const unsigned long EEPROM_WRITE_INTERVAL = 300000; // EEPROM yazma aralığı (5 dakika)
// Hata yönetimi için sabitler
#define MAX_ERRORS 3 // Maksimum ardışık hata sayısı
#define SENSOR_TIMEOUT 5000 // Sensör okuma zaman aşımı (ms)
#define DEBUG true // Debug modu açık/kapalı
// Veri yapıları
struct SensorData {
float temp; // Sıcaklık değeri
float hum; // Nem değeri
bool isValid; // Sensör verisi geçerli mi?
};
struct MotorSettings {
unsigned long duration; // Motor çalışma süresi
unsigned long interval; // Motor çalışma aralığı
};
struct IncubatorSettings {
int type; // Kuluçka tipi (0:Tavuk, 1:Kaz, 2:Bıldırcın, 3:Manuel, 4:Sıfırlama)
float targetTemp; // Hedef sıcaklık
float targetHum; // Hedef nem
int totalDays; // Toplam kuluçka süresi
uint32_t startTime; // Başlangıç zamanı (time_t yerine uint32_t kullanıyoruz)
bool motorActive; // Motor aktif mi?
bool heaterState; // Isıtıcı durumu
bool humidifierState; // Nemlendirici durumu
// Her sensör için ayrı offset değerleri
float tempOffset1; // Sıcaklık kalibrasyon offseti sensör 1
float tempOffset2; // Sıcaklık kalibrasyon offseti sensör 2
float humOffset1; // Nem kalibrasyon offseti sensör 1
float humOffset2; // Nem kalibrasyon offseti sensör 2
};
struct ErrorManager {
int tempErrors; // Sıcaklık sensörü hata sayacı
int humErrors; // Nem sensörü hata sayacı
int rtcErrors; // RTC hata sayacı
unsigned long lastSensorRead; // Son sensör okuma zamanı
unsigned long lastEEPROMWrite; // Son EEPROM yazma zamanı
bool safeMode; // Güvenli mod aktif mi?
ErrorManager() {
tempErrors = 0;
humErrors = 0;
rtcErrors = 0;
lastSensorRead = 0;
lastEEPROMWrite = 0;
safeMode = false;
}
void resetErrors() {
tempErrors = 0;
humErrors = 0;
rtcErrors = 0;
}
bool checkSensor(float value, float min, float max) {
if (isnan(value) || value < min || value > max) {
return false;
}
return true;
}
};
//-------------- GLOBAL DEĞİŞKENLER --------------//
// Donanım nesneleri
Adafruit_PCD8544 lcd(A0, A1, A2, A3, A4); // LCD ekran
Adafruit_SHT31 sht31_1 = Adafruit_SHT31(); // İlk SHT31 sensörü
Adafruit_SHT31 sht31_2 = Adafruit_SHT31(); // İkinci SHT31 sensörü
RTC_DS3231 rtc; // RTC modülü
// Kuluçka tipi isimleri
const char* typeNames[] = {"Tavuk", "Kaz", "Bildircin", "Manuel"};
// Global değişkenler
IncubatorSettings settings; // Kuluçka ayarları
MotorSettings motorSettings; // Motor ayarları
ErrorManager errorMgr; // Hata yöneticisi
int currentMenu = MENU_MAIN; // Mevcut menü durumu
bool inSubMenu = false; // Alt menüde miyiz?
unsigned long lastKeyPressTime = 0; // Son tuş basma zamanı
unsigned long lastDebounceTime = 0; // Son debounce zamanı
bool backlightState = false; // Arka ışık durumu
unsigned long previousMillis = 0; // Önceki millis değeri
char lastKey = 0; // Son basılan tuş
// Her sensör için ayrı offset değişkenleri
float tempOffset1 = 0.0; // Sensör 1 sıcaklık kalibrasyon offseti
float humOffset1 = 0.0; // Sensör 1 nem kalibrasyon offseti
float tempOffset2 = 0.0; // Sensör 2 sıcaklık kalibrasyon offseti
float humOffset2 = 0.0; // Sensör 2 nem kalibrasyon offseti
bool isFirstMotorActivation = true; // İlk motor aktivasyonu için bayrak
char messageBuffer[40]; // Mesaj tamponlama için
// Fonksiyon prototipleri
void handleMainMenu(char key);
void handleSettingsMenu(char key);
void handleMotorMenu(char key);
void handleCalibrationMenu(char key);
void handleNormalOperation(char key);
void showMainMenu();
void showSettingsMenu();
void showMotorMenu();
void showSensorInfo();
void showCalibrationMenu();
void showMessage(const char* message);
void showError(const char* message);
void setupChicken();
void setupGoose();
void setupQuail();
void setupManual();
void resetSystem();
void askForReset();
void saveState();
void savePersistentState();
void loadState();
void loadMotorSettings();
uint8_t calculateChecksum();
void updateSensorsAndControl();
void updateMainDisplay(float temp, float hum, int currentDay);
void updateBacklight();
void testMotor();
void controlMotor(uint32_t currentTime);
void controlTemperature(float currentTemp);
void controlHumidity(float currentHum);
void checkAndUpdateSettings(int currentDay);
void getAverageReadings(float &avgTemp, float &avgHum);
char getKey();
float getNumberInput();
void enterSafeMode(const char* errorMsg);
void handlePowerFailure();
void debugPrint(const char* message);
void debugPrint(const char* message, int value);
void debugPrint(const char* message, float value);
void debugPrint(const char* message, uint32_t value);
void resetAllOffsets();
void saveTimeWithBackup(uint32_t timeToSave);
uint32_t getStartTimeFromEEPROM();
// Yeni prototip fonksiyonlar - her sensör için ayrı kalibrasyon
void calibrateTemperature1();
void calibrateHumidity1();
void calibrateTemperature2();
void calibrateHumidity2();
SensorData readSHT31_1();
SensorData readSHT31_2();
uint32_t getTimeFromRTC();
int getMotorCountdown();
void setMotorDuration();
void setMotorInterval();
// Saat çevirimi için yardımcı fonksiyon - time_t yerine uint32_t kullanıyoruz
uint32_t getTimeFromRTC() {
DateTime now = rtc.now();
return now.unixtime(); // .get() yerine .unixtime() kullanıyoruz
}
// Debug mesajları için yardımcı fonksiyonlar
void debugPrint(const char* message) {
if (DEBUG) {
Serial.println(message);
}
}
void debugPrint(const char* message, int value) {
if (DEBUG) {
Serial.print(message);
Serial.println(value);
}
}
void debugPrint(const char* message, float value) {
if (DEBUG) {
Serial.print(message);
Serial.println(value);
}
}
void debugPrint(const char* message, uint32_t value) {
if (DEBUG) {
Serial.print(message);
Serial.println(value);
}
}
// 4. Zamanı güvenli bir şekilde kaydetmek için özel fonksiyon
// Başlangıç zamanını 3 farklı adreste yedekleyerek kaydeder
void saveTimeWithBackup(uint32_t timeToSave) {
Serial.print("Zaman kaydediliyor: ");
Serial.println(timeToSave);
// Ana konum + 2 yedek konuma kaydet
EEPROM.put(EEPROM_START_TIME, timeToSave);
delay(10);
wdt_reset();
EEPROM.put(EEPROM_START_TIME_BACKUP1, timeToSave);
delay(10);
wdt_reset();
EEPROM.put(EEPROM_START_TIME_BACKUP2, timeToSave);
delay(10);
wdt_reset();
// Doğrulama
uint32_t readBack;
EEPROM.get(EEPROM_START_TIME, readBack);
if (readBack != timeToSave) {
Serial.println("UYARI: Zaman kaydı doğrulanamadı!");
} else {
Serial.println("Zaman başarıyla kaydedildi ve doğrulandı.");
}
}
// 5. Yedeklerden doğru zamanı almak için güçlendirilmiş fonksiyon
uint32_t getStartTimeFromEEPROM() {
uint32_t time1, time2, time3;
EEPROM.get(EEPROM_START_TIME, time1);
EEPROM.get(EEPROM_START_TIME_BACKUP1, time2);
EEPROM.get(EEPROM_START_TIME_BACKUP2, time3);
Serial.print("Okunan zamanlar - Ana: ");
Serial.print(time1);
Serial.print(", Yedek1: ");
Serial.print(time2);
Serial.print(", Yedek2: ");
Serial.println(time3);
uint32_t currentTime = getTimeFromRTC();
Serial.print("Şimdiki zaman: ");
Serial.println(currentTime);
// Üç zamandan en az ikisi aynı ise, bu zamanı kullan
if (time1 == time2 || time1 == time3) return time1;
if (time2 == time3) return time2;
// Zamanlar tutarsız, geçerli görünen zamanı döndür
if (time1 > 1577836800 && time1 < currentTime) return time1; // 2020'den sonra ve şimdiden önce
if (time2 > 1577836800 && time2 < currentTime) return time2;
if (time3 > 1577836800 && time3 < currentTime) return time3;
// Hiçbiri geçerli değilse, 0 döndür
return 0;
}
void setup() {
Serial.begin(9600);
debugPrint("Kuluçka Makinesi Başlatılıyor...");
Wire.begin();
// Pin modlarını ayarla
pinMode(RELAY1, OUTPUT);
pinMode(RELAY2, OUTPUT);
pinMode(RELAY3, OUTPUT);
pinMode(RELAY4, OUTPUT);
pinMode(ROW1, INPUT_PULLUP);
pinMode(ROW2, INPUT_PULLUP);
pinMode(ROW3, INPUT_PULLUP);
pinMode(ROW4, INPUT_PULLUP);
pinMode(COL1, OUTPUT);
pinMode(COL2, OUTPUT);
pinMode(COL3, OUTPUT);
pinMode(COL4, OUTPUT);
pinMode(BACKLIGHT_PIN, OUTPUT);
// Başlangıç durumları - DEĞİŞTİRİLDİ: LOW/HIGH tersine çevrildi
digitalWrite(RELAY1, HIGH); // Isıtıcı kapalı
digitalWrite(RELAY2, HIGH); // Nemlendirici kapalı
digitalWrite(RELAY3, LOW); // Fanlar sürekli çalışacak
digitalWrite(RELAY4, HIGH); // Motor kapalı
digitalWrite(BACKLIGHT_PIN, LOW); // Arka ışık kapalı
// Röle durumlarını sıfırla
settings.heaterState = false;
settings.humidifierState = false;
// LCD başlatma
lcd.begin();
lcd.setContrast(50);
lcd.clearDisplay();
lcd.setTextSize(1);
lcd.setCursor(0,0);
lcd.println("Kulucka");
lcd.println("Makinesi v2.4");
lcd.println("Baslatiliyor...");
lcd.display();
delay(2000);
// Sensörleri başlat
if (!sht31_1.begin(SHT31_1_ADDR)) {
showError("Sensor 1 Hata!");
debugPrint("Sensor 1 başlatılamadı!");
delay(2000);
}
if (!sht31_2.begin(SHT31_2_ADDR)) {
showError("Sensor 2 Hata!");
debugPrint("Sensor 2 başlatılamadı!");
delay(2000);
}
// Sensör heater'larını kapat
sht31_1.heater(false);
sht31_2.heater(false);
// RTC başlatma - düzeltilmiş
if (!rtc.begin()) {
showError("RTC Hatasi!");
debugPrint("RTC başlatılamadı!");
delay(2000);
}
// RTC pil kontrolü
if (rtc.lostPower()) {
showError("RTC Pil Zayif!");
debugPrint("RTC pil zayıf, tarih ayarlanıyor");
delay(2000);
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
// EEPROM'dan ayarları yükle
loadState();
// Motor ayarlarını yükle
loadMotorSettings();
// EEPROM yüklendikten sonra debug bilgisi
debugPrint("Yüklenen ayarlar:");
debugPrint("Tip:", settings.type);
debugPrint("Başlangıç zamanı:", settings.startTime);
debugPrint("Güncel RTC zamanı:", getTimeFromRTC());
debugPrint("Hedef sıcaklık:", settings.targetTemp);
debugPrint("Hedef nem:", settings.targetHum);
// Güç kesintisi kontrolü
handlePowerFailure();
// İlk açılışta menüyü göster
if (settings.type == 4 || settings.type == 255) {
settings.type = 4;
showMainMenu();
currentMenu = MENU_MAIN;
isFirstMotorActivation = false; // Ana menüde motor aktivasyonu yok
} else {
// Normal çalışma modunda, ilk ekranı güncelle
float avgTemp = 0.0, avgHum = 0.0;
SensorData sensor1 = readSHT31_1();
SensorData sensor2 = readSHT31_2();
if (sensor1.isValid && sensor2.isValid) {
avgTemp = (sensor1.temp + sensor2.temp) / 2.0;
avgHum = (sensor1.hum + sensor2.hum) / 2.0;
} else if (sensor1.isValid) {
avgTemp = sensor1.temp;
avgHum = sensor1.hum;
} else if (sensor2.isValid) {
avgTemp = sensor2.temp;
avgHum = sensor2.hum;
}
uint32_t currentTime = getTimeFromRTC();
int currentDay = 1; // Varsayılan olarak 1. gün
if (settings.startTime > 0 && currentTime > settings.startTime) {
currentDay = (currentTime - settings.startTime) / 86400 + 1;
if (currentDay > settings.totalDays) {
currentDay = settings.totalDays;
}
}
// Aktif kuluçka bilgisi göster
lcd.clearDisplay();
lcd.setTextSize(1);
lcd.setCursor(0,0);
lcd.println("Kulucka Aktif");
lcd.setCursor(0,16);
sprintf(messageBuffer, "Gun: %d/%d", currentDay, settings.totalDays);
lcd.println(messageBuffer);
lcd.setCursor(0,32);
lcd.println("Devam ediyor...");
lcd.display();
delay(2000);
updateMainDisplay(avgTemp, avgHum, currentDay);
currentMenu = -1; // Normal çalışma modu
// Güç kesintisi sonrası ilk dönüşü iptal et
if (millis() < 3000) { // Sistem yeni açıldıysa (3 saniyeden az çalışıyorsa)
isFirstMotorActivation = true; // İlk dönüşü aktifleştir
previousMillis = 0; // Motor zamanını sıfırla
debugPrint("Güç kesintisi sonrası, ilk motor dönüşü aktifleştirildi");
} else {
isFirstMotorActivation = false; // Normal zamanlı dönüşlere devam et
}
}
// Son güç zamanını kaydet
uint32_t currentTime = getTimeFromRTC();
EEPROM.put(EEPROM_LAST_POWER_TIME, currentTime);
// Watchdog timer başlat - 8 saniye
wdt_enable(WDTO_8S);
debugPrint("Watchdog timer aktifleştirildi (8s)");
}
void loop() {
// Watchdog timer'ı sıfırla
wdt_reset();
// Arka ışık kontrolü
updateBacklight();
// Tuş kontrolü
char key = getKey();
// Menü durumunu kontrol et
if (currentMenu == MENU_SETTINGS) {
if(key) {
handleSettingsMenu(key);
}
}
else if (currentMenu == MENU_MOTOR) {
if(key) {
handleMotorMenu(key);
}
}
else if (currentMenu == MENU_CALIBRATION) {
if(key) {
handleCalibrationMenu(key);
}
}
// Ana menü modu
else if (settings.type == 4 || currentMenu == MENU_MAIN) {
if(key) {
handleMainMenu(key);
}
}
// Normal çalışma modu
else {
// Sensör değerlerini oku ve kontrolleri yap
updateSensorsAndControl();
// Tuş işlemlerini yönet
if(key) {
handleNormalOperation(key);
}
// EEPROM'a periyodik kayıt
savePersistentState();
}
// Gecikme - ana döngüyü yavaşlat
delay(100);
}
// Güç kesintilerini tespit ve kayıt
void handlePowerFailure() {
uint32_t currentTime = getTimeFromRTC();
uint32_t lastPowerTime = 0;
EEPROM.get(EEPROM_LAST_POWER_TIME, lastPowerTime);
if (lastPowerTime > 0 && currentTime > lastPowerTime) {
uint32_t downtime = currentTime - lastPowerTime;
if (downtime > 3600) { // 1 saatten uzun kesinti
// Kullanıcıyı bilgilendir
sprintf(messageBuffer, "Guc Kesintisi\nSure: %d saat", (int)(downtime / 3600));
showMessage(messageBuffer);
debugPrint("Güç kesintisi tespit edildi:");
debugPrint("Son güç zamanı:", lastPowerTime);
debugPrint("Şimdiki zaman:", currentTime);
debugPrint("Kesinti süresi (saat):", (int)(downtime / 3600));
}
}
// Şimdiki zamanı kaydet
EEPROM.put(EEPROM_LAST_POWER_TIME, currentTime);
}
void updateSensorsAndControl() {
// Sensör değerlerini oku
float avgTemp, avgHum;
getAverageReadings(avgTemp, avgHum);
uint32_t currentTime = getTimeFromRTC();
int currentDay = 0;
// Gün hesaplama güvenliği ekle
if (settings.startTime > 0 && settings.startTime <= currentTime) {
currentDay = (currentTime - settings.startTime) / 86400 + 1;
// Geçersiz gün değerlerini düzelt
if (currentDay <= 0 || currentDay > settings.totalDays) {
currentDay = 1;
}
} else {
currentDay = 1;
}
// Son günlerde özel ayarları kontrol et
checkAndUpdateSettings(currentDay);
// Sıcaklık kontrolü
controlTemperature(avgTemp);
// Nem kontrolü
controlHumidity(avgHum);
// Motor kontrolü
controlMotor(currentTime);
// Ekranı güncelle
updateMainDisplay(avgTemp, avgHum, currentDay);
}
void getAverageReadings(float &avgTemp, float &avgHum) {
SensorData sensor1 = readSHT31_1();
SensorData sensor2 = readSHT31_2();
if(sensor1.isValid && sensor2.isValid) {
avgTemp = (sensor1.temp + sensor2.temp) / 2.0;
avgHum = (sensor1.hum + sensor2.hum) / 2.0;
// Sensörlerin farkı çok fazlaysa uyarı - fark eşiğini 15 dereceye yükselttik
if(abs(sensor1.temp - sensor2.temp) > 15.0) {
errorMgr.tempErrors++;
if(errorMgr.tempErrors > MAX_ERRORS) {
enterSafeMode("Sicaklik Fark!\nCok Fazla!");
}
} else {
errorMgr.tempErrors = 0;
}
}
else if(sensor1.isValid) {
// Tek sensör çalışıyorsa hata sayacını artırma, normal çalışmaya devam et
avgTemp = sensor1.temp;
avgHum = sensor1.hum;
// errorMgr.tempErrors++ satırını kaldırdık
}
else if(sensor2.isValid) {
// Tek sensör çalışıyorsa hata sayacını artırma, normal çalışmaya devam et
avgTemp = sensor2.temp;
avgHum = sensor2.hum;
// errorMgr.tempErrors++ satırını kaldırdık
}
else {
// Tüm sensörler arızalıysa güvenli moda geç
errorMgr.tempErrors++;
if(errorMgr.tempErrors > MAX_ERRORS) {
enterSafeMode("Tum Sensorler\nHatali!");
}
// Geçersiz değerler
avgTemp = 0;
avgHum = 0;
}
}
void handleMainMenu(char key) {
switch(key) {
case '1':
setupChicken();
showMessage("Tavuk secildi\n21 gun");
currentMenu = -1; // Normal çalışma moduna geç
break;
case '2':
setupGoose();
showMessage("Kaz secildi\n30 gun");
currentMenu = -1; // Normal çalışma moduna geç
break;
case '3':
setupQuail();
showMessage("Bildircin secildi\n18 gun");
currentMenu = -1; // Normal çalışma moduna geç
break;
case '4':
setupManual();
currentMenu = -1; // Normal çalışma moduna geç
break;
case '5':
showSettingsMenu();
currentMenu = MENU_SETTINGS;
break;
}
}
void handleNormalOperation(char key) {
switch(key) {
case 'A': // Sıcaklık artır
settings.targetTemp += 0.1;
if(settings.targetTemp > 39.0) settings.targetTemp = 39.0;
saveState();
break;
case 'B': // Sıcaklık azalt
settings.targetTemp -= 0.1;
if(settings.targetTemp < 35.0) settings.targetTemp = 35.0;
saveState();
break;
case 'C': // Nem artır
settings.targetHum += 1.0;
if(settings.targetHum > 85) settings.targetHum = 85;
saveState();
break;
case 'D': // Nem azalt
settings.targetHum -= 1.0;
if(settings.targetHum < 30) settings.targetHum = 30;
saveState();
break;
case '5': // Ayarlar menüsüne git
showSettingsMenu();
currentMenu = MENU_SETTINGS;
break;
case '6': // Manuel motor kontrolü
testMotor();
break;
}
}
void handleSettingsMenu(char key) {
switch(key) {
case '1': // Sıcaklık
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Sicaklik (C):");
lcd.setCursor(0,8);
lcd.print("Mevcut: ");
lcd.println(settings.targetTemp, 1);
lcd.setCursor(0,16);
lcd.println("Yeni Deger:");
lcd.display();
settings.targetTemp = getNumberInput();
if(settings.targetTemp < 35.0) settings.targetTemp = 35.0;
if(settings.targetTemp > 39.0) settings.targetTemp = 39.0;
saveState();
showSettingsMenu();
break;
case '2': // Nem
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Nem (%):");
lcd.setCursor(0,8);
lcd.print("Mevcut: ");
lcd.println(settings.targetHum, 1);
lcd.setCursor(0,16);
lcd.println("Yeni Deger:");
lcd.display();
settings.targetHum = getNumberInput();
if(settings.targetHum < 30) settings.targetHum = 30;
if(settings.targetHum > 85) settings.targetHum = 85;
saveState();
showSettingsMenu();
break;
case '3': // Motor
showMotorMenu();
currentMenu = MENU_MOTOR;
break;
case '4': // Kalibrasyon
showCalibrationMenu();
currentMenu = MENU_CALIBRATION;
break;
case '5': // Sensör bilgileri
showSensorInfo();
break;
case '6': // Resetleme seçeneği (Ayarlar menüsünde güvenli)
askForReset();
break;
case '*': // Geri
if (settings.type == 4) {
showMainMenu();
currentMenu = MENU_MAIN;
} else {
currentMenu = -1; // Normal çalışma moduna dön
// Geri dönerken ekranı güncelle
float avgTemp, avgHum;
getAverageReadings(avgTemp, avgHum);
uint32_t currentTime = getTimeFromRTC();
int currentDay = (currentTime - settings.startTime) / 86400 + 1;
if (currentDay <= 0 || currentDay > settings.totalDays) currentDay = 1;
updateMainDisplay(avgTemp, avgHum, currentDay);
}
break;
}
}
void handleMotorMenu(char key) {
// Loglama ekle
Serial.print("Motor Menu Key: ");
Serial.println(key);
switch(key) {
case '1': // Aralık değiştir
setMotorInterval();
break;
case '2': // Süre değiştir
setMotorDuration();
break;
case '*': // Geri tuşu
showSettingsMenu();
currentMenu = MENU_SETTINGS;
break;
}
}
// GÜNCEL KALİBRASYON MENÜSÜ - Offset sıfırlama seçeneği eklenmiş
void handleCalibrationMenu(char key) {
switch(key) {
case '1': // Sensör 1 Sıcaklık kalibrasyonu
calibrateTemperature1();
break;
case '2': // Sensör 1 Nem kalibrasyonu
calibrateHumidity1();
break;
case '3': // Sensör 2 Sıcaklık kalibrasyonu
calibrateTemperature2();
break;
case '4': // Sensör 2 Nem kalibrasyonu
calibrateHumidity2();
break;
case '5': // YENİ: Tüm offset değerlerini sıfırlama
resetAllOffsets();
break;
case '*': // Geri tuşu
showSettingsMenu();
currentMenu = MENU_SETTINGS;
break;
}
}
// 3. Offset sıfırlama fonksiyonu
void resetAllOffsets() {
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Offsetleri");
lcd.println("Sifirlamak Icin");
lcd.setCursor(0,16);
lcd.println("# tusu: Evet");
lcd.println("* tusu: Hayir");
lcd.display();
while(true) {
char key = getKey();
if(key == '#') {
// Tüm offsetleri sıfırla
settings.tempOffset1 = 0.0;
settings.tempOffset2 = 0.0;
settings.humOffset1 = 0.0;
settings.humOffset2 = 0.0;
// EEPROM'a kaydet
EEPROM.put(EEPROM_TEMP_OFFSET1, 0.0f);
delay(10);
EEPROM.put(EEPROM_HUM_OFFSET1, 0.0f);
delay(10);
EEPROM.put(EEPROM_TEMP_OFFSET2, 0.0f);
delay(10);
EEPROM.put(EEPROM_HUM_OFFSET2, 0.0f);
delay(10);
wdt_reset();
lcd.clearDisplay();
lcd.setCursor(0,16);
lcd.println("Offsetler");
lcd.println("Sifirlandi!");
lcd.display();
delay(2000);
showCalibrationMenu();
break;
}
else if(key == '*') {
showCalibrationMenu();
break;
}
wdt_reset();
}
}
/* Motor ayar fonksiyonları */
void setMotorDuration() {
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Sure (sn):");
lcd.setCursor(0,8);
lcd.print("Mevcut: ");
lcd.println(motorSettings.duration / 1000);
lcd.setCursor(0,16);
lcd.println("Yeni Deger:");
lcd.display();
float seconds = getNumberInput();
if(seconds < 1) seconds = 1;
if(seconds > 7) seconds = 7; // Watchdog limiti altında kalmak için maksimum 7 saniye
motorSettings.duration = seconds * 1000;
EEPROM.put(EEPROM_MOTOR_DURATION, motorSettings.duration);
showMotorMenu();
}
void setMotorInterval() {
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Aralik (dk):");
lcd.setCursor(0,8);
lcd.print("Mevcut: ");
lcd.println(motorSettings.interval / 60000);
lcd.setCursor(0,16);
lcd.println("Yeni Deger:");
lcd.display();
float minutes = getNumberInput();
if(minutes < 60) minutes = 60;
if(minutes > 480) minutes = 480;
motorSettings.interval = minutes * 60000;
EEPROM.put(EEPROM_MOTOR_INTERVAL, motorSettings.interval);
showMotorMenu();
}
// GÜNCELLENMİŞ setupChicken
void setupChicken() {
settings.type = 0;
settings.targetTemp = 37.8; // Başlangıç sıcaklığı 37.8°C
settings.targetHum = 65; // Başlangıç nemi %65
settings.totalDays = 21;
// Zamanı al ve güvenli şekilde kaydet
uint32_t currentTime = getTimeFromRTC();
settings.startTime = currentTime;
// Hemen kaydet (acil durum için)
EEPROM.put(EEPROM_TYPE, settings.type);
delay(5);
// Güvenli zaman kaydı
saveTimeWithBackup(settings.startTime);
settings.motorActive = true;
settings.heaterState = false;
settings.humidifierState = false;
isFirstMotorActivation = true; // İlk dönüşü aktifleştir
// Sonra diğer verileri kaydet
saveState();
debugPrint("Tavuk kuluçkası başladı. Zaman:", settings.startTime);
// Başlama mesajı göster
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Tavuk Kuluckasi");
lcd.setCursor(0,16);
lcd.println("Baslatildi!");
lcd.setCursor(0,32);
// Günün ve saatin doğru gösterildiğini kontrol etmek için
DateTime now = rtc.now();
sprintf(messageBuffer, "Gun: 1/%d", settings.totalDays);
lcd.println(messageBuffer);
lcd.setCursor(0,40);
sprintf(messageBuffer, "Saat: %02d:%02d", now.hour(), now.minute());
lcd.println(messageBuffer);
lcd.display();
delay(3000);
}
// GÜNCELLENMİŞ setupGoose
void setupGoose() {
settings.type = 1;
settings.targetTemp = 37.7; // Başlangıç sıcaklığı 37.7°C
settings.targetHum = 60; // Başlangıç nemi %60
settings.totalDays = 30;
// Zamanı al ve güvenli şekilde kaydet
uint32_t currentTime = getTimeFromRTC();
settings.startTime = currentTime;
// Hemen kaydet (acil durum için)
EEPROM.put(EEPROM_TYPE, settings.type);
delay(5);
// Güvenli zaman kaydı
saveTimeWithBackup(settings.startTime);
settings.motorActive = true;
settings.heaterState = false;
settings.humidifierState = false;
isFirstMotorActivation = true; // İlk dönüşü aktifleştir
// Sonra diğer verileri kaydet
saveState();
debugPrint("Kaz kuluçkası başladı. Zaman:", settings.startTime);
// Başlama mesajı göster
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Kaz Kuluckasi");
lcd.setCursor(0,16);
lcd.println("Baslatildi!");
lcd.setCursor(0,32);
// Günün ve saatin doğru gösterildiğini kontrol etmek için
DateTime now = rtc.now();
sprintf(messageBuffer, "Gun: 1/%d", settings.totalDays);
lcd.println(messageBuffer);
lcd.setCursor(0,40);
sprintf(messageBuffer, "Saat: %02d:%02d", now.hour(), now.minute());
lcd.println(messageBuffer);
lcd.display();
delay(3000);
}
// GÜNCELLENMİŞ setupQuail
void setupQuail() {
settings.type = 2;
settings.targetTemp = 37.7; // Başlangıç sıcaklığı 37.7°C
settings.targetHum = 65; // Başlangıç nemi %65
settings.totalDays = 18;
// Zamanı al ve güvenli şekilde kaydet
uint32_t currentTime = getTimeFromRTC();
settings.startTime = currentTime;
// Hemen kaydet (acil durum için)
EEPROM.put(EEPROM_TYPE, settings.type);
delay(5);
// Güvenli zaman kaydı
saveTimeWithBackup(settings.startTime);
settings.motorActive = true;
settings.heaterState = false;
settings.humidifierState = false;
isFirstMotorActivation = true; // İlk dönüşü aktifleştir
// Sonra diğer verileri kaydet
saveState();
debugPrint("Bıldırcın kuluçkası başladı. Zaman:", settings.startTime);
// Başlama mesajı göster
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Bildircin Kulucka");
lcd.setCursor(0,16);
lcd.println("Baslatildi!");
lcd.setCursor(0,32);
// Günün ve saatin doğru gösterildiğini kontrol etmek için
DateTime now = rtc.now();
sprintf(messageBuffer, "Gun: 1/%d", settings.totalDays);
lcd.println(messageBuffer);
lcd.setCursor(0,40);
sprintf(messageBuffer, "Saat: %02d:%02d", now.hour(), now.minute());
lcd.println(messageBuffer);
lcd.display();
delay(3000);
}
// GÜNCELLENMİŞ setupManual
void setupManual() {
settings.type = 3;
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Manuel Ayar");
lcd.setCursor(0,8);
lcd.println("Sicaklik (C):");
lcd.display();
float temp = getNumberInput();
if(temp < 35.0) temp = 35.0;
if(temp > 39.0) temp = 39.0;
settings.targetTemp = temp;
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Nem (%):");
lcd.display();
float hum = getNumberInput();
if(hum < 30) hum = 30;
if(hum > 85) hum = 85;
settings.targetHum = hum;
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Gun Sayisi:");
lcd.display();
int days = (int)getNumberInput();
if(days < 1) days = 1;
if(days > 60) days = 60;
settings.totalDays = days;
// Zamanı al ve güvenli şekilde kaydet
uint32_t currentTime = getTimeFromRTC();
settings.startTime = currentTime;
// Hemen kaydet (acil durum için)
EEPROM.put(EEPROM_TYPE, settings.type);
delay(5);
// Güvenli zaman kaydı
saveTimeWithBackup(settings.startTime);
settings.motorActive = true;
settings.heaterState = false;
settings.humidifierState = false;
isFirstMotorActivation = true; // İlk dönüşü aktifleştir
// Sonra diğer verileri kaydet
saveState();
debugPrint("Manuel kuluçka başladı. Zaman:", settings.startTime);
// Başlama mesajı göster
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Manuel Kulucka");
lcd.setCursor(0,16);
lcd.println("Baslatildi!");
lcd.setCursor(0,32);
// Günün ve saatin doğru gösterildiğini kontrol etmek için
DateTime now = rtc.now();
sprintf(messageBuffer, "Gun: 1/%d", settings.totalDays);
lcd.println(messageBuffer);
lcd.setCursor(0,40);
sprintf(messageBuffer, "Saat: %02d:%02d", now.hour(), now.minute());
lcd.println(messageBuffer);
lcd.display();
delay(3000);
}
void resetSystem() {
settings.type = 4;
saveState();
showMainMenu();
}
void askForReset() {
lcd.clearDisplay();
lcd.setTextSize(1);
lcd.setCursor(0,0);
lcd.println("Sifirlama?");
lcd.println("#-Evet *-Hayir");
lcd.display();
while(true) {
char key = getKey();
if(key == '#') {
settings.type = 4;
saveState();
showMainMenu();
currentMenu = MENU_MAIN;
break;
}
else if(key == '*') {
showSettingsMenu();
currentMenu = MENU_SETTINGS;
break;
}
// Watchdog resetleme
wdt_reset();
}
}
void checkAndUpdateSettings(int currentDay) {
switch(settings.type) {
case 0: // Tavuk
if (currentDay >= 19) {
settings.targetTemp = 37.5; // Son 3 gün sıcaklık 37.5°C
settings.targetHum = 75; // Son 3 gün nem %75
settings.motorActive = false;
}
break;
case 1: // Kaz
if (currentDay >= 27) {
settings.targetTemp = 37.5; // Son 4 gün sıcaklık 37.5°C
settings.targetHum = 80; // Son 4 gün nem %80
settings.motorActive = false;
}
break;
case 2: // Bıldırcın
if (currentDay >= 16) {
settings.targetTemp = 37.5; // Son 3 gün sıcaklık 37.5°C
settings.targetHum = 75; // Son 3 gün nem %75
settings.motorActive = false;
}
break;
}
}
void controlTemperature(float currentTemp) {
static float lastTemp = 0;
static unsigned long lastTime = 0;
unsigned long currentTime = millis();
float tempRate = 0;
if (lastTime > 0) {
// Sıcaklık değişim hızı (derece/dakika)
tempRate = (currentTemp - lastTemp) * 60000 / (currentTime - lastTime);
}
if(!errorMgr.safeMode) {
if(currentTemp < (settings.targetTemp - TEMP_HYSTERESIS)) {
digitalWrite(RELAY1, LOW); // DEĞİŞTİRİLDİ: HIGH → LOW (Isıtıcıyı aç)
settings.heaterState = true;
}
else if(currentTemp > (settings.targetTemp + TEMP_HYSTERESIS) ||
(tempRate > 0.5 && currentTemp > settings.targetTemp)) {
// Sıcaklık çok yüksek VEYA hızla yükseliyor ve hedefin üzerinde
digitalWrite(RELAY1, HIGH); // DEĞİŞTİRİLDİ: LOW → HIGH (Isıtıcıyı kapat)
settings.heaterState = false;
}
// Histerezis aralığında durumu değiştirme
}
lastTemp = currentTemp;
lastTime = currentTime;
}
void controlHumidity(float currentHum) {
if(!errorMgr.safeMode) {
if(currentHum < (settings.targetHum - HUM_HYSTERESIS)) {
digitalWrite(RELAY2, LOW); // DEĞİŞTİRİLDİ: HIGH → LOW (Nemlendiricyi aç)
settings.humidifierState = true;
}
else if(currentHum > (settings.targetHum + HUM_HYSTERESIS)) {
digitalWrite(RELAY2, HIGH); // DEĞİŞTİRİLDİ: LOW → HIGH (Nemlendiricyi kapat)
settings.humidifierState = false;
}
// Histerezis aralığında durumu değiştirme
}
}
// GÜNCELLENMİŞ MOTOR KONTROLÜ - Watchdog reset önleme
void controlMotor(uint32_t currentTime) {
if (!settings.motorActive || errorMgr.safeMode) return;
unsigned long currentMillis = millis();
// İlk açılışta motoru 1 dakika sonra çalıştır
if (isFirstMotorActivation) {
// Sistem açıldıktan 1 dakika sonra ilk motor aktivasyonu
if (currentMillis > 60000) { // 60 saniye
debugPrint("İlk motor aktivasyonu yapılıyor");
digitalWrite(RELAY4, LOW); // DEĞİŞTİRİLDİ: HIGH → LOW (Motoru çalıştır)
// Motor çalışma süresi
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Motor");
lcd.println("Ilk Donus");
lcd.println("Yapiliyor...");
lcd.display();
// Büyük bir delay yerine küçük adımlarla bekle ve watchdog'u sıfırla
for (unsigned long start = millis(); millis() - start < motorSettings.duration;) {
delay(100); // 100ms adımlarla bekle
wdt_reset(); // Her 100ms'de bir watchdog'u sıfırla
}
digitalWrite(RELAY4, HIGH); // DEĞİŞTİRİLDİ: LOW → HIGH (Motoru durdur)
debugPrint("İlk motor aktivasyonu tamamlandı");
previousMillis = currentMillis;
isFirstMotorActivation = false; // Artık ilk aktivasyon değil
// Kritik verileri hemen EEPROM'a kaydet
saveTimeWithBackup(settings.startTime);
delay(20); // EEPROM yazmanın tamamlanması için bekle
wdt_reset(); // Watchdog'u tekrar sıfırla
}
}
else if (currentMillis - previousMillis >= motorSettings.interval) {
debugPrint("Periyodik motor dönüşü yapılıyor");
digitalWrite(RELAY4, LOW); // DEĞİŞTİRİLDİ: HIGH → LOW (Motoru çalıştır)
// Motor çalışma süresi
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Motor");
lcd.println("Donduruluyor...");
lcd.display();
// Büyük bir delay yerine küçük adımlarla bekle ve watchdog'u sıfırla
for (unsigned long start = millis(); millis() - start < motorSettings.duration;) {
delay(100); // 100ms adımlarla bekle
wdt_reset(); // Her 100ms'de bir watchdog'u sıfırla
}
digitalWrite(RELAY4, HIGH); // DEĞİŞTİRİLDİ: LOW → HIGH (Motoru durdur)
debugPrint("Motor dönüşü tamamlandı");
previousMillis = currentMillis;
}
}
// GÜNCELLENMİŞ TEST MOTOR FONKSİYONU - Watchdog reset önleme
void testMotor() {
if(errorMgr.safeMode) {
showMessage("Guvenli Mod!\nMotor Pasif");
return;
}
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Motor Test");
lcd.println("Calistiriliyor...");
lcd.display();
digitalWrite(RELAY4, LOW); // DEĞİŞTİRİLDİ: HIGH → LOW (Motoru çalıştır)
// Büyük bir delay yerine küçük adımlarla bekle ve watchdog'u sıfırla
for (unsigned long start = millis(); millis() - start < 1000;) {
delay(100); // 100ms adımlarla bekle
wdt_reset(); // Her 100ms'de bir watchdog'u sıfırla
}
digitalWrite(RELAY4, HIGH); // DEĞİŞTİRİLDİ: LOW → HIGH (Motoru durdur)
showMessage("Motor Testi\nTamamlandi");
}
// SENSÖRLERİ OKUMA - DAHA SAĞLAM OKUMA MEKANİZMASI
SensorData readSHT31_1() {
SensorData data;
// 3 kez okuma dene
for (int attempt = 0; attempt < 3; attempt++) {
data.temp = sht31_1.readTemperature() + settings.tempOffset1;
data.hum = sht31_1.readHumidity() + settings.humOffset1;
if (!isnan(data.temp) && !isnan(data.hum)) {
break; // Başarılı okuma, döngüden çık
}
delay(100); // Yeniden denemeden önce kısa bekle
wdt_reset(); // Watchdog'u sıfırla
}
data.isValid = (!isnan(data.temp) && !isnan(data.hum) &&
data.temp > -10 && data.temp < 60 &&
data.hum >= 0 && data.hum <= 100);
return data;
}
SensorData readSHT31_2() {
SensorData data;
// 3 kez okuma dene
for (int attempt = 0; attempt < 3; attempt++) {
data.temp = sht31_2.readTemperature() + settings.tempOffset2;
data.hum = sht31_2.readHumidity() + settings.humOffset2;
if (!isnan(data.temp) && !isnan(data.hum)) {
break; // Başarılı okuma, döngüden çık
}
delay(100); // Yeniden denemeden önce kısa bekle
wdt_reset(); // Watchdog'u sıfırla
}
data.isValid = (!isnan(data.temp) && !isnan(data.hum) &&
data.temp > -10 && data.temp < 60 &&
data.hum >= 0 && data.hum <= 100);
return data;
}
int getMotorCountdown() {
unsigned long timeSinceLastMotor = millis() - previousMillis;
if(timeSinceLastMotor >= motorSettings.interval) return 0;
return (motorSettings.interval - timeSinceLastMotor) / 60000;
}
// GÜNCELLENMİŞ LOAD STATE - Daha fazla kontrol ve hata önleme
void loadState() {
// EEPROM'dan veri oku
uint8_t storedType;
EEPROM.get(EEPROM_TYPE, storedType); // Önce bir byte olarak oku
debugPrint("EEPROM'dan tip okundu:", (int)storedType);
// EEPROM ilk kez kullanılıyorsa veya geçersiz değer varsa
if (storedType == 255 || storedType < 0 || storedType > 4) {
debugPrint("Geçersiz tip, varsayılanlar yükleniyor");
settings.type = 4; // Sıfırlama durumu - kuluçka tipi seçimi
settings.targetTemp = 37.5; // Varsayılan sıcaklık
settings.targetHum = 65.0; // Varsayılan nem
settings.totalDays = 0;
settings.startTime = 0; // Başlangıç zamanı 0 olarak ayarla
settings.motorActive = false;
settings.heaterState = false;
settings.humidifierState = false;
// Her sensör için sıfır offset başlangıç değeri
settings.tempOffset1 = 0;
settings.tempOffset2 = 0;
settings.humOffset1 = 0;
settings.humOffset2 = 0;
isFirstMotorActivation = false; // İlk açılışta motor aktivasyonu yok
// Varsayılanları EEPROM'a kaydet
EEPROM.put(EEPROM_TYPE, settings.type);
delay(5);
EEPROM.put(EEPROM_START_TIME, settings.startTime);
delay(5);
EEPROM.put(EEPROM_TEMP, settings.targetTemp);
delay(5);
EEPROM.put(EEPROM_HUM, settings.targetHum);
delay(5);
// Her sensör için offset değerleri kaydet
EEPROM.put(EEPROM_TEMP_OFFSET1, settings.tempOffset1);
delay(5);
EEPROM.put(EEPROM_HUM_OFFSET1, settings.humOffset1);
delay(5);
EEPROM.put(EEPROM_TEMP_OFFSET2, settings.tempOffset2);
delay(5);
EEPROM.put(EEPROM_HUM_OFFSET2, settings.humOffset2);
delay(5);
wdt_reset(); // Watchdog'u sıfırla
return;
}
// Geçerli bir kuluçka tipi varsa diğer ayarları oku
settings.type = storedType;
// Geliştirilmiş zaman alma fonksiyonu
uint32_t storedTime = getStartTimeFromEEPROM();
settings.startTime = storedTime;
// Debug - zaman kontrolü
Serial.print("Ana EEPROM'dan okunan zaman: ");
Serial.println(settings.startTime);
EEPROM.get(EEPROM_TEMP, settings.targetTemp);
EEPROM.get(EEPROM_HUM, settings.targetHum);
// Her sensör için offset değerlerini oku
EEPROM.get(EEPROM_TEMP_OFFSET1, tempOffset1);
EEPROM.get(EEPROM_HUM_OFFSET1, humOffset1);
EEPROM.get(EEPROM_TEMP_OFFSET2, tempOffset2);
EEPROM.get(EEPROM_HUM_OFFSET2, humOffset2);
settings.tempOffset1 = tempOffset1;
settings.humOffset1 = humOffset1;
settings.tempOffset2 = tempOffset2;
settings.humOffset2 = humOffset2;
// Tutarlılık kontrolü - değerler mantıklı aralıkta değilse düzelt
if (settings.targetTemp < 30.0 || settings.targetTemp > 40.0) {
settings.targetTemp = 37.5; // Varsayılan sıcaklık
}
if (settings.targetHum < 0.0 || settings.targetHum > 100.0) {
settings.targetHum = 65.0; // Varsayılan nem
}
// Zaman kontrolü ve düzeltme
uint32_t currentTime = getTimeFromRTC();
// Aktif kuluçka var ama zaman geçersiz
if (settings.type >= 0 && settings.type <= 3) {
if (settings.startTime == 0 || settings.startTime > currentTime ||
(currentTime - settings.startTime) > 86400 * 100) { // 100 günden eski ise
Serial.println("UYARI: Geçersiz başlangıç zamanı, şimdiki zamanla ayarlanıyor");
settings.startTime = currentTime;
saveTimeWithBackup(settings.startTime);
}
// Başlangıç zamanı hesaplandıktan sonra gün kontrolü
int daysPassed = (currentTime - settings.startTime) / 86400 + 1;
Serial.print("Hesaplanan gün: ");
Serial.println(daysPassed);
// Toplam gün sayısını ayarla (tip değiştirilmişse)
switch(settings.type) {
case 0: settings.totalDays = 21; break; // Tavuk
case 1: settings.totalDays = 30; break; // Kaz
case 2: settings.totalDays = 18; break; // Bıldırcın
case 3: break; // Manuel - değiştirme
}
}
// Motor ayarları
if (settings.type <= 3) { // Aktif bir kuluçka varsa
settings.motorActive = true;
// Son günlerde motor kontrolünü devre dışı bırak
uint32_t currentTime = getTimeFromRTC();
if (settings.startTime > 0 && currentTime > settings.startTime) {
int currentDay = (currentTime - settings.startTime) / 86400 + 1;
if ((settings.type == 0 && currentDay >= 19) || // Tavuk
(settings.type == 1 && currentDay >= 27) || // Kaz
(settings.type == 2 && currentDay >= 16)) { // Bıldırcın
settings.motorActive = false;
}
}
}
// Güç kesintisi tespit edildiğinde ilk motor dönüşünü aktifleştir
isFirstMotorActivation = true;
debugPrint("Ayarlar yüklendi: Tip:", settings.type);
debugPrint("Başlangıç zamanı:", settings.startTime);
debugPrint("Hedef sıcaklık:", settings.targetTemp);
debugPrint("Hedef nem:", settings.targetHum);
}
void loadMotorSettings() {
EEPROM.get(EEPROM_MOTOR_DURATION, motorSettings.duration);
EEPROM.get(EEPROM_MOTOR_INTERVAL, motorSettings.interval);
// Geçersiz değerler için varsayılanları kullan
if (motorSettings.duration < 1000 || motorSettings.duration > 8000) {
motorSettings.duration = MOTOR_DURATION_DEFAULT;
}
if (motorSettings.interval < 3600000 || motorSettings.interval > 28800000) {
motorSettings.interval = MOTOR_INTERVAL_DEFAULT;
}
// Motor süresini watchdog limiti altında tut (güvenlik önlemi)
if (motorSettings.duration > 7000) {
motorSettings.duration = 7000; // Maksimum 7 saniye (8 saniye watchdog limiti)
}
debugPrint("Motor ayarları yüklendi:");
debugPrint("Süre (ms):", motorSettings.duration);
debugPrint("Aralık (ms):", motorSettings.interval);
}
// GÜNCELLENMİŞ SAVE STATE - Watchdog reset ile güvenli yazma
void saveState() {
// Önce kritik verileri kaydet
EEPROM.put(EEPROM_TYPE, settings.type);
EEPROM.put(EEPROM_START_TIME, settings.startTime);
// Kısa bir bekleme ile EEPROM'un yazması için zaman tanı
delay(10);
wdt_reset(); // Burada da watchdog'u sıfırla
// Sonra diğer verileri kaydet
EEPROM.put(EEPROM_TEMP, settings.targetTemp);
EEPROM.put(EEPROM_HUM, settings.targetHum);
// Her sensör için offset değerlerini kaydet
EEPROM.put(EEPROM_TEMP_OFFSET1, settings.tempOffset1);
EEPROM.put(EEPROM_HUM_OFFSET1, settings.humOffset1);
EEPROM.put(EEPROM_TEMP_OFFSET2, settings.tempOffset2);
EEPROM.put(EEPROM_HUM_OFFSET2, settings.humOffset2);
// Bekle ve watchdog'u sıfırla
delay(10);
wdt_reset();
// Checksum hesapla ve kaydet
uint8_t checksum = calculateChecksum();
EEPROM.put(EEPROM_CHECKSUM, checksum);
delay(20); // EEPROM yazma işleminin tamamlanması için ek bekleme
wdt_reset(); // Son bir kez daha watchdog'u sıfırla
debugPrint("Ayarlar EEPROM'a kaydedildi");
}
// Kalıcı durum kaydetme - sadece değişen verileri kaydet
void savePersistentState() {
static uint8_t lastType = 255;
static float lastTargetTemp = 0;
static float lastTargetHum = 0;
static unsigned long lastSaveTime = 0;
// EEPROM'a çok sık yazma yapmamak için - en fazla 5 dakika arayla
if (millis() - lastSaveTime < EEPROM_WRITE_INTERVAL) {
return;
}
// Sadece değişen değerleri kaydet - EEPROM ömrünü uzatır
if (settings.type != lastType) {
EEPROM.put(EEPROM_TYPE, settings.type);
lastType = settings.type;
}
if (abs(settings.targetTemp - lastTargetTemp) > 0.1) {
EEPROM.put(EEPROM_TEMP, settings.targetTemp);
lastTargetTemp = settings.targetTemp;
}
if (abs(settings.targetHum - lastTargetHum) > 0.1) {
EEPROM.put(EEPROM_HUM, settings.targetHum);
lastTargetHum = settings.targetHum;
}
// Sadece 6 saatte bir startTime güncelle (çok sık yazma yapma)
static unsigned long lastTimeUpdate = 0;
if (millis() - lastTimeUpdate > 21600000) { // 6 saat
saveTimeWithBackup(settings.startTime);
lastTimeUpdate = millis();
}
lastSaveTime = millis();
}
// Checksum hesaplama
uint8_t calculateChecksum() {
uint8_t checksum = 0;
// settings içindeki kritik değerleri kullanarak basit bir checksum oluştur
checksum += settings.type;
checksum += (uint8_t)(settings.targetTemp * 10);
checksum += (uint8_t)(settings.targetHum * 10);
checksum += (uint8_t)(settings.totalDays);
// Başlangıç zamanından da bir değer al
checksum += (uint8_t)(settings.startTime & 0xFF);
return checksum;
}
void showMainMenu() {
lcd.clearDisplay();
lcd.setTextSize(1);
lcd.setCursor(0,0);
lcd.println("Kulucka Tipi:");
lcd.setCursor(0,8);
lcd.println("1-Tavuk");
lcd.setCursor(0,16);
lcd.println("2-Kaz");
lcd.setCursor(0,24);
lcd.println("3-Bildircin");
lcd.setCursor(0,32);
lcd.println("4-Manuel");
lcd.setCursor(0,40);
lcd.println("5-Ayarlar");
lcd.display();
}
void showSettingsMenu() {
lcd.clearDisplay();
lcd.setTextSize(1);
lcd.setCursor(0,0);
lcd.println("Ayarlar:");
lcd.setCursor(0,8);
lcd.println("1-Sicaklik");
lcd.setCursor(0,16);
lcd.println("2-Nem");
lcd.setCursor(0,24);
lcd.println("3-Motor");
lcd.setCursor(0,32);
lcd.println("4-Kalibrasyon");
lcd.setCursor(0,40);
lcd.println("5-Sens 6-Reset");
lcd.display();
}
void showMotorMenu() {
lcd.clearDisplay();
lcd.setTextSize(1);
lcd.setCursor(0,0);
lcd.println("Motor Ayarlari:");
lcd.setCursor(0,8);
lcd.print("Aralik: ");
lcd.print(motorSettings.interval / 60000);
lcd.println(" dk");
lcd.setCursor(0,16);
lcd.print("Sure: ");
lcd.print(motorSettings.duration / 1000);
lcd.println(" sn");
lcd.setCursor(0,24);
lcd.println("1-Aralik D.");
lcd.setCursor(0,32);
lcd.println("2-Sure D.");
lcd.display();
}
// HER SENSÖR BİLGİSİNİ AYRI AYRI GÖSTEREN SENSÖR BİLGİLERİ EKRANI
void showSensorInfo() {
lcd.clearDisplay();
lcd.setTextSize(1);
lcd.setCursor(0,0);
lcd.println("Sensor Bilgileri:");
SensorData sensor1 = readSHT31_1();
SensorData sensor2 = readSHT31_2();
lcd.setCursor(0,8);
lcd.print("S1: ");
if (sensor1.isValid) {
lcd.print(sensor1.temp, 1);
lcd.print("C ");
lcd.print(sensor1.hum, 1);
lcd.print("%");
} else {
lcd.print("Hata!");
}
lcd.setCursor(0,16);
lcd.print("S2: ");
if (sensor2.isValid) {
lcd.print(sensor2.temp, 1);
lcd.print("C ");
lcd.print(sensor2.hum, 1);
lcd.print("%");
} else {
lcd.print("Hata!");
}
lcd.setCursor(0,24);
lcd.print("Fark: ");
if (sensor1.isValid && sensor2.isValid) {
lcd.print(abs(sensor1.temp - sensor2.temp), 1);
lcd.print("C ");
lcd.print(abs(sensor1.hum - sensor2.hum), 1);
lcd.print("%");
} else {
lcd.print("--");
}
// Offset değerlerini de göster
lcd.setCursor(0,32);
lcd.print("OfsT: ");
lcd.print(settings.tempOffset1, 1);
lcd.print("/");
lcd.print(settings.tempOffset2, 1);
lcd.setCursor(0,40);
lcd.print("OfsH: ");
lcd.print(settings.humOffset1, 1);
lcd.print("/");
lcd.print(settings.humOffset2, 1);
lcd.display();
// Tuş bekle
while(true) {
char key = getKey();
if(key == '*') {
showSettingsMenu();
currentMenu = MENU_SETTINGS;
break;
}
wdt_reset(); // Watchdog'u resetle
}
}
// KALİBRASYON MENÜSÜ - HER SENSÖR İÇİN AYRI AYRI KALİBRASYON
void showCalibrationMenu() {
lcd.clearDisplay();
lcd.setTextSize(1);
lcd.setCursor(0,0);
lcd.println("Kalibrasyon:");
lcd.setCursor(0,8);
lcd.println("1-S1 Sicaklik");
lcd.setCursor(0,16);
lcd.println("2-S1 Nem");
lcd.setCursor(0,24);
lcd.println("3-S2 Sicaklik");
lcd.setCursor(0,32);
lcd.println("4-S2 Nem");
lcd.setCursor(0,40);
lcd.println("5-Offsetleri Sifirla");
lcd.display();
}
void showMessage(const char* message) {
lcd.clearDisplay();
lcd.setTextSize(1);
lcd.setCursor(0,16);
lcd.println(message);
lcd.display();
// Watchdog'u periyodik olarak sıfırlayarak mesaj süresince bekle
unsigned long startTime = millis();
while (millis() - startTime < MESSAGE_DURATION) {
delay(100);
wdt_reset();
}
}
void showError(const char* message) {
lcd.clearDisplay();
lcd.setTextSize(1);
lcd.setCursor(0,0);
lcd.println("HATA!");
lcd.setCursor(0,16);
lcd.println(message);
lcd.display();
}
// GÜNCELLENMİŞ ANA EKRAN - Daha sağlam gün hesaplama
void updateMainDisplay(float temp, float hum, int currentDay) {
lcd.clearDisplay();
lcd.setTextSize(1);
// Sıcaklık gösterimi
lcd.setCursor(0,0);
lcd.print("S:");
if (temp < 100 && temp > -40) {
lcd.print(temp, 1);
lcd.print(settings.heaterState ? "+" : " ");
} else {
lcd.print("--.-");
}
lcd.print("/");
lcd.print(settings.targetTemp, 1);
lcd.print("C");
// Nem gösterimi
lcd.setCursor(0,8);
lcd.print("N:");
if (hum < 100 && hum >= 0) {
lcd.print(hum, 1);
lcd.print(settings.humidifierState ? "+" : " ");
} else {
lcd.print("--.-");
}
lcd.print("/");
lcd.print(settings.targetHum, 1);
lcd.print("%");
// Gün gösterimi - daha güçlü hesaplama
lcd.setCursor(0,16);
lcd.print("G:");
// Aktif kuluçka varsa (tip 0-3)
if (settings.type >= 0 && settings.type <= 3) {
int dayNum = 1; // Varsayılan olarak 1. gün
// RTCDEN GÜN HESAPLA
uint32_t currentTime = getTimeFromRTC();
if (settings.startTime > 0 && currentTime >= settings.startTime) {
dayNum = (currentTime - settings.startTime) / 86400 + 1;
// Makul bir aralık kontrolü
if (dayNum > settings.totalDays) {
dayNum = settings.totalDays;
}
if (dayNum <= 0) {
dayNum = 1;
}
}
lcd.print(dayNum);
lcd.print("/");
lcd.print(settings.totalDays);
// Debug - gün hesaplama
Serial.print("Gün hesaplama: Şimdi=");
Serial.print(currentTime);
Serial.print(", Başlangıç=");
Serial.print(settings.startTime);
Serial.print(", Fark=");
Serial.print(currentTime - settings.startTime);
Serial.print(", Gün=");
Serial.println(dayNum);
// Kuluçka tipi gösterimi
lcd.setCursor(0,24);
lcd.print(typeNames[settings.type]);
// Motor zamanını göster
if (settings.motorActive) {
lcd.print(" M:");
lcd.print(getMotorCountdown());
lcd.print("dk");
}
} else {
lcd.print("--/--"); // Aktif kuluçka yok
}
// Saat gösterimi
DateTime now = rtc.now();
lcd.setCursor(0,32);
if (now.hour() < 10) lcd.print("0");
lcd.print(now.hour());
lcd.print(":");
if (now.minute() < 10) lcd.print("0");
lcd.print(now.minute());
// Alt bilgi satırı - hangi sensörlerin aktif olduğunu göster
lcd.setCursor(0,40);
if (settings.type >= 0 && settings.type <= 3 && settings.startTime > 0) {
uint32_t currentTime = getTimeFromRTC();
int dayNum = (currentTime - settings.startTime) / 86400 + 1;
if (dayNum > 0 && dayNum <= settings.totalDays &&
dayNum >= settings.totalDays - 2) {
lcd.print("!SON GUNLER!");
} else if (errorMgr.safeMode) {
lcd.print("!GUVENLI MOD!");
} else {
// Aktif sensörleri göster
SensorData sensor1 = readSHT31_1();
SensorData sensor2 = readSHT31_2();
lcd.print("S:");
lcd.print(sensor1.isValid ? "1" : "-");
lcd.print(sensor2.isValid ? "2" : "-");
lcd.print(" 5-Menu 6-Motor");
}
} else {
lcd.print("S:-- 5-Menu 6-Motor");
}
// İlerleme çubuğu ekle
if (settings.type >= 0 && settings.type <= 3 && settings.startTime > 0) {
uint32_t currentTime = getTimeFromRTC();
int dayNum = (currentTime - settings.startTime) / 86400 + 1;
if (dayNum > 0 && dayNum <= settings.totalDays) {
int progress = map(dayNum, 0, settings.totalDays, 0, 80);
lcd.drawRect(2, 47, 80, 2, BLACK);
lcd.fillRect(2, 47, progress, 2, BLACK);
}
}
lcd.display();
}
void updateBacklight() {
unsigned long currentTime = millis();
if (backlightState && (currentTime - lastKeyPressTime >= BACKLIGHT_TIMEOUT)) {
digitalWrite(BACKLIGHT_PIN, LOW);
backlightState = false;
}
}
char getKey() {
static unsigned long lastDebounceTime = 0;
static char lastKey = 0;
const unsigned long debounceDelay = 250; // Tuş sıçraması için daha kısa bekleme süresi
char keys[4][4] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
// Geçen son tuş basma süresini kontrol et
unsigned long currentTime = millis();
if (currentTime - lastDebounceTime < debounceDelay) {
return 0; // Debounce süresi dolmadan yeni tuş algılamayı reddet
}
for(int i = 0; i < 4; i++) {
digitalWrite(COL1 + i*2, LOW);
for(int j = 0; j < 4; j++) {
if(digitalRead(ROW1 + j*2) == LOW) {
// Tuş basıldığında arka ışığı aç
lastKeyPressTime = currentTime;
if (!backlightState) {
digitalWrite(BACKLIGHT_PIN, HIGH);
backlightState = true;
}
digitalWrite(COL1 + i*2, HIGH);
lastDebounceTime = currentTime; // Debounce zamanını güncelle
return keys[j][i];
}
}
digitalWrite(COL1 + i*2, HIGH);
}
return 0;
}
float getNumberInput() {
String input = "";
bool hasDecimal = false;
while(true) {
char key = getKey();
if(key) {
if(key >= '0' && key <= '9') {
if(input.length() < 5) { // Maksimum 5 karakter
input += key;
lcd.print(key);
lcd.display();
}
}
else if(key == '*' && !hasDecimal) {
if(!input.length()) input = "0";
input += '.';
hasDecimal = true;
lcd.print('.');
lcd.display();
}
else if(key == '#' && input.length() > 0) {
return input.toFloat();
}
}
// Watchdog resetleme
wdt_reset();
}
}
// SENSÖR 1 İÇİN SICAKLIK KALİBRASYONU
void calibrateTemperature1() {
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("S1 Sicaklik Kal.:");
lcd.setCursor(0,8);
lcd.println("Referans olcum:");
lcd.display();
float refTemp = getNumberInput();
// Sensör 1'den okuma al
SensorData sensor1 = readSHT31_1();
if (!sensor1.isValid) {
showError("Sensor 1\nHatali!");
delay(2000);
showCalibrationMenu();
return;
}
// Offset hesapla (sadece sensör 1 için)
float rawTemp = sht31_1.readTemperature(); // Offset olmadan ham değer
float newOffset = refTemp - rawTemp;
settings.tempOffset1 = newOffset;
tempOffset1 = newOffset;
EEPROM.put(EEPROM_TEMP_OFFSET1, tempOffset1);
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("S1 Sicaklik");
lcd.println("Kalibr. Tamam");
lcd.setCursor(0,16);
lcd.print("Yeni Offset: ");
lcd.print(settings.tempOffset1, 1);
lcd.display();
delay(2000);
showCalibrationMenu();
}
// SENSÖR 1 İÇİN NEM KALİBRASYONU
void calibrateHumidity1() {
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("S1 Nem Kalibr.:");
lcd.setCursor(0,8);
lcd.println("Referans olcum:");
lcd.display();
float refHum = getNumberInput();
// Sensör 1'den okuma al
SensorData sensor1 = readSHT31_1();
if (!sensor1.isValid) {
showError("Sensor 1\nHatali!");
delay(2000);
showCalibrationMenu();
return;
}
// Offset hesapla (sadece sensör 1 için)
float rawHum = sht31_1.readHumidity(); // Offset olmadan ham değer
float newOffset = refHum - rawHum;
settings.humOffset1 = newOffset;
humOffset1 = newOffset;
EEPROM.put(EEPROM_HUM_OFFSET1, humOffset1);
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("S1 Nem");
lcd.println("Kalibr. Tamam");
lcd.setCursor(0,16);
lcd.print("Yeni Offset: ");
lcd.print(settings.humOffset1, 1);
lcd.display();
delay(2000);
showCalibrationMenu();
}
// SENSÖR 2 İÇİN SICAKLIK KALİBRASYONU
void calibrateTemperature2() {
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("S2 Sicaklik Kal.:");
lcd.setCursor(0,8);
lcd.println("Referans olcum:");
lcd.display();
float refTemp = getNumberInput();
// Sensör 2'den okuma al
SensorData sensor2 = readSHT31_2();
if (!sensor2.isValid) {
showError("Sensor 2\nHatali!");
delay(2000);
showCalibrationMenu();
return;
}
// Offset hesapla (sadece sensör 2 için)
float rawTemp = sht31_2.readTemperature(); // Offset olmadan ham değer
float newOffset = refTemp - rawTemp;
settings.tempOffset2 = newOffset;
tempOffset2 = newOffset;
EEPROM.put(EEPROM_TEMP_OFFSET2, tempOffset2);
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("S2 Sicaklik");
lcd.println("Kalibr. Tamam");
lcd.setCursor(0,16);
lcd.print("Yeni Offset: ");
lcd.print(settings.tempOffset2, 1);
lcd.display();
delay(2000);
showCalibrationMenu();
}
// SENSÖR 2 İÇİN NEM KALİBRASYONU
void calibrateHumidity2() {
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("S2 Nem Kalibr.:");
lcd.setCursor(0,8);
lcd.println("Referans olcum:");
lcd.display();
float refHum = getNumberInput();
// Sensör 2'den okuma al
SensorData sensor2 = readSHT31_2();
if (!sensor2.isValid) {
showError("Sensor 2\nHatali!");
delay(2000);
showCalibrationMenu();
return;
}
// Offset hesapla (sadece sensör 2 için)
float rawHum = sht31_2.readHumidity(); // Offset olmadan ham değer
float newOffset = refHum - rawHum;
settings.humOffset2 = newOffset;
humOffset2 = newOffset;
EEPROM.put(EEPROM_HUM_OFFSET2, humOffset2);
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("S2 Nem");
lcd.println("Kalibr. Tamam");
lcd.setCursor(0,16);
lcd.print("Yeni Offset: ");
lcd.print(settings.humOffset2, 1);
lcd.display();
delay(2000);
showCalibrationMenu();
}
void enterSafeMode(const char* errorMsg) {
if (!errorMgr.safeMode) {
errorMgr.safeMode = true;
// Tüm çıkışları güvenli duruma al - DEĞİŞTİRİLDİ: LOW/HIGH tersine çevrildi
digitalWrite(RELAY1, HIGH); // Isıtıcıyı kapat
digitalWrite(RELAY2, HIGH); // Nemlenidiriciyi kapat
digitalWrite(RELAY4, HIGH); // Motoru kapat
digitalWrite(RELAY3, LOW); // Fanı çalıştır
// Durumları güncelle
settings.heaterState = false;
settings.humidifierState = false;
// Hata mesajını göster ve seri porta yazdır
showError(errorMsg);
debugPrint("GÜVENLİ MOD AKTİFLEŞTİRİLDİ:");
debugPrint(errorMsg);
// Hata durumunu EEPROM'a kaydet
EEPROM.write(EEPROM_ERROR_FLAG, 1);
}
}