#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ı
#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_OFFSET 14 // Sıcaklık offset (4 byte)
#define EEPROM_HUM_OFFSET 18 // Nem offset (4 byte)
#define EEPROM_MOTOR_DURATION 22 // Motor çalışma süresi (4 byte)
#define EEPROM_MOTOR_INTERVAL 26 // Motor çalışma aralığı (4 byte)
#define EEPROM_CHECKSUM 30 // Veri doğrulama (1 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)
// 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
float tempOffset; // Sıcaklık kalibrasyon offseti
float humOffset; // Nem kalibrasyon offseti
};
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;
}
};
// 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ü
// 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ş
float tempOffset = 0.0; // Sıcaklık kalibrasyon offseti
float humOffset = 0.0; // Nem kalibrasyon offseti
// Kuluçka tipi isimleri
const char* typeNames[] = {"Tavuk", "Kaz", "Bildircin", "Manuel"};
// 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 calibrateTemperature();
void calibrateHumidity();
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
}
void setup() {
Serial.begin(9600);
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ı
digitalWrite(RELAY1, LOW); // Isıtıcı kapalı
digitalWrite(RELAY2, LOW); // Nemlendirici kapalı
digitalWrite(RELAY3, HIGH); // Fanlar sürekli çalışacak
digitalWrite(RELAY4, LOW); // 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.0");
lcd.println("Baslatiliyor...");
lcd.display();
delay(2000);
// Sensörleri başlat
if (!sht31_1.begin(SHT31_1_ADDR)) {
showError("Sensor 1 Hata!");
delay(2000);
}
if (!sht31_2.begin(SHT31_2_ADDR)) {
showError("Sensor 2 Hata!");
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!");
delay(2000);
}
// RTC pil kontrolü
if (rtc.lostPower()) {
showError("RTC Pil Zayif!");
delay(2000);
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
// EEPROM'dan ayarları yükle
loadState();
// Motor ayarlarını yükle
loadMotorSettings();
// İlk açılışta menüyü göster
if (settings.type == 4 || settings.type == 255) {
settings.type = 4;
showMainMenu();
currentMenu = MENU_MAIN;
} 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;
}
}
updateMainDisplay(avgTemp, avgHum, currentDay);
currentMenu = -1; // Normal çalışma modu
}
// Watchdog timer başlat - 8 saniye
wdt_enable(WDTO_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);
}
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 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;
}
}
void handleCalibrationMenu(char key) {
switch(key) {
case '1': // Sıcaklık kalibrasyonu
calibrateTemperature();
break;
case '2': // Nem kalibrasyonu
calibrateHumidity();
break;
case '*': // Geri tuşu
showSettingsMenu();
currentMenu = MENU_SETTINGS;
break;
}
}
/* 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 > 30) seconds = 30;
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();
}
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;
settings.startTime = getTimeFromRTC();
settings.motorActive = true;
settings.heaterState = false;
settings.humidifierState = false;
saveState();
}
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;
settings.startTime = getTimeFromRTC();
settings.motorActive = true;
settings.heaterState = false;
settings.humidifierState = false;
saveState();
}
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;
settings.startTime = getTimeFromRTC();
settings.motorActive = true;
settings.heaterState = false;
settings.humidifierState = false;
saveState();
}
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;
settings.startTime = getTimeFromRTC();
settings.motorActive = true;
settings.heaterState = false;
settings.humidifierState = false;
saveState();
}
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) {
if(!errorMgr.safeMode) {
if(currentTemp < (settings.targetTemp - TEMP_HYSTERESIS)) {
digitalWrite(RELAY1, HIGH);
settings.heaterState = true;
}
else if(currentTemp > (settings.targetTemp + TEMP_HYSTERESIS)) {
digitalWrite(RELAY1, LOW);
settings.heaterState = false;
}
// Histerezis aralığında durumu değiştirme
}
}
void controlHumidity(float currentHum) {
if(!errorMgr.safeMode) {
if(currentHum < (settings.targetHum - HUM_HYSTERESIS)) {
digitalWrite(RELAY2, HIGH);
settings.humidifierState = true;
}
else if(currentHum > (settings.targetHum + HUM_HYSTERESIS)) {
digitalWrite(RELAY2, LOW);
settings.humidifierState = false;
}
// Histerezis aralığında durumu değiştirme
}
}
void controlMotor(uint32_t currentTime) {
if (!settings.motorActive || errorMgr.safeMode) return;
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= motorSettings.interval) {
digitalWrite(RELAY4, HIGH);
// Motor çalışma süresi
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Motor");
lcd.println("Donduruluyor...");
lcd.display();
delay(motorSettings.duration);
digitalWrite(RELAY4, LOW);
previousMillis = currentMillis;
}
}
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, HIGH);
delay(1000);
digitalWrite(RELAY4, LOW);
showMessage("Motor Testi\nTamamlandi");
}
SensorData readSHT31_1() {
SensorData data;
data.temp = sht31_1.readTemperature() + settings.tempOffset;
data.hum = sht31_1.readHumidity() + settings.humOffset;
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;
data.temp = sht31_2.readTemperature() + settings.tempOffset;
data.hum = sht31_2.readHumidity() + settings.humOffset;
data.isValid = (!isnan(data.temp) && !isnan(data.hum) &&
data.temp > -10 && data.temp < 60 &&
data.hum >= 0 && data.hum <= 100);
return data;
}
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;
}
}
int getMotorCountdown() {
unsigned long timeSinceLastMotor = millis() - previousMillis;
if(timeSinceLastMotor >= motorSettings.interval) return 0;
return (motorSettings.interval - timeSinceLastMotor) / 60000;
}
void loadState() {
// EEPROM'dan veri oku
uint8_t storedType;
EEPROM.get(EEPROM_TYPE, storedType); // Önce bir byte olarak oku
// EEPROM ilk kez kullanılıyorsa veya geçersiz değer varsa
if (storedType == 255 || storedType < 0 || storedType > 4) {
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;
settings.tempOffset = 0;
settings.humOffset = 0;
// Varsayılanları EEPROM'a kaydet
EEPROM.put(EEPROM_TYPE, settings.type);
EEPROM.put(EEPROM_START_TIME, settings.startTime);
EEPROM.put(EEPROM_TEMP, settings.targetTemp);
EEPROM.put(EEPROM_HUM, settings.targetHum);
EEPROM.put(EEPROM_TEMP_OFFSET, settings.tempOffset);
EEPROM.put(EEPROM_HUM_OFFSET, settings.humOffset);
return;
}
// Geçerli bir kuluçka tipi varsa diğer ayarları oku
settings.type = storedType;
EEPROM.get(EEPROM_START_TIME, settings.startTime);
EEPROM.get(EEPROM_TEMP, settings.targetTemp);
EEPROM.get(EEPROM_HUM, settings.targetHum);
EEPROM.get(EEPROM_TEMP_OFFSET, tempOffset);
EEPROM.get(EEPROM_HUM_OFFSET, humOffset);
settings.tempOffset = tempOffset;
settings.humOffset = humOffset;
// 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
}
// RTC kontrolü - startTime çok eski veya gelecekte ise düzelt
uint32_t currentTime = getTimeFromRTC();
if (settings.startTime > currentTime || (currentTime - settings.startTime) > 86400 * 60) { // 60 günden eski ise
// RTC ile ayarla
settings.startTime = currentTime;
EEPROM.put(EEPROM_START_TIME, settings.startTime);
}
}
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 > 30000) {
motorSettings.duration = MOTOR_DURATION_DEFAULT;
}
if (motorSettings.interval < 3600000 || motorSettings.interval > 28800000) {
motorSettings.interval = MOTOR_INTERVAL_DEFAULT;
}
}
void saveState() {
EEPROM.put(EEPROM_TYPE, settings.type);
EEPROM.put(EEPROM_START_TIME, settings.startTime);
EEPROM.put(EEPROM_TEMP, settings.targetTemp);
EEPROM.put(EEPROM_HUM, settings.targetHum);
EEPROM.put(EEPROM_TEMP_OFFSET, settings.tempOffset);
EEPROM.put(EEPROM_HUM_OFFSET, settings.humOffset);
// Checksum hesapla ve kaydet
uint8_t checksum = calculateChecksum();
EEPROM.put(EEPROM_CHECKSUM, checksum);
}
// 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
EEPROM.put(EEPROM_START_TIME, 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();
}
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("--");
}
lcd.display();
// Tuş bekle
while(true) {
char key = getKey();
if(key == '*') {
showSettingsMenu();
currentMenu = MENU_SETTINGS;
break;
}
wdt_reset(); // Watchdog'u resetle
}
}
void showCalibrationMenu() {
lcd.clearDisplay();
lcd.setTextSize(1);
lcd.setCursor(0,0);
lcd.println("Kalibrasyon:");
lcd.setCursor(0,12);
lcd.print("Sicaklik: ");
lcd.print(settings.tempOffset, 1);
lcd.println("C");
lcd.setCursor(0,24);
lcd.print("Nem: ");
lcd.print(settings.humOffset, 1);
lcd.println("%");
lcd.setCursor(0,36);
lcd.println("1-Sicak. 2-Nem Kal.");
lcd.display();
}
void showMessage(const char* message) {
lcd.clearDisplay();
lcd.setTextSize(1);
lcd.setCursor(0,16);
lcd.println(message);
lcd.display();
delay(MESSAGE_DURATION);
}
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();
}
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 ve tür gösterimi
lcd.setCursor(0,16);
lcd.print("G:");
if (currentDay > 0 && currentDay <= settings.totalDays) {
lcd.print(currentDay);
lcd.print("/");
lcd.print(settings.totalDays);
} else {
lcd.print("--/--");
}
lcd.setCursor(0,24);
if (settings.type >= 0 && settings.type <= 3) {
lcd.print(typeNames[settings.type]);
// Motor zamanını göster
if (settings.motorActive) {
lcd.print(" M:");
lcd.print(getMotorCountdown());
lcd.print("dk");
}
}
// 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 (currentDay > 0 && currentDay <= settings.totalDays &&
currentDay >= 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");
}
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();
}
}
void calibrateTemperature() {
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Sicaklik Kalibr.:");
lcd.setCursor(0,8);
lcd.println("Referans olcum:");
lcd.display();
float refTemp = getNumberInput();
// Sensör değerlerini oku
float avgTemp, avgHum;
getAverageReadings(avgTemp, avgHum);
// Offset hesapla
float newOffset = refTemp - (avgTemp - settings.tempOffset);
settings.tempOffset = newOffset;
tempOffset = newOffset;
EEPROM.put(EEPROM_TEMP_OFFSET, tempOffset);
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Kalibrasyon");
lcd.println("Tamamlandi");
lcd.setCursor(0,16);
lcd.print("Yeni Offset: ");
lcd.print(settings.tempOffset, 1);
lcd.display();
delay(2000);
showCalibrationMenu();
}
void calibrateHumidity() {
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Nem Kalibr.:");
lcd.setCursor(0,8);
lcd.println("Referans olcum:");
lcd.display();
float refHum = getNumberInput();
// Sensör değerlerini oku
float avgTemp, avgHum;
getAverageReadings(avgTemp, avgHum);
// Offset hesapla
float newOffset = refHum - (avgHum - settings.humOffset);
settings.humOffset = newOffset;
humOffset = newOffset;
EEPROM.put(EEPROM_HUM_OFFSET, humOffset);
lcd.clearDisplay();
lcd.setCursor(0,0);
lcd.println("Kalibrasyon");
lcd.println("Tamamlandi");
lcd.setCursor(0,16);
lcd.print("Yeni Offset: ");
lcd.print(settings.humOffset, 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
digitalWrite(RELAY1, LOW); // Isıtıcıyı kapat
digitalWrite(RELAY2, LOW); // Nemlenidiriciyi kapat
digitalWrite(RELAY4, LOW); // Motoru kapat
digitalWrite(RELAY3, HIGH); // Fanı çalıştır
// Durumları güncelle
settings.heaterState = false;
settings.humidifierState = false;
showError(errorMsg);
// Hata durumunu EEPROM'a kaydet
EEPROM.write(EEPROM_ERROR_FLAG, 1);
}
}