#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#include <TM1637Display.h>
#include <RTClib.h>
#include <EEPROM.h>
// تعريف منافذ المستشعرات والشاشات
#define DHT_PIN 2
#define DHT_TYPE DHT22
#define TEMP_CLK_PIN 3
#define TEMP_DIO_PIN 4
#define HUMID_CLK_PIN 5
#define HUMID_DIO_PIN 6
// تعريف منافذ المحركات والأجهزة
#define HEATER_PIN 7 // مقاومة التسخين
#define EXTRACTOR_PIN 8 // مروحة الاستخراج
#define HUMIDIFIER_PIN 9 // مرطب الموجات فوق الصوتية
#define HUMID_RESISTOR_PIN 10 // مقاومة الترطيب
#define MOTOR_LEFT_PIN 11 // محرك الدوران لليسار
#define MOTOR_RIGHT_PIN 12 // محرك الدوران لليمين
#define LIGHT_PIN 13 // مصباح الإضاءة
#define BUZZER_PIN A0 // buzzer
// تعريف منافذ الليدات
#define LED_HEATER_PIN A1 // LED أحمر للمقاومة
#define LED_EXTRACTOR_PIN A2 // LED أصفر للمروحة
#define LED_HUMIDIFIER_PIN A3 // LED أزرق للمرطب
#define LED_HUMID_RES_PIN A4 // LED بنفسجي لمقاومة الترطيب
#define LED_LEFT_PIN A5 // LED أخضر للدوران لليسار
#define LED_RIGHT_PIN A6 // LED برتقالي للدوران لليمين
// تعريف منافذ الأزرار
#define BTN_LIGHT_PIN 14 // زر الإضاءة
#define BTN_MODE_PIN 15 // زر الوضع
#define BTN_UP_PIN 16 // زر الأعلى
#define BTN_DOWN_PIN 17 // زر الأسفل
#define BTN_SAVE_PIN 18 // زر الحفظ/إيقاف الجرس
#define BTN_SELECT_PIN 19 // زر التحديد/التبديل بين الخانات
// تعريف منافذ switch نهاية الحركة
#define SWITCH_LEFT_PIN 20 // switch نهاية الحركة لليسار
#define SWITCH_RIGHT_PIN 21 // switch نهاية الحركة لليمين
// تهيئة الكائنات
LiquidCrystal_I2C lcd(0x27, 20, 4); // LCD I2C 20x4
DHT dht(DHT_PIN, DHT_TYPE);
TM1637Display tempDisplay(TEMP_CLK_PIN, TEMP_DIO_PIN);
TM1637Display humidDisplay(HUMID_CLK_PIN, HUMID_DIO_PIN);
RTC_DS1307 rtc;
// هيكل البيانات للحفظ في EEPROM
struct Settings {
float tempMin;
float tempMax;
float humidMin;
float humidMax;
float tempHysteresis;
float humidHysteresis;
int rotationInterval; // دقائق بين كل دورات
int rotationDuration; // ثواني مدة الدوران
int incubationDays;
float tempCorrection;
float humidCorrection;
};
Settings settings;
// المتغيرات العامة
int currentDay = 0;
bool incubationStarted = false;
DateTime incubationStartDate;
unsigned long lastRotationTime = 0;
bool motorRunning = false;
bool motorDirection = false; // false: left, true: right
int currentPage = 0;
bool settingMode = false;
int settingCursor = 0;
unsigned long lastSensorRead = 0;
unsigned long lastButtonCheck = 0;
bool lightOn = false;
bool lightContinuous = false;
unsigned long lightStartTime = 0;
const int LIGHT_DURATION = 30000; // 30 ثانية للإضاءة
// حالة الأجهزة
bool heaterOn = false;
bool extractorOn = false;
bool humidifierOn = false;
bool humidResistorOn = false;
// متغيرات للتحكم في الجرس
unsigned long lastBuzzerTime = 0;
bool buzzerActive = false;
bool incubationComplete = false;
unsigned long incubationCompleteTime = 0;
bool tempAlarm = false; // إنذار درجة الحرارة المرتفعة
bool humidAlarm = false; // إنذار الرطوبة المرتفعة
bool buzzerMuted = false; // كتم صوت الجرس
unsigned long buzzerMuteTime = 0;
const unsigned long BUZZER_MUTE_DURATION = 60000; // كتم الصوت لمدة دقيقة واحدة
// مصفوفة لتحديد عدد الخانات في كل صفحة
const int fieldsPerPage[8] = {2, 2, 2, 1, 1, 1, 2, 4}; // عدد الخانات في كل صفحة
void setup() {
// تهيئة الاتصال التسلسلي
Serial.begin(9600);
// تهيئة LCD
lcd.init();
lcd.backlight();
displayWelcome();
// تهيئة المستشعرات
dht.begin();
// تهيئة شاشات TM1637
tempDisplay.setBrightness(7);
humidDisplay.setBrightness(7);
// تهيئة RTC
if (!rtc.begin()) {
lcd.clear();
lcd.print("Erreur RTC!");
while(1);
}
if (!rtc.isrunning()) {
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
// تهيئة المنافذ
pinMode(HEATER_PIN, OUTPUT);
pinMode(EXTRACTOR_PIN, OUTPUT);
pinMode(HUMIDIFIER_PIN, OUTPUT);
pinMode(HUMID_RESISTOR_PIN, OUTPUT);
pinMode(MOTOR_LEFT_PIN, OUTPUT);
pinMode(MOTOR_RIGHT_PIN, OUTPUT);
pinMode(LIGHT_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(LED_HEATER_PIN, OUTPUT);
pinMode(LED_EXTRACTOR_PIN, OUTPUT);
pinMode(LED_HUMIDIFIER_PIN, OUTPUT);
pinMode(LED_HUMID_RES_PIN, OUTPUT);
pinMode(LED_LEFT_PIN, OUTPUT);
pinMode(LED_RIGHT_PIN, OUTPUT);
pinMode(BTN_LIGHT_PIN, INPUT_PULLUP);
pinMode(BTN_MODE_PIN, INPUT_PULLUP);
pinMode(BTN_UP_PIN, INPUT_PULLUP);
pinMode(BTN_DOWN_PIN, INPUT_PULLUP);
pinMode(BTN_SAVE_PIN, INPUT_PULLUP);
pinMode(BTN_SELECT_PIN, INPUT_PULLUP); // زر التحديد الجديد
pinMode(SWITCH_LEFT_PIN, INPUT_PULLUP);
pinMode(SWITCH_RIGHT_PIN, INPUT_PULLUP);
// تحميل الإعدادات من EEPROM
loadSettings();
// إيقاف جميع الأجهزة في البداية
digitalWrite(HEATER_PIN, LOW);
digitalWrite(EXTRACTOR_PIN, LOW);
digitalWrite(HUMIDIFIER_PIN, LOW);
digitalWrite(HUMID_RESISTOR_PIN, LOW);
digitalWrite(MOTOR_LEFT_PIN, LOW);
digitalWrite(MOTOR_RIGHT_PIN, LOW);
digitalWrite(LIGHT_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
delay(3000);
lcd.clear();
}
void loop() {
// قراءة المستشعرات كل ثانيتين
if (millis() - lastSensorRead >= 2000) {
readSensors();
controlEnvironment();
updateDisplays();
lastSensorRead = millis();
}
// التحقق من الأزرار كل 100 مللي ثانية
if (millis() - lastButtonCheck >= 100) {
checkButtons();
lastButtonCheck = millis();
}
// التحكم في الإضاءة
controlLight();
// التحكم في دوران المحرك
controlMotor();
// التحكم في الجرس
controlBuzzer();
// التحقق من اكتمال مدة التفقيس
checkIncubationCompletion();
// التحقق من إنذار درجة الحرارة والرطوبة
checkTemperatureAlarm();
checkHumidityAlarm();
// التحقق من انتهاء مدة كتم الصوت
checkBuzzerMute();
delay(50);
}
void displayWelcome() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Sghaier Group Trading");
lcd.setCursor(0, 1);
lcd.print("SKY");
lcd.setCursor(0, 2);
lcd.print("SKY Version 2.0");
lcd.setCursor(0, 3);
lcd.print("Tel: 98 240 843");
// تشغيل نغمة ترحيبية
tone(BUZZER_PIN, 1500, 200);
delay(250);
tone(BUZZER_PIN, 2000, 200);
}
void readSensors() {
float temp = dht.readTemperature();
float humid = dht.readHumidity();
if (!isnan(temp) && !isnan(humid)) {
// تطبيق التصحيح على القراءات
temp += settings.tempCorrection;
humid += settings.humidCorrection;
// عرض القيم على شاشات TM1637
tempDisplay.showNumberDecEx(temp * 10, 0x40, false, 3, 0);
humidDisplay.showNumberDec(humid, false, 2, 0);
}
}
void controlEnvironment() {
float temp = dht.readTemperature() + settings.tempCorrection;
float humid = dht.readHumidity() + settings.humidCorrection;
// التحكم في التسخين
if (temp < settings.tempMin - settings.tempHysteresis) {
digitalWrite(HEATER_PIN, HIGH);
heaterOn = true;
} else if (temp > settings.tempMax + settings.tempHysteresis) {
digitalWrite(HEATER_PIN, LOW);
heaterOn = false;
}
// التحكم في الرطوبة
if (humid < settings.humidMin - settings.humidHysteresis) {
digitalWrite(HUMIDIFIER_PIN, HIGH);
humidifierOn = true;
} else if (humid > settings.humidMax + settings.humidHysteresis) {
digitalWrite(HUMIDIFIER_PIN, LOW);
humidifierOn = false;
}
// التحكم في مروحة الاستخراج
if (temp > settings.tempMax + settings.tempHysteresis + 1.0) {
digitalWrite(EXTRACTOR_PIN, HIGH);
extractorOn = true;
} else if (temp < settings.tempMax) {
digitalWrite(EXTRACTOR_PIN, LOW);
extractorOn = false;
}
// التحكم في مقاومة الترطيب (تعمل فقط إذا كانت المروحة تعمل وخفضت الرطوبة)
if (extractorOn && humid < settings.humidMin - 5.0) {
digitalWrite(HUMID_RESISTOR_PIN, HIGH);
humidResistorOn = true;
} else {
digitalWrite(HUMID_RESISTOR_PIN, LOW);
humidResistorOn = false;
}
// تحديث حالة الليدات
digitalWrite(LED_HEATER_PIN, heaterOn);
digitalWrite(LED_EXTRACTOR_PIN, extractorOn);
digitalWrite(LED_HUMIDIFIER_PIN, humidifierOn);
digitalWrite(LED_HUMID_RES_PIN, humidResistorOn);
digitalWrite(LED_LEFT_PIN, motorRunning && !motorDirection);
digitalWrite(LED_RIGHT_PIN, motorRunning && motorDirection);
}
void updateDisplays() {
if (settingMode) {
displaySettingsPage();
} else {
displayMainPage();
}
}
void displayMainPage() {
float temp = dht.readTemperature() + settings.tempCorrection;
float humid = dht.readHumidity() + settings.humidCorrection;
lcd.clear();
// السطر الأول: درجة الحرارة
lcd.setCursor(0, 0);
lcd.print("T:");
lcd.print(temp, 1);
lcd.print("C (");
lcd.print(settings.tempMin, 1);
lcd.print("-");
lcd.print(settings.tempMax, 1);
lcd.print(")");
// السطر الثاني: الرطوبة
lcd.setCursor(0, 1);
lcd.print("H:");
lcd.print(humid, 1);
lcd.print("% (");
lcd.print(settings.humidMin);
lcd.print("-");
lcd.print(settings.humidMax);
lcd.print(")");
// السطر الثالث: الأيام
lcd.setCursor(0, 2);
lcd.print("Jours:");
lcd.print(currentDay);
lcd.print("/");
lcd.print(settings.incubationDays);
// السطر الرابع: حالة المحرك أو رسالة إنذار
lcd.setCursor(0, 3);
if (incubationComplete) {
lcd.print("ALARME: Terminee!");
} else if (tempAlarm) {
lcd.print("ALARME: Temp HIGH!");
} else if (humidAlarm) {
lcd.print("ALARME: Humid HIGH!");
} else if (buzzerMuted) {
lcd.print("Buzzer Mute: ");
int secondsLeft = (BUZZER_MUTE_DURATION - (millis() - buzzerMuteTime)) / 1000;
lcd.print(secondsLeft);
lcd.print("s");
} else {
lcd.print("Rotation:");
lcd.print(motorDirection ? "D" : "G");
lcd.print(" - ");
lcd.print(motorRunning ? "M" : "S");
}
}
void displaySettingsPage() {
lcd.clear();
switch (currentPage) {
case 0: // PROG-TEMPERATURE (Min/Max)
lcd.setCursor(0, 0);
lcd.print("PROG-TEMPERATURE");
lcd.setCursor(0, 2);
lcd.print("Max:");
lcd.print(settings.tempMax, 1);
lcd.setCursor(0, 3);
lcd.print("Min:");
lcd.print(settings.tempMin, 1);
// تحديد موضع المؤشر بشكل صحيح مع تمييز الخانة النشطة
if (settingCursor == 0) {
lcd.setCursor(0, 2);
lcd.print(">Max:");
lcd.setCursor(6, 2);
lcd.blink();
} else {
lcd.setCursor(0, 3);
lcd.print(">Min:");
lcd.setCursor(6, 3);
lcd.blink();
}
break;
case 1: // PROG-HUMIDITE (Min/Max)
lcd.setCursor(0, 0);
lcd.print("PROG-HUMIDITE");
lcd.setCursor(0, 2);
lcd.print("Max:");
lcd.print(settings.humidMax, 1);
lcd.setCursor(0, 3);
lcd.print("Min:");
lcd.print(settings.humidMin, 1);
if (settingCursor == 0) {
lcd.setCursor(0, 2);
lcd.print(">Max:");
lcd.setCursor(6, 2);
lcd.blink();
} else {
lcd.setCursor(0, 3);
lcd.print(">Min:");
lcd.setCursor(6, 3);
lcd.blink();
}
break;
case 2: // PROG-HYSTERESIS
lcd.setCursor(0, 0);
lcd.print("PROG-HYSTERESIS");
lcd.setCursor(0, 2);
lcd.print("Temp:");
lcd.print(settings.tempHysteresis, 1);
lcd.setCursor(0, 3);
lcd.print("Humid:");
lcd.print(settings.humidHysteresis, 1);
if (settingCursor == 0) {
lcd.setCursor(0, 2);
lcd.print(">Temp:");
lcd.setCursor(7, 2);
lcd.blink();
} else {
lcd.setCursor(0, 3);
lcd.print(">Humid:");
lcd.setCursor(8, 3);
lcd.blink();
}
break;
case 3: // PROG-TEMPERATURE (Correction)
lcd.setCursor(0, 0);
lcd.print("PROG-TEMPERATURE");
lcd.setCursor(0, 1);
lcd.print("CORRECTION");
lcd.setCursor(0, 3);
lcd.print(">Valeur:");
lcd.print(settings.tempCorrection, 1);
lcd.setCursor(9, 3);
lcd.blink();
break;
case 4: // PROG-HUMIDITE (Correction)
lcd.setCursor(0, 0);
lcd.print("PROG-HUMIDITE");
lcd.setCursor(0, 1);
lcd.print("CORRECTION");
lcd.setCursor(0, 3);
lcd.print(">Valeur:");
lcd.print(settings.humidCorrection, 1);
lcd.setCursor(9, 3);
lcd.blink();
break;
case 5: // PROG-JOURS
lcd.setCursor(0, 0);
lcd.print("PROG-JOURS");
lcd.setCursor(0, 2);
lcd.print(">Jours:");
lcd.print(settings.incubationDays);
lcd.setCursor(8, 2);
lcd.blink();
break;
case 6: // PROG-MOTEUR
lcd.setCursor(0, 0);
lcd.print("PROG-MOTEUR");
lcd.setCursor(0, 2);
lcd.print("Interval:");
lcd.print(settings.rotationInterval);
lcd.print("m");
lcd.setCursor(0, 3);
lcd.print("Duree:");
lcd.print(settings.rotationDuration);
lcd.print("s");
if (settingCursor == 0) {
lcd.setCursor(0, 2);
lcd.print(">Interval:");
lcd.setCursor(11, 2);
lcd.blink();
} else {
lcd.setCursor(0, 3);
lcd.print(">Duree:");
lcd.setCursor(8, 3);
lcd.blink();
}
break;
case 7: // PROG-HORLOGE
lcd.setCursor(0, 0);
lcd.print("PROG-HORLOGE");
DateTime now = rtc.now();
if (settingCursor == 0) {
lcd.setCursor(0, 2);
lcd.print(">Heure:");
lcd.print(now.hour());
lcd.print(":");
if (now.minute() < 10) lcd.print("0");
lcd.print(now.minute());
lcd.setCursor(8, 2);
lcd.blink();
} else if (settingCursor == 1) {
lcd.setCursor(0, 2);
lcd.print("Heure:");
lcd.print(now.hour());
lcd.print(":");
if (now.minute() < 10) lcd.print("0");
lcd.print(now.minute());
lcd.setCursor(0, 3);
lcd.print(">Date:");
lcd.print(now.day());
lcd.print("/");
lcd.print(now.month());
lcd.print("/");
lcd.print(now.year());
lcd.setCursor(6, 3);
lcd.blink();
} else if (settingCursor == 2) {
lcd.setCursor(0, 3);
lcd.print(">Date:");
lcd.print(now.day());
lcd.print("/");
lcd.print(now.month());
lcd.print("/");
lcd.print(now.year());
lcd.setCursor(7, 3);
lcd.blink();
} else {
lcd.setCursor(0, 3);
lcd.print(">Date:");
lcd.print(now.day());
lcd.print("/");
lcd.print(now.month());
lcd.print("/");
lcd.print(now.year());
lcd.setCursor(10, 3);
lcd.blink();
}
break;
}
}
void checkButtons() {
static unsigned long lastModePress = 0;
static unsigned long lastLightPress = 0;
static unsigned long lastUpPress = 0;
static unsigned long lastDownPress = 0;
static unsigned long lastSavePress = 0;
static unsigned long lastSelectPress = 0;
// زر الإضاءة
if (digitalRead(BTN_LIGHT_PIN) == LOW && millis() - lastLightPress > 300) {
// ضغطة قصيرة: تبديل الإضاءة
if (millis() - lastLightPress < 1000) {
lightOn = !lightOn;
lightContinuous = false;
lightStartTime = millis();
// نغمة عند تشغيل/إيقاف الإضاءة
tone(BUZZER_PIN, 1000, 100);
}
// ضغطة طويلة: إضاءة مستمرة
else {
lightContinuous = true;
lightOn = true;
// نغمة عند التبديل للإضاءة المستمرة
tone(BUZZER_PIN, 1200, 200);
}
digitalWrite(LIGHT_PIN, lightOn);
lastLightPress = millis();
}
// زر الوضع
if (digitalRead(BTN_MODE_PIN) == LOW && millis() - lastModePress > 300) {
// نغمة عند تغيير الوضع
tone(BUZZER_PIN, 800, 100);
if (settingMode) {
currentPage = (currentPage + 1) % 8;
settingCursor = 0; // إعادة تعيين المؤشر عند تغيير الصفحة
} else {
settingMode = true;
currentPage = 0;
settingCursor = 0;
}
lastModePress = millis();
}
// زر التحديد/التبديل بين الخانات
if (digitalRead(BTN_SELECT_PIN) == LOW && millis() - lastSelectPress > 300) {
if (settingMode) {
// التبديل بين الخانات داخل الصفحة الحالية
settingCursor = (settingCursor + 1) % fieldsPerPage[currentPage];
tone(BUZZER_PIN, 600, 50); // نغمة عند التبديل بين الخانات
}
lastSelectPress = millis();
}
// زر الأعلى
if (digitalRead(BTN_UP_PIN) == LOW && millis() - lastUpPress > 200) {
if (settingMode) {
adjustSetting(1);
tone(BUZZER_PIN, 500, 50);
} else {
// خارج وضع الإعدادات: تشغيل المحرك لليسار
startMotor(false);
// نغمة عند تشغيل المحرك
tone(BUZZER_PIN, 600, 100);
}
lastUpPress = millis();
}
// زر الأسفل
if (digitalRead(BTN_DOWN_PIN) == LOW && millis() - lastDownPress > 200) {
if (settingMode) {
adjustSetting(-1);
tone(BUZZER_PIN, 500, 50);
} else {
// خارج وضع الإعدادات: تشغيل المحرك لليمين
startMotor(true);
// نغمة عند تشغيل المحرك
tone(BUZZER_PIN, 600, 100);
}
lastDownPress = millis();
}
// زر الحفظ/إيقاف الجرس
if (digitalRead(BTN_SAVE_PIN) == LOW && millis() - lastSavePress > 300) {
if (settingMode) {
// في وضع الإعدادات: حفظ الخروج
// نغمة عند الضغط على زر الحفظ
tone(BUZZER_PIN, 700, 100);
saveSettings();
settingMode = false;
lcd.noBlink();
} else if (motorRunning) {
// خارج وضع الإعدادات والمحرك يعمل: إيقاف المحرك
// نغمة عند إيقاف المحرك
tone(BUZZER_PIN, 400, 100);
stopMotor();
} else if (buzzerActive) {
// خارج وضع الإعدادات والجرس يعمل: كتم صوت الجرس
muteBuzzer();
}
lastSavePress = millis();
}
}
void adjustSetting(int direction) {
DateTime now = rtc.now();
switch (currentPage) {
case 0: // درجة الحرارة Min/Max
if (settingCursor == 0) {
settings.tempMax += direction * 0.1;
settings.tempMax = constrain(settings.tempMax, 30.0, 40.0);
} else {
settings.tempMin += direction * 0.1;
settings.tempMin = constrain(settings.tempMin, 30.0, settings.tempMax);
}
break;
case 1: // الرطوبة Min/Max
if (settingCursor == 0) {
settings.humidMax += direction;
settings.humidMax = constrain(settings.humidMax, 40, 80);
} else {
settings.humidMin += direction;
settings.humidMin = constrain(settings.humidMin, 40, settings.humidMax);
}
break;
case 2: // الهيستيريسيس
if (settingCursor == 0) {
settings.tempHysteresis += direction * 0.1;
settings.tempHysteresis = constrain(settings.tempHysteresis, 0.1, 2.0);
} else {
settings.humidHysteresis += direction;
settings.humidHysteresis = constrain(settings.humidHysteresis, 1, 10);
}
break;
case 3: // تصحيح درجة الحرارة
settings.tempCorrection += direction * 0.1;
settings.tempCorrection = constrain(settings.tempCorrection, -5.0, 5.0);
break;
case 4: // تصحيح الرطوبة
settings.humidCorrection += direction;
settings.humidCorrection = constrain(settings.humidCorrection, -10, 10);
break;
case 5: // أيام التفقيس
settings.incubationDays += direction;
settings.incubationDays = constrain(settings.incubationDays, 1, 30);
break;
case 6: // إعدادات المحرك
if (settingCursor == 0) {
settings.rotationInterval += direction;
settings.rotationInterval = constrain(settings.rotationInterval, 1, 240);
} else {
settings.rotationDuration += direction;
settings.rotationDuration = constrain(settings.rotationDuration, 1, 60);
}
break;
case 7: // إعدادات الساعة
if (settingCursor == 0) {
// تعديل الساعة
int newHour = now.hour() + direction;
if (newHour < 0) newHour = 23;
if (newHour > 23) newHour = 0;
rtc.adjust(DateTime(now.year(), now.month(), now.day(), newHour, now.minute(), 0));
} else if (settingCursor == 1) {
// تعديل الدقائق
int newMinute = now.minute() + direction;
if (newMinute < 0) newMinute = 59;
if (newMinute > 59) newMinute = 0;
rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), newMinute, 0));
} else if (settingCursor == 2) {
// تعديل اليوم
int newDay = now.day() + direction;
if (newDay < 1) newDay = 31;
if (newDay > 31) newDay = 1;
rtc.adjust(DateTime(now.year(), now.month(), newDay, now.hour(), now.minute(), 0));
} else {
// تعديل الشهر
int newMonth = now.month() + direction;
if (newMonth < 1) newMonth = 12;
if (newMonth > 12) newMonth = 1;
rtc.adjust(DateTime(now.year(), newMonth, now.day(), now.hour(), now.minute(), 0));
}
break;
}
}
void controlLight() {
if (lightOn && !lightContinuous && millis() - lightStartTime > LIGHT_DURATION) {
lightOn = false;
digitalWrite(LIGHT_PIN, LOW);
tone(BUZZER_PIN, 900, 100);
}
}
// ===== دوال الإنذار المحسنة =====
void checkTemperatureAlarm() {
float temp = dht.readTemperature() + settings.tempCorrection;
// تفعيل إنذار درجة الحرارة إذا تجاوزت الحد الأقصى أو انخفضت عن الحد الأدنى
if ((temp >= settings.tempMax + settings.tempHysteresis + 2.0 ||
temp <= settings.tempMin - settings.tempHysteresis - 2.0) && !tempAlarm) {
tempAlarm = true;
buzzerActive = true;
lastBuzzerTime = millis();
}
// إيقاف إنذار درجة الحرارة إذا عادت إلى النطاق الطبيعي
else if (temp < settings.tempMax + settings.tempHysteresis &&
temp > settings.tempMin - settings.tempHysteresis && tempAlarm) {
tempAlarm = false;
buzzerActive = false;
noTone(BUZZER_PIN);
}
}
void checkHumidityAlarm() {
float humid = dht.readHumidity() + settings.humidCorrection;
// تفعيل إنذار الرطوبة إذا تجاوزت الحد الأقصى أو انخفضت عن الحد الأدنى
if ((humid >= settings.humidMax + settings.humidHysteresis + 10.0 ||
humid <= settings.humidMin - settings.humidHysteresis - 10.0) && !humidAlarm) {
humidAlarm = true;
buzzerActive = true;
lastBuzzerTime = millis();
}
// إيقاف إنذار الرطوبة إذا عادت إلى النطاق الطبيعي
else if (humid < settings.humidMax + settings.humidHysteresis &&
humid > settings.humidMin - settings.humidHysteresis && humidAlarm) {
humidAlarm = false;
buzzerActive = false;
noTone(BUZZER_PIN);
}
}
void controlBuzzer() {
// إذا كان الجرس مكتوماً، لا تفعل شيئاً
if (buzzerMuted) {
noTone(BUZZER_PIN);
return;
}
// التحكم في إنذار اكتمال التفقيس
if (incubationComplete) {
if (millis() - lastBuzzerTime > 1000) {
// صوت إنذار متقطع للتكتمل
if (buzzerActive) {
noTone(BUZZER_PIN);
buzzerActive = false;
} else {
tone(BUZZER_PIN, 1000);
buzzerActive = true;
}
lastBuzzerTime = millis();
}
return;
}
// التحكم في إنذار درجة الحرارة المرتفعة
if (tempAlarm) {
float temp = dht.readTemperature() + settings.tempCorrection;
if (temp >= settings.tempMax + settings.tempHysteresis + 2.0) {
// صوت إنذار سريع لدرجة الحرارة المرتفعة
if (millis() - lastBuzzerTime > 300) {
if (buzzerActive) {
noTone(BUZZER_PIN);
buzzerActive = false;
} else {
tone(BUZZER_PIN, 1200);
buzzerActive = true;
}
lastBuzzerTime = millis();
}
} else if (temp <= settings.tempMin - settings.tempHysteresis - 2.0) {
// صوت إنذار مختلف لدرجة الحرارة المنخفضة
if (millis() - lastBuzzerTime > 500) {
if (buzzerActive) {
noTone(BUZZER_PIN);
buzzerActive = false;
} else {
tone(BUZZER_PIN, 800);
buzzerActive = true;
}
lastBuzzerTime = millis();
}
}
return;
}
// التحكم in إنذار الرطوبة المرتفعة
if (humidAlarm) {
float humid = dht.readHumidity() + settings.humidCorrection;
if (humid >= settings.humidMax + settings.humidHysteresis + 10.0) {
// صوت إنذار للرطوبة المرتفعة
if (millis() - lastBuzzerTime > 400) {
if (buzzerActive) {
noTone(BUZZER_PIN);
buzzerActive = false;
} else {
tone(BUZZER_PIN, 900);
buzzerActive = true;
}
lastBuzzerTime = millis();
}
} else if (humid <= settings.humidMin - settings.humidHysteresis - 10.0) {
// صوت إنذار للرطوبة المنخفضة
if (millis() - lastBuzzerTime > 600) {
if (buzzerActive) {
noTone(BUZZER_PIN);
buzzerActive = false;
} else {
tone(BUZZER_PIN, 700);
buzzerActive = true;
}
lastBuzzerTime = millis();
}
}
return;
}
// إيقاف الجرس إذا كان يعمل ولم يكن هناك إنذар
if (buzzerActive) {
noTone(BUZZER_PIN);
buzzerActive = false;
}
}
void checkBuzzerMute() {
if (buzzerMuted && millis() - buzzerMuteTime > BUZZER_MUTE_DURATION) {
buzzerMuted = false;
}
}
void muteBuzzer() {
buzzerMuted = true;
buzzerMuteTime = millis();
buzzerActive = false;
noTone(BUZZER_PIN);
tone(BUZZER_PIN, 600, 100);
delay(100);
tone(BUZZER_PIN, 400, 100);
}
void startIncubation() {
incubationStarted = true;
incubationStartDate = rtc.now();
currentDay = 0;
incubationComplete = false;
lcd.clear();
lcd.print("Incubation started!");
lcd.setCursor(0, 1);
lcd.print("Date: ");
lcd.print(incubationStartDate.day());
lcd.print("/");
lcd.print(incubationStartDate.month());
lcd.print("/");
lcd.print(incubationStartDate.year());
tone(BUZZER_PIN, 1500, 300);
delay(300);
tone(BUZZER_PIN, 2000, 300);
delay(2000);
lcd.clear();
}
void controlMotor() {
if (autoRotationEnabled && incubationStarted && !motorRunning &&
millis() - lastRotationTime > settings.rotationInterval * 60000) {
startMotor(!motorDirection);
tone(BUZZER_PIN, 600, 100);
}
if (motorRunning && millis() - lastRotationTime > settings.rotationDuration * 1000) {
stopMotor();
tone(BUZZER_PIN, 400, 100);
}
if (motorRunning) {
if (!motorDirection && digitalRead(SWITCH_LEFT_PIN) == LOW) {
stopMotor();
tone(BUZZER_PIN, 300, 200);
} else if (motorDirection && digitalRead(SWITCH_RIGHT_PIN) == LOW) {
stopMotor();
tone(BUZZER_PIN, 300, 200);
}
}
}
void startMotor(bool direction) {
if (motorRunning) return;
motorRunning = true;
motorDirection = direction;
lastRotationTime = millis();
if (direction) {
digitalWrite(MOTOR_RIGHT_PIN, HIGH);
digitalWrite(LED_RIGHT_PIN, HIGH);
} else {
digitalWrite(MOTOR_LEFT_PIN, HIGH);
digitalWrite(LED_LEFT_PIN, HIGH);
}
}
void stopMotor() {
motorRunning = false;
digitalWrite(MOTOR_LEFT_PIN, LOW);
digitalWrite(MOTOR_RIGHT_PIN, LOW);
digitalWrite(LED_LEFT_PIN, LOW);
digitalWrite(LED_RIGHT_PIN, LOW);
}
void checkIncubationCompletion() {
if (incubationStarted && !incubationComplete) {
DateTime now = rtc.now();
unsigned long elapsedSeconds = now.unixtime() - incubationStartDate.unixtime();
currentDay = (elapsedSeconds / 86400) + 1;
if (currentDay >= settings.incubationDays) {
incubationComplete = true;
incubationCompleteTime = millis();
lcd.clear();
lcd.print("FELICITATIONS!");
lcd.setCursor(0, 1);
lcd.print("Incubation terminee");
buzzerActive = true;
lastBuzzerTime = millis();
}
}
}
void loadSettings() {
EEPROM.get(0, settings);
if (isnan(settings.tempMin)) {
settings.tempMin = 37.0;
settings.tempMax = 37.5;
settings.humidMin = 50;
settings.humidMax = 55;
settings.tempHysteresis = 0.5;
settings.humidHysteresis = 5;
settings.rotationInterval = 60;
settings.rotationDuration = 10;
settings.incubationDays = 21;
settings.tempCorrection = 0.0;
settings.humidCorrection = 0.0;
}
}
void saveSettings() {
EEPROM.put(0, settings);
tone(BUZZER_PIN, 1500, 200);
delay(100);
tone(BUZZER_PIN, 2000, 200);
}heater
extract
disque
resistance
gauche
droite
heater
extract
disque
resistance
gauche
droite
led
led
mode
up
down
save