#include <Wire.h>
#include <RTClib.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include "Adafruit_SHT31.h"
#include <avr/wdt.h>
// Pin Tanımları
#define JOY_X A0
#define JOY_Y A2
#define JOY_SW 2
#define FAN_PIN 3
#define MOTOR_PIN 4
#define RELAY_HEAT 11
#define RELAY_HUMID 12
#define RELAY_COOL 13
#define BUZZER_PIN 5
// EEPROM Ayarları
const int ADDR_VERSION = 0;
const byte PROFILE_VERSION = 9; // Versiyon artırıldı
const int ADDR_TUR = 4;
const int ADDR_TARIH = 8;
const int ADDR_SON_DONUS = 12;
const int ADDR_PROFIL = 16;
const int ADDR_SICAK_HATA = 512;
const int ADDR_NEM_HATA = 516;
const int ADDR_SON_ACIL = 520;
const int ADDR_SISTEM_LOG = 524;
const int ADDR_ISTATISTIK = 1000; // İstatistikler için yeni adres
// Güvenlik sabitleri
const float MAX_SICAKLIK = 42.0;
const float MIN_SICAKLIK = 35.0;
const float KRITIK_SICAKLIK = 45.0;
const int MAX_SENSOR_HATA = 10;
const unsigned long SENSOR_TIMEOUT = 30000;
const unsigned long WATCHDOG_RESET = 8000;
// Joystick kalibrasyonu için sabitler
const int JOY_CENTER_X = 512;
const int JOY_CENTER_Y = 512;
const int JOY_DEADZONE = 100;
const int JOY_THRESHOLD = 200;
// Menü Yapıları
enum Button { BTN_YOK, BTN_SAG, BTN_YUKARI, BTN_ASAGI, BTN_SOL, BTN_SEC };
enum MenuOgesi { ANA_EKRAN, TUR_SEC, KULUCAKA_S, KULUCAKA_N, TARIH, HATA_MENU };
enum HataMenuOgesi {
HATA_GENEL,
HATA_SENSOR,
HATA_SICAKLIK,
HATA_NEM,
HATA_SISTEM,
HATA_ISTATISTIK
};
#pragma pack(push, 1)
struct TurProfili {
char isim[12];
int kuluckaGun;
float kuluSicak[2];
float kuluNem[2];
int donusAralik;
int donusSuresi;
};
struct SistemLog {
uint32_t zaman;
uint8_t hataTuru;
float deger1;
float deger2;
};
struct SistemIstatistik {
uint16_t toplamSicakHata;
uint16_t toplamNemHata;
uint16_t toplamAcilDurum;
uint16_t sistemYenidenBaslama;
uint32_t toplamCalismaZamani;
uint32_t sonSicakHata;
uint32_t sonNemHata;
uint32_t sonAcilDurum;
};
#pragma pack(pop)
struct SistemDurumu {
uint8_t aktifTur;
uint32_t baslangicTarihi;
uint32_t sonDonus;
uint32_t donusBaslangic;
bool donuyor;
bool isitici;
bool nemlendirici;
bool sogutma;
bool alarm;
MenuOgesi menu;
HataMenuOgesi hataMenu;
byte sicakSecim;
byte nemSecim;
bool ilkOkuma;
unsigned long snoozeTime;
int sicaklikHata;
int nemHata;
unsigned long sonSensorOkuma;
bool guvenliMod;
unsigned long sistemBaslangic;
bool lcdDirty;
byte tarihSecimi;
SistemIstatistik istatistik;
};
// Sistem Değişkenleri
TurProfili TURLAR[4] = {
{"Tavuk",21,{37.5,38.0},{55,65},3,5},
{"Hindi",28,{37.3,37.6},{60,70},4,7},
{"Bildircn",17,{37.7,38.0},{60,65},2,3},
{"Kaz",30,{37.3,37.6},{65,75},6,10}
};
SistemDurumu durum;
Adafruit_SHT31 sht31;
LiquidCrystal_I2C lcd(0x27, 16, 2);
RTC_DS3231 rtc;
float sicakBuffer[10], nemBuffer[10];
byte sicakIndex=0, nemIndex=0;
float sicaklik=0, nem=0;
float sonGecerliSicaklik=37.5, sonGecerliNem=60;
// Non-blocking zamanlayıcılar
unsigned long sensorTimer = 0;
unsigned long kontrolTimer = 0;
unsigned long ekranTimer = 0;
unsigned long buttonTimer = 0;
unsigned long saglikTimer = 0;
unsigned long donusTimer = 0;
unsigned long alarmTimer = 0;
unsigned long watchdogTimer = 0;
// Button debounce için
Button sonButton = BTN_YOK;
unsigned long sonButtonZaman = 0;
void setup() {
Serial.begin(9600);
// Watchdog timer başlatma
wdt_disable();
wdt_enable(WDTO_8S);
Wire.begin();
lcd.begin(16,2);
lcd.backlight();
lcd.noBlink();
lcd.print("Sistem Baslat...");
// Non-blocking başlatma
unsigned long startTime = millis();
while(millis() - startTime < 1000) {
wdt_reset();
delay(10);
}
// Sistem durumunu başlat
memset(&durum, 0, sizeof(durum));
// Sensor başlatma
if(!sht31.begin(0x44)) {
lcd.clear();
lcd.print("Sensor Hata!");
lcd.setCursor(0,1);
lcd.print("Guvenli Mod");
durum.guvenliMod = true;
logKaydet(1, 0, 0);
bekle(2000);
}
// RTC başlatma
if(!rtc.begin()) {
lcd.clear();
lcd.print("RTC Hata!");
lcd.setCursor(0,1);
lcd.print("Varsayilan Zaman");
logKaydet(4, 1, 0);
bekle(2000);
} else if(rtc.lostPower()) {
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
logKaydet(4, 2, 0);
}
donanimiBaslat();
ayarlariYukle();
durum.ilkOkuma = true;
durum.sistemBaslangic = millis();
durum.lcdDirty = true;
durum.tarihSecimi = 0;
durum.hataMenu = HATA_GENEL;
logKaydet(0, millis()/1000, PROFILE_VERSION);
// Zamanlayıcıları başlat
unsigned long now = millis();
sensorTimer = now;
kontrolTimer = now;
ekranTimer = now;
buttonTimer = now;
saglikTimer = now;
donusTimer = now;
alarmTimer = now;
watchdogTimer = now;
lcd.clear();
lcd.print("Sistem Hazir!");
bekle(1000);
durum.lcdDirty = true;
wdt_reset();
}
void loop() {
unsigned long currentTime = millis();
// Watchdog timer reset
if(currentTime - watchdogTimer >= 1000) {
wdt_reset();
watchdogTimer = currentTime;
}
// Ana döngü frekans kontrolü
static unsigned long lastMainLoop = 0;
if(currentTime - lastMainLoop < 50) {
delay(10);
return;
}
lastMainLoop = currentTime;
// Buton okuma (geliştirilmiş debounce ile)
Button btn = BTN_YOK;
if(currentTime - buttonTimer >= 150) {
btn = tusOku();
if(btn != BTN_YOK && btn != sonButton) {
buttonTimer = currentTime;
sonButton = btn;
durum.lcdDirty = true;
} else if(btn == BTN_YOK) {
sonButton = BTN_YOK;
}
}
// Profil güvenlik kontrolü
profilGuvenlik();
// Menü işlemleri
if(durum.menu != ANA_EKRAN) {
menuIslem(btn);
} else {
anaEkranButonlar(btn);
}
// Sensor okuma
if(currentTime - sensorTimer >= 2000) {
sensorOku();
sensorTimer = currentTime;
}
// Sıcaklık/nem kontrolü
if(currentTime - kontrolTimer >= 5000) {
sicaklikNemKontrol();
kontrolTimer = currentTime;
}
// Yumurta çevirme
if(currentTime - donusTimer >= 10000) {
yumurtaCevir();
donusTimer = currentTime;
}
// Alarm kontrolü
if(currentTime - alarmTimer >= 1000) {
alarmKontrol(btn);
alarmTimer = currentTime;
}
// Ekran güncelleme
if(currentTime - ekranTimer >= 1000 || durum.lcdDirty) {
ekranGuncelle();
ekranTimer = currentTime;
durum.lcdDirty = false;
}
// Sistem sağlık kontrolü
if(currentTime - saglikTimer >= 30000) {
sistemSaglikKontrol();
saglikTimer = currentTime;
}
delay(10);
}
void bekle(unsigned long ms) {
unsigned long startTime = millis();
while(millis() - startTime < ms) {
wdt_reset();
delay(10);
}
}
void anaEkranButonlar(Button btn) {
if(btn == BTN_SEC) {
durum.menu = TUR_SEC;
durum.lcdDirty = true;
} else if(btn == BTN_YUKARI) {
durum.menu = HATA_MENU;
durum.hataMenu = HATA_GENEL;
durum.lcdDirty = true;
}
}
void logKaydet(uint8_t hataTuru, float deger1, float deger2) {
DateTime now = rtc.now();
SistemLog yeniLog;
yeniLog.zaman = now.unixtime();
yeniLog.hataTuru = hataTuru;
yeniLog.deger1 = deger1;
yeniLog.deger2 = deger2;
// Son 20 log kaydını döngüsel olarak sakla
static byte logIndex = 0;
// EEPROM overflow kontrolü
int adres = ADDR_SISTEM_LOG + (logIndex * sizeof(SistemLog));
if(adres + sizeof(SistemLog) < 1024) { // EEPROM sınırı kontrolü
EEPROM.put(adres, yeniLog);
logIndex = (logIndex + 1) % 20;
// Log index'ini sakla
EEPROM.put(ADDR_SISTEM_LOG - 4, logIndex);
}
}
void sensorOku() {
if(durum.guvenliMod) {
sicaklik = sonGecerliSicaklik;
nem = sonGecerliNem;
return;
}
float nt = sht31.readTemperature();
float nh = sht31.readHumidity();
// İlk okuma için buffer doldur
if(durum.ilkOkuma) {
float defaultTemp = isnan(nt) ? 37.5 : nt;
float defaultHum = isnan(nh) ? 60 : nh;
for(int i=0; i<10; i++) {
sicakBuffer[i] = defaultTemp;
nemBuffer[i] = defaultHum;
}
durum.ilkOkuma = false;
sicaklik = defaultTemp;
nem = defaultHum;
sonGecerliSicaklik = sicaklik;
sonGecerliNem = nem;
durum.sonSensorOkuma = millis();
return;
}
// Sıcaklık okuma kontrolü
if(!isnan(nt) && nt > 10 && nt < 60) {
sicakBuffer[sicakIndex] = nt;
sicakIndex = (sicakIndex+1)%10;
durum.sicaklikHata = 0;
durum.sonSensorOkuma = millis();
sicaklik = medyanBul(sicakBuffer, 10);
sonGecerliSicaklik = sicaklik;
} else {
durum.sicaklikHata++;
durum.istatistik.toplamSicakHata++;
if(durum.sicaklikHata > MAX_SENSOR_HATA) {
if(!durum.guvenliMod) {
durum.guvenliMod = true;
durum.lcdDirty = true;
durum.istatistik.sonSicakHata = rtc.now().unixtime();
logKaydet(2, nt, sicaklik);
}
}
sicaklik = sonGecerliSicaklik;
}
// Nem okuma kontrolü
if(!isnan(nh) && nh >= 0 && nh <= 100) {
nemBuffer[nemIndex] = nh;
nemIndex = (nemIndex+1)%10;
durum.nemHata = 0;
nem = medyanBul(nemBuffer, 10);
sonGecerliNem = nem;
} else {
durum.nemHata++;
durum.istatistik.toplamNemHata++;
if(durum.nemHata > MAX_SENSOR_HATA) {
if(!durum.guvenliMod) {
durum.guvenliMod = true;
durum.lcdDirty = true;
durum.istatistik.sonNemHata = rtc.now().unixtime();
logKaydet(3, nh, nem);
}
}
nem = sonGecerliNem;
}
}
float medyanBul(float arr[], int n) {
if(n <= 0) return 0;
float temp[10];
memcpy(temp, arr, n * sizeof(float));
// Bubble sort
for(int i=0; i<n-1; i++) {
for(int j=0; j<n-i-1; j++) {
if(temp[j] > temp[j+1]) {
float t = temp[j];
temp[j] = temp[j+1];
temp[j+1] = t;
}
}
}
if(n % 2 == 0) {
return (temp[n/2-1] + temp[n/2]) / 2.0;
} else {
return temp[n/2];
}
}
void profilGuvenlik() {
for(int i=0; i<4; i++) {
TurProfili &t = TURLAR[i];
// Sıcaklık sınırları
t.kuluSicak[0] = constrain(t.kuluSicak[0], MIN_SICAKLIK, MAX_SICAKLIK-0.5);
t.kuluSicak[1] = constrain(t.kuluSicak[1], t.kuluSicak[0]+0.5, MAX_SICAKLIK);
// Nem sınırları
t.kuluNem[0] = constrain(t.kuluNem[0], 30.0, 95.0);
t.kuluNem[1] = constrain(t.kuluNem[1], t.kuluNem[0]+5, 95.0);
// Çevirme parametreleri
t.donusAralik = constrain(t.donusAralik, 1, 24);
t.donusSuresi = constrain(t.donusSuresi, 1, 30);
// Kuluçka günü
t.kuluckaGun = constrain(t.kuluckaGun, 10, 40);
}
}
void sicaklikNemKontrol() {
if(durum.aktifTur >= 4) return; // Güvenlik kontrolü
TurProfili &t = TURLAR[durum.aktifTur];
// KRİTİK DURUM KONTROLÜ
if(sicaklik > KRITIK_SICAKLIK) {
acilDurumProtokolu();
return;
}
float hedefSicaklik = (t.kuluSicak[0] + t.kuluSicak[1]) / 2.0;
float hedefNem = (t.kuluNem[0] + t.kuluNem[1]) / 2.0;
// Sıcaklık kontrolü - Geliştirilmiş histerezis
float sicaklikFark = sicaklik - hedefSicaklik;
if(sicaklikFark < -0.5) {
digitalWrite(RELAY_HEAT, LOW); // Isıtıcı aç
durum.isitici = true;
} else if(sicaklikFark > 0.5) {
digitalWrite(RELAY_HEAT, HIGH); // Isıtıcı kapat
durum.isitici = false;
}
// Nem kontrolü - Geliştirilmiş histerezis
float nemFark = nem - hedefNem;
if(nemFark < -5) {
digitalWrite(RELAY_HUMID, LOW); // Nemlendirici aç
durum.nemlendirici = true;
} else if(nemFark > 5) {
digitalWrite(RELAY_HUMID, HIGH); // Nemlendirici kapat
durum.nemlendirici = false;
}
// Fan kontrolü - PWM
int fanHiz = hesaplaFanHiz(sicaklik, t.kuluSicak[0], t.kuluSicak[1]);
analogWrite(FAN_PIN, fanHiz);
// Soğutucu kontrolü
if(sicaklikFark > 1.5) {
digitalWrite(RELAY_COOL, LOW); // Soğutucu aç
durum.sogutma = true;
} else if(sicaklikFark < 0.5) {
digitalWrite(RELAY_COOL, HIGH); // Soğutucu kapat
durum.sogutma = false;
}
}
int hesaplaFanHiz(float mevcut, float min, float max) {
if(max <= min) return 100;
float oran = (mevcut - min) / (max - min);
oran = constrain(oran, 0.0, 1.0);
int minHiz = 80;
int maxHiz = 255;
return constrain((int)(minHiz + oran * (maxHiz - minHiz)), minHiz, maxHiz);
}
void acilDurumProtokolu() {
durum.istatistik.toplamAcilDurum++;
durum.istatistik.sonAcilDurum = rtc.now().unixtime();
// Güvenlik önlemleri
digitalWrite(RELAY_HEAT, HIGH); // Isıtıcı kapat
digitalWrite(RELAY_HUMID, HIGH); // Nemlendirici kapat
digitalWrite(RELAY_COOL, LOW); // Soğutucu aç
analogWrite(FAN_PIN, 255); // Fan tam hız
logKaydet(4, sicaklik, nem); // Eksik parametreler tamamlandı
}
// Non-blocking alarm sesi
for(int i=0; i<10; i++) {
tone(BUZZER_PIN, 1000, 200);
bekle(300);
}
lcd.clear();
lcd.print("ACIL DURUM!");
lcd.setCursor(0,1);
lcd.print("Sicak: ");
lcd.print(sicaklik,1);
lcd.print("C");
DateTime now = rtc.now();
EEPROM.put(ADDR_SICAK_HATA, now.unixtime());
bekle(5000);
durum.lcdDirty = true;
}
void yumurtaCevir() {
if(!kuluckaDonemindeMi()) {
digitalWrite(MOTOR_PIN, HIGH);
durum.donuyor = false;
return;
}
DateTime now = rtc.now();
TurProfili &t = TURLAR[durum.aktifTur];
if(!durum.donuyor) {
uint32_t gecenSaniye = now.unixtime() - durum.sonDonus;
if(gecenSaniye >= (unsigned long)t.donusAralik * 3600) {
durum.donuyor = true;
durum.donusBaslangic = now.unixtime();
digitalWrite(MOTOR_PIN, LOW);
durum.lcdDirty = true;
tone(BUZZER_PIN, 500, 100);
delay(150);
tone(BUZZER_PIN, 700, 100);
}
} else {
uint32_t sureFark = now.unixtime() - durum.donusBaslangic;
if(sureFark >= (unsigned long)t.donusSuresi * 60) {
digitalWrite(MOTOR_PIN, HIGH);
durum.donuyor = false;
durum.sonDonus = now.unixtime();
ayarlariKaydet();
tone(BUZZER_PIN, 800, 200); // Eksik kapanış parantezi eklendi
}
}
}
} else {
uint32_t sureFark = now.unixtime() - durum.donusBaslangic;
if(sureFark >= (unsigned long)t.donusSuresi * 60) {
digitalWrite(MOTOR_PIN, HIGH); // Motor kapat
durum.donuyor = false;
durum.sonDonus = now.unixtime();
ayarlariKaydet();
lcd.clear();
lcd.print("Fabrika Ayarlari");
lcd.setCursor(0,1);
lcd.print("Yuklendi!");
bekle(2000);
durum.lcdDirty = true;
}
void donanimiBaslat() {
// Pin modları
pinMode(JOY_SW, INPUT_PULLUP);
pinMode(FAN_PIN, OUTPUT);
pinMode(MOTOR_PIN, OUTPUT);
pinMode(RELAY_HEAT, OUTPUT);
pinMode(RELAY_HUMID, OUTPUT);
pinMode(RELAY_COOL, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
// Güvenli başlangıç - tüm röleleri kapat
digitalWrite(RELAY_HEAT, HIGH);
digitalWrite(RELAY_HUMID, HIGH);
digitalWrite(RELAY_COOL, HIGH);
digitalWrite(MOTOR_PIN, HIGH);
analogWrite(FAN_PIN, 0);
// Başlangıç sesi
tone(BUZZER_PIN, 1000, 200);
delay(300);
tone(BUZZER_PIN, 1500, 200);
}
void ayarlariYukle() {
byte storedVersion = EEPROM.read(ADDR_VERSION);
if(storedVersion != PROFILE_VERSION) {
fabrikaAyarlari();
return;
}
// Ayarları yükle
EEPROM.get(ADDR_TUR, durum.aktifTur);
EEPROM.get(ADDR_TARIH, durum.baslangicTarihi);
EEPROM.get(ADDR_SON_DONUS, durum.sonDonus);
EEPROM.get(ADDR_ISTATISTIK, durum.istatistik);
// Profilleri yükle
for(int i=0; i<4; i++) {
EEPROM.get(ADDR_PROFIL + sizeof(TurProfili)*i, TURLAR[i]);
}
// Güvenlik kontrolü
durum.aktifTur = constrain(durum.aktifTur, 0, 3);
// Geçersiz tarih kontrolü
if(durum.baslangicTarihi == 0 || durum.baslangicTarihi == 0xFFFFFFFF) {
DateTime now = rtc.now();
durum.baslangicTarihi = now.unixtime();
}
if(durum.sonDonus == 0 || durum.sonDonus == 0xFFFFFFFF) {
durum.sonDonus = durum.baslangicTarihi;
}
}
void ayarlariKaydet() {
EEPROM.put(ADDR_TUR, durum.aktifTur);
EEPROM.put(ADDR_TARIH, durum.baslangicTarihi);
EEPROM.put(ADDR_SON_DONUS, durum.sonDonus);
EEPROM.put(ADDR_ISTATISTIK, durum.istatistik);
for(int i=0; i<4; i++) {
EEPROM.put(ADDR_PROFIL + sizeof(TurProfili)*i, TURLAR[i]);
}
}
Button tusOku() {
int joyX = analogRead(JOY_X);
int joyY = analogRead(JOY_Y);
bool sw = !digitalRead(JOY_SW);
// Joystick kalibrasyonu
int deltaX = joyX - JOY_CENTER_X;
int deltaY = joyY - JOY_CENTER_Y;
// Dead zone kontrolü
if(abs(deltaX) < JOY_DEADZONE) deltaX = 0;
if(abs(deltaY) < JOY_DEADZONE) deltaY = 0;
// Button mapping
if(sw) return BTN_SEC;
if(deltaX > JOY_THRESHOLD) return BTN_SAG;
if(deltaX < -JOY_THRESHOLD) return BTN_SOL;
if(deltaY > JOY_THRESHOLD) return BTN_ASAGI;
if(deltaY < -JOY_THRESHOLD) return BTN_YUKARI;
return BTN_YOK;
}
void menuIslem(Button btn) {
switch(durum.menu) {
case TUR_SEC:
turSecMenu(btn);
break;
case KULUCAKA_S:
sicaklikMenu(btn);
break;
case KULUCAKA_N:
nemMenu(btn);
break;
case TARIH:
tarihDuzenle(btn);
break;
case HATA_MENU:
hataMenu(btn);
break;
default:
durum.menu = ANA_EKRAN;
durum.lcdDirty = true;
break;
}
// Geri dönüş (SOL tuşu)
if(btn == BTN_SOL && durum.menu != ANA_EKRAN) {
if(durum.menu == HATA_MENU) {
durum.menu = ANA_EKRAN;
} else {
durum.menu = TUR_SEC;
}
durum.lcdDirty = true;
}
}
void turSecMenu(Button btn) {
if(btn == BTN_YUKARI) {
durum.aktifTur = (durum.aktifTur + 3) % 4;
ayarlariKaydet();
durum.lcdDirty = true;
} else if(btn == BTN_ASAGI) {
durum.aktifTur = (durum.aktifTur + 1) % 4;
ayarlariKaydet();
durum.lcdDirty = true;
} else if(btn == BTN_SAG) {
durum.menu = KULUCAKA_S;
durum.sicakSecim = 0;
durum.lcdDirty = true;
} else if(btn == BTN_SEC) {
durum.menu = TARIH;
durum.tarihSecimi = 0;
durum.lcdDirty = true;
}
}
void sicaklikMenu(Button btn) {
TurProfili &t = TURLAR[durum.aktifTur];
if(btn == BTN_SEC) {
durum.sicakSecim = (durum.sicakSecim + 1) % 2;
durum.lcdDirty = true;
} else if(btn == BTN_YUKARI) {
t.kuluSicak[durum.sicakSecim] += 0.1;
t.kuluSicak[durum.sicakSecim] = constrain(t.kuluSicak[durum.sicakSecim], MIN_SICAKLIK, MAX_SICAKLIK);
ayarlariKaydet();
durum.lcdDirty = true;
} else if(btn == BTN_ASAGI) {
t.kuluSicak[durum.sicakSecim] -= 0.1;
t.kuluSicak[durum.sicakSecim] = constrain(t.kuluSicak[durum.sicakSecim], MIN_SICAKLIK, MAX_SICAKLIK);
ayarlariKaydet();
durum.lcdDirty = true;
} else if(btn == BTN_SAG) {
durum.menu = KULUCAKA_N;
durum.nemSecim = 0;
durum.lcdDirty = true;
}
}
void nemMenu(Button btn) {
TurProfili &t = TURLAR[durum.aktifTur];
if(btn == BTN_SEC) {
durum.nemSecim = (durum.nemSecim + 1) % 2;
durum.lcdDirty = true;
} else if(btn == BTN_YUKARI) {
t.kuluNem[durum.nemSecim] += 1;
t.kuluNem[durum.nemSecim] = constrain(t.kuluNem[durum.nemSecim], 30, 95);
ayarlariKaydet();
durum.lcdDirty = true;
} else if(btn == BTN_ASAGI) {
t.kuluNem[durum.nemSecim] -= 1;
t.kuluNem[durum.nemSecim] = constrain(t.kuluNem[durum.nemSecim], 30, 95);
ayarlariKaydet();
durum.lcdDirty = true;
}
}
void hataMenu(Button btn) {
if(btn == BTN_YUKARI) {
durum.hataMenu = (HataMenuOgesi)((durum.hataMenu + 5) % 6);
durum.lcdDirty = true;
} else if(btn == BTN_ASAGI) {
durum.hataMenu = (HataMenuOgesi)((durum.hataMenu + 1) % 6);
durum.lcdDirty = true;
} else if(btn == BTN_SEC) {
if(durum.hataMenu == HATA_GENEL) {
fabrikaAyarlari();
}
durum.lcdDirty = true;
}
}
void ekranGuncelle() {
lcd.clear();
switch(durum.menu) {
case ANA_EKRAN:
anaEkranGoster();
break;
case TUR_SEC:
turSecEkran();
break;
case KULUCAKA_S:
sicaklikEkran();
break;
case KULUCAKA_N:
nemEkran();
break;
case TARIH:
tarihEkran();
break;
case HATA_MENU:
hataEkranGoster();
break;
}
}
void anaEkranGoster() {
TurProfili &t = TURLAR[durum.aktifTur];
// İlk satır: Tür ve gün bilgisi
lcd.setCursor(0,0);
lcd.print(t.isim);
lcd.print(" G:");
lcd.print(kuluckaGunuHesapla());
lcd.print("/");
lcd.print(t.kuluckaGun);
// Durum göstergeleri
if(durum.alarm) lcd.print("!");
if(durum.guvenliMod) lcd.print("S");
if(durum.donuyor) lcd.print("C");
// İkinci satır: Sıcaklık ve nem
lcd.setCursor(0,1);
lcd.print(sicaklik, 1);
lcd.print("C ");
lcd.print((int)nem);
lcd.print("%");
// Aktif cihaz göstergeleri
if(durum.isitici) lcd.print("H");
if(durum.nemlendirici) lcd.print("M");
if(durum.sogutma) lcd.print("F");
}
void turSecEkran() {
TurProfili &t = TURLAR[durum.aktifTur];
lcd.setCursor(0,0);
lcd.print("Tur Secimi:");
lcd.setCursor(0,1);
lcd.print(">");
lcd.print(t.isim);
lcd.print(" ");
lcd.print(t.kuluckaGun);
lcd.print("gun");
}
void sicaklikEkran() {
TurProfili &t = TURLAR[durum.aktifTur];
lcd.setCursor(0,0);
lcd.print("Sicaklik Ayari:");
lcd.setCursor(0,1);
for(int i=0; i<2; i++) {
if(i == durum.sicakSecim) lcd.print(">");
else lcd.print(" ");
lcd.print(t.kuluSicak[i], 1);
if(i < 1) lcd.print(" ");
}
lcd.print("C");
}
void nemEkran() {
TurProfili &t = TURLAR[durum.aktifTur];
lcd.setCursor(0,0);
lcd.print("Nem Ayari:");
lcd.setCursor(0,1);
for(int i=0; i<2; i++) {
if(i == durum.nemSecim) lcd.print(">");
else lcd.print(" ");
lcd.print((int)t.kuluNem[i]);
if(i < 1) lcd.print(" ");
}
lcd.print("%");
}
void tarihEkran() {
DateTime t = DateTime(durum.baslangicTarihi);
lcd.setCursor(0,0);
lcd.print("Baslangic Tarihi");
lcd.setCursor(0,1);
// Gün
if(durum.tarihSecimi == 0) lcd.print(">");
else lcd.print(" ");
if(t.day() < 10) lcd.print("0");
lcd.print(t.day());
lcd.print("/");
// Ay
if(durum.tarihSecimi == 1) lcd.print(">");
else lcd.print(" ");
if(t.month() < 10) lcd.print("0");
lcd.print(t.month());
lcd.print("/");
// Yıl
if(durum.tarihSecimi == 2) lcd.print(">");
else lcd.print(" ");
lcd.print(t.year());
}
void hataEkranGoster() {
const char* hataMenuler[] = {
"Genel Bilgi",
"Sensor Durum",
"Sicaklik Hata",
"Nem Hatasi",
"Sistem Log",
"Istatistikler"
};
lcd.setCursor(0,0);
lcd.print(hataMenuler[durum.hataMenu]);
lcd.setCursor(0,1);
switch(durum.hataMenu) {
case HATA_GENEL:
lcd.print("SEC:Fabrika Rst");
break;
case HATA_SENSOR:
lcd.print("S:");
lcd.print(durum.sicaklikHata);
lcd.print(" N:");
lcd.print(durum.nemHata);
if(durum.guvenliMod) lcd.print(" GM");
break;
case HATA_SICAKLIK:
lcd.print("T.Hata:");
lcd.print(durum.istatistik.toplamSicakHata);
break;
case HATA_NEM:
lcd.print("T.Hata:");
lcd.print(durum.istatistik.toplamNemHata);
break;
case HATA_SISTEM:
lcd.print("Acil:");
lcd.print(durum.istatistik.toplamAcilDurum);
break;
case HATA_ISTATISTIK:
lcd.print("Calisma:");
lcd.print(durum.istatistik.toplamCalismaZamani / 3600);
lcd.print("h");
break;
}
}
int kuluckaGunuHesapla() {
if(durum.baslangicTarihi == 0) return 0;
DateTime now = rtc.now();
uint32_t gecenSaniye = now.unixtime() - durum.baslangicTarihi;
int gecenGun = gecenSaniye / 86400;
return constrain(gecenGun + 1, 1, 99);
}
bool kuluckaDonemindeMi() {
if(durum.aktifTur >= 4) return false;
TurProfili &t = TURLAR[durum.aktifTur];
int mevcutGun = kuluckaGunuHesapla();
// Son 3 gün çevirme yapma
return (mevcutGun > 0 && mevcutGun <= (t.kuluckaGun - 3));
}det();
durum.lcdDirty = true;
// Çevirme bitiş sesi
tone(BUZZER_PIN, 800, 200);
}
}
}
void alarmKontrol(Button btn) {
if(durum.aktifTur >= 4) return; // Güvenlik kontrolü
TurProfili &t = TURLAR[durum.aktifTur];
// Alarm koşulları
bool sicakAlarm = (sicaklik < (t.kuluSicak[0] - 1.0)) ||
(sicaklik > (t.kuluSicak[1] + 1.0));
bool nemAlarm = (nem < (t.kuluNem[0] - 10)) ||
(nem > (t.kuluNem[1] + 10));
bool sensorAlarm = (durum.sicaklikHata > MAX_SENSOR_HATA) ||
(durum.nemHata > MAX_SENSOR_HATA);
bool yeniAlarm = sicakAlarm || nemAlarm || sensorAlarm || durum.guvenliMod;
if(yeniAlarm != durum.alarm) {
durum.alarm = yeniAlarm;
durum.lcdDirty = true;
}
if(!durum.alarm) {
noTone(BUZZER_PIN);
durum.snoozeTime = 0;
return;
}
// Snooze kontrolü
if(durum.snoozeTime && (millis() - durum.snoozeTime) < 600000) {
noTone(BUZZER_PIN);
return;
}
// Alarm susturma (ana ekranda SEC tuşu)
if(btn == BTN_SEC && durum.menu == ANA_EKRAN) {
snoozeAlarm();
return;
}
// Alarm sesi (non-blocking)
static unsigned long lastBeep = 0;
if(millis() - lastBeep > 2000) {
tone(BUZZER_PIN, 2000, 300);
lastBeep = millis();
}
}
void snoozeAlarm() {
durum.snoozeTime = millis();
noTone(BUZZER_PIN);
lcd.clear();
lcd.print("Alarm Susturldu");
lcd.setCursor(0,1);
lcd.print("10 dk bekle");
bekle(2000);
durum.lcdDirty = true;
}
void sistemSaglikKontrol() {
unsigned long calismaZamani = millis() - durum.sistemBaslangic;
durum.istatistik.toplamCalismaZamani = calismaZamani / 1000;
// Sensor timeout kontrolü
if(millis() - durum.sonSensorOkuma > SENSOR_TIMEOUT && !durum.guvenliMod) {
durum.guvenliMod = true;
sicaklik = sonGecerliSicaklik;
nem = sonGecerliNem;
durum.lcdDirty = true;
logKaydet(1, SENSOR_TIMEOUT/1000, 0);
}
// Ayarları periyodik kaydet
static unsigned long sonKayit = 0;
if(calismaZamani - sonKayit > 300000) { // Her 5 dakikada bir
ayarlariKaydet();
sonKayit = calismaZamani;
}
}
void tarihDuzenle(Button btn) {
if(btn == BTN_SEC) {
durum.tarihSecimi = (durum.tarihSecimi + 1) % 3;
durum.lcdDirty = true;
return;
}
DateTime t = DateTime(durum.baslangicTarihi);
int yil = t.year();
int ay = t.month();
int gun = t.day();
int artis = (btn == BTN_YUKARI) ? 1 : (btn == BTN_ASAGI) ? -1 : 0;
if(artis != 0) {
if(durum.tarihSecimi == 0) {
gun = constrain(gun + artis, 1, 31);
} else if(durum.tarihSecimi == 1) {
ay = constrain(ay + artis, 1, 12);
} else if(durum.tarihSecimi == 2) {
yil = constrain(yil + artis, 2020, 2030);
}
// Geçerli tarih oluştur
DateTime yeniTarih(yil, ay, gun);
durum.baslangicTarihi = yeniTarih.unixtime();
ayarlariKaydet();
durum.lcdDirty = true;
}
}
void fabrikaAyarlari() {
EEPROM.write(ADDR_VERSION, PROFILE_VERSION);
durum.aktifTur = 0;
DateTime now = rtc.now();
durum.baslangicTarihi = now.unixtime();
durum.sonDonus = now.unixtime();
durum.sicaklikHata = 0;
durum.nemHata = 0;
durum.guvenliMod = false;
durum.lcdDirty = true;
durum.tarihSecimi = 0;
durum.hataMenu = HATA_GENEL;
// İstatistikleri sıfırla
memset(&durum.istatistik, 0, sizeof(SistemIstatistik));
// Default profilleri geri yükle
TurProfili defaultTurlar[4] = {
{"Tavuk",21,{37.5,38.0},{55,65},3,5},
{"Hindi",28,{37.3,37.6},{60,70},4,7},
{"Bildircn",17,{37.7,38.0},{60,65},2,3},
{"Kaz",30,{37.3,37.6},{65,75},6,10}
};
for(int i=0; i<4; i++) {
TURLAR[i] = defaultTurlar[i];
EEPROM.put(ADDR_PROFIL + sizeof(TurProfili)*i, TURLAR[i]);
}
ayarlariKaydet(); // Eksik fonksiyon tamamlandı
lcd.clear();
lcd.print("Fabrika Ayarlari");
lcd.setCursor(0,1);
lcd.print("Yuklendi!");
bekle(2000);
durum.lcdDirty = true;
}