#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <RTClib.h>
#include <TM1637Display.h>
#include <DHT.h>
int lastLdrValue = 0;
int nightWakeCount = 0; // تعداد بیدار شدن در شب
bool nightMovementActive = false; // برای جلوگیری از شمارش تکراری
int lastNightDay = -1; // برای ریست شب جدید
bool patientAwakeAtNight = false;
unsigned long awakeDetectTimer = 0;
#define LDR_JUMP_THRESHOLD 500 // جهش ناگهانی نور
#define AWAKE_HOLD_TIME 10000UL // 10 ثانیه نمایش هشدار
#define BUZZER_PIN 15
bool buzzerOn = false;
#define BUZZER_CHANNEL 0
#define BUZZER_FREQ 2000
#define BUZZER_RES 8
#define BTN_SILENCE 13
#define LED_ENV 32
#define LDR_PIN 35 // فقط ADC
int ldrValue = 0;
bool isNight = false;
#define LDR_PIN 35 // پین LDR
#define LDR_NIGHT_THRESHOLD 1500 // آستانه تشخیص شب
#define MQ2_PIN 34
#define MQ2_THRESHOLD 300 // مقدار آستانه تشخیص گاز (0 تا 4095)
bool gasAlert = false;
#define GAS_BUZZER_ON_TIME 200 // مدت زمان روشن بودن صدا (ms)
#define GAS_BUZZER_OFF_TIME 200 // مدت زمان خاموش بودن صدا (ms)
enum BuzzerMode { BUZZER_OFF, BUZZER_FALL, BUZZER_GAS };
BuzzerMode buzzerMode = BUZZER_OFF;
unsigned long buzzerTimer = 0;
bool buzzerState = false;
// برای گاز
#define GAS_BUZZER_ON_TIME 200
#define GAS_BUZZER_OFF_TIME 200
unsigned long silencePressTime = 0;
#define DHT_PIN 16
#define DHT_TYPE DHT22
DHT dht(DHT_PIN, DHT_TYPE);
#define TEMP_MIN 20
#define TEMP_MAX 26
#define HUM_MIN 30
#define HUM_MAX 60
float roomTemp = 0;
float roomHum = 0;
unsigned long lastDHTRead = 0;
#define TRIG_PIN 5
#define ECHO_PIN 17
DateTime lastMotionRTC;
bool hasMotionRecord = false;
int dailyMotionCount = 0; // تعداد حرکات ثبت شده در طول روز
int lastMotionDay = -1; // روز آخرین ریست شمارش
// ===== Seven Segment =====
#define SEG_CLK 25
#define SEG_DIO 26
TM1637Display segment(SEG_CLK, SEG_DIO);
// ===== LCD =====
LiquidCrystal_I2C lcd(0x27, 16, 2);
// ===== OLED =====
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
TwoWire I2C_2 = TwoWire(1);
Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &I2C_2, -1);
// ===== RTC =====
RTC_DS3231 rtc;
// ===== Pins =====
#define LED_RED 2
#define LED_YELLOW 27
#define LED_BLUE 4
#define BTN_MED 12
#define BTN_MODE 14
#define POT_PIN 36
#define PIR_PIN 33
// ===== Constants =====
#define MAX_DAYS_REAL 30
#define MAX_DAYS_TEST 5
int dailySugar[MAX_DAYS_REAL];
int dayIndex = 0;
int daysPassed = 0;
bool needInsulin = false;
unsigned long lastDayTimer = 0;
// ===== Medicine =====
int medHours[3] = {8, 14, 21};
bool medTaken[3] = {false, false, false};
int currentMed = 0;
int medPressCount = 0; // برای TEST
// ===== States =====
enum MedState { COUNTDOWN, YELLOW_ON, RED_ALERT };
MedState medState = COUNTDOWN;
// ===== Variables =====
bool mode = 0; // 0 REAL | 1 TEST
unsigned long timerState = 0;
unsigned long blinkTimer = 0;
bool blinkState = false;
int sugar = 0;
// ===== Activity Monitoring =====
unsigned long lastMotionTime = 0;
bool lowActivityAlert = false;
unsigned long lastMotionDetected = 0;
unsigned long lastMotionGap = 0;
#define ACTIVITY_LIMIT_REAL 1800000UL // 30 دقیقه
#define ACTIVITY_LIMIT_TEST 30000UL // 30 ثانیه
#define MIN_VALID_GAP 5000UL // 5 دقیقه
// ===== Button =====
bool buttonPressed(int pin) {
static unsigned long lastPress = 0;
if (!digitalRead(pin) && millis() - lastPress > 300) {
lastPress = millis();
return true;
}
return false;
}
// ===== Next medicine =====
DateTime nextMedicine(DateTime now) {
for (int i = 0; i < 3; i++) {
if (!medTaken[i] && now.hour() < medHours[i]) {
currentMed = i;
return DateTime(now.year(), now.month(), now.day(), medHours[i], 0, 0);
}
}
currentMed = 0;
return DateTime(now.year(), now.month(), now.day() + 1, 8, 0, 0);
}
// ===== Average & Insulin =====
float calcWeightedAverage(int days) {
if (dayIndex == 0) return 0;
float sum = 0;
float weightSum = 0;
for (int i = 0; i < days; i++) {
int idx = (dayIndex - i - 1 + MAX_DAYS_REAL) % MAX_DAYS_REAL;
float weight = (i + 1);
sum += dailySugar[idx] * weight;
weightSum += weight;
}
return sum / weightSum;
}
float highSugarPercent(int days) {
int high = 0;
for (int i = 0; i < days; i++) {
int idx = (dayIndex - i - 1 + MAX_DAYS_REAL) % MAX_DAYS_REAL;
if (dailySugar[idx] > 180) high++;
}
return (high * 100.0) / days;
}
float lowSugarPercent(int days) {
int low = 0;
for (int i = 0; i < days; i++) {
int idx = (dayIndex - i - 1 + MAX_DAYS_REAL) % MAX_DAYS_REAL;
if (dailySugar[idx] < 70) low++;
}
return (low * 100.0) / days;
}
long lastDistance = 0;
long currentDistance = 0;
unsigned long fallTimer = 0;
bool fallDetected = false;
#define BED_DISTANCE_MIN 30
#define BED_DISTANCE_MAX 70
#define FALL_DISTANCE 110 // فاصله بعد از سقوط
#define FALL_CONFIRM_MS 3000 // 3 ثانیه
long readUltrasonic() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH, 30000); // timeout 30ms
if (duration == 0) return -1;
return duration * 0.034 / 2;
}
// ===== SETUP =====
void setup() {
pinMode(LED_RED, OUTPUT);
pinMode(LED_YELLOW, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
pinMode(BTN_MED, INPUT_PULLUP);
pinMode(BTN_MODE, INPUT_PULLUP);
pinMode(PIR_PIN, INPUT);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW);
pinMode(BTN_SILENCE, INPUT_PULLUP);
pinMode(LED_ENV, OUTPUT);
digitalWrite(LED_ENV, LOW); // اول خاموش باشه
ledcAttach(BUZZER_PIN, BUZZER_FREQ, BUZZER_RES);
ledcWrite(BUZZER_PIN, 0);
Wire.begin(21, 22);
lcd.init();
lcd.backlight();
I2C_2.begin(19, 18);
oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);
oled.setTextSize(1);
oled.setTextColor(WHITE);
dht.begin();
rtc.begin();
segment.setBrightness(0x0f);
timerState = millis();
}
// ===== LOOP =====
// void loop() {
// bool coldAlert = false; // هشدار دمای پایین
// bool heatAlert = false; // هشدار دمای بالا
// bool humAlert = false; // هشدار رطوبت
// DateTime now = rtc.now();
// // ===== Reset daily motion count =====
// if (now.day() != lastMotionDay) {
// lastMotionDay = now.day();
// dailyMotionCount = 0;
// }
// if (millis() - lastDHTRead > 2000) {
// lastDHTRead = millis();
// float t = dht.readTemperature(); // °C
// float h = dht.readHumidity(); // %
// if (!isnan(t) && !isnan(h)) {
// roomTemp = t;
// roomHum = h;
// }
// }
// bool envAlert = false;
// if (roomTemp < TEMP_MIN || roomTemp > TEMP_MAX ||
// roomHum < HUM_MIN || roomHum > HUM_MAX) {
// envAlert = true;
// }
// // ریست همه هشدارها
// coldAlert = false;
// heatAlert = false;
// humAlert = false;
// // بررسی دمای پایین و بالا
// if (roomTemp < TEMP_MIN) {
// coldAlert = true; // سرمازدگی
// }
// if (roomTemp > TEMP_MAX) {
// heatAlert = true; // گرمازدگی
// }
// // بررسی رطوبت
// if (roomHum < HUM_MIN || roomHum > HUM_MAX) {
// humAlert = true;
// }
// // روشن/خاموش کردن LED هشدار محیط
// digitalWrite(LED_ENV, envAlert ? HIGH : LOW);
// if (gasAlert) {
// unsigned long nowMillis = millis();
// // آژیر متناوب با فرکانس شبیه ماشین آتشنشانی
// if (gasBuzzerState) {
// // صدا روشن است
// if (nowMillis - gasBuzzerTimer >= GAS_BUZZER_ON_TIME) {
// gasBuzzerTimer = nowMillis;
// gasBuzzerState = false;
// ledcWrite(BUZZER_PIN, 0); // خاموش کردن صدا
// }
// } else {
// // صدا خاموش است
// if (nowMillis - gasBuzzerTimer >= GAS_BUZZER_OFF_TIME) {
// gasBuzzerTimer = nowMillis;
// gasBuzzerState = true;
// // فرکانس های متفاوت برای شبیهسازی آژیر
// static bool toggleFreq = false;
// toggleFreq = !toggleFreq;
// int freq = toggleFreq ? 2000 : 2500; // فرکانس بین 2kHz و 2.5kHz
// ledcWriteTone(BUZZER_CHANNEL, freq);
// }
// }
// } else {
// // خاموش کردن بازر اگر گاز پایین باشد
// ledcWrite(BUZZER_PIN, 0);
// gasBuzzerState = false;
// }
// // ===== PIR Activity Check =====
// if (digitalRead(PIR_PIN) == HIGH) {
// unsigned long nowTime = millis();
// lastMotionRTC = rtc.now();
// hasMotionRecord = true;
// unsigned long gap = nowTime - lastMotionDetected;
// if (gap > MIN_VALID_GAP) {
// dailyMotionCount++;
// }
// lastMotionDetected = nowTime;
// lastMotionTime = nowTime;
// lowActivityAlert = false;
// }
// // ===== Mode Toggle =====
// if (buttonPressed(BTN_MODE)) {
// mode = !mode;
// medState = COUNTDOWN;
// timerState = millis();
// medPressCount = 0;
// }
// // ===== Read Sugar =====
// sugar = map(analogRead(POT_PIN), 0, 4095, 40, 300);
// digitalWrite(LED_BLUE, (sugar < 70 || sugar > 180));
// // ===== Medicine Taken =====
// if (buttonPressed(BTN_MED)) {
// medTaken[currentMed] = true;
// medState = COUNTDOWN;
// timerState = millis();
// digitalWrite(LED_RED, LOW);
// digitalWrite(LED_YELLOW, LOW);
// if (mode) {
// medPressCount++;
// if (medPressCount == 3) {
// medPressCount = 0;
// dailySugar[dayIndex++] = sugar;
// daysPassed++;
// if (dayIndex >= MAX_DAYS_TEST) dayIndex = 0;
// if (daysPassed >= MAX_DAYS_TEST) {
// float avg = calcWeightedAverage(MAX_DAYS_TEST);
// float highPercent = highSugarPercent(MAX_DAYS_TEST);
// float lowPercent = lowSugarPercent(MAX_DAYS_TEST);
// needInsulin = (avg > 160) || (highPercent > 30.0) || (lowPercent > 20.0);
// }
// }
// }
// }
// // ===== Reset REAL daily =====
// if (!mode && now.hour() == 0 && now.minute() == 0) {
// for (int i = 0; i < 3; i++) medTaken[i] = false;
// }
// // ===== STATE MACHINE =====
// switch (medState) {
// case COUNTDOWN:
// digitalWrite(LED_RED, LOW);
// digitalWrite(LED_YELLOW, LOW);
// if (mode) {
// int t = 10 - (millis() - timerState) / 1000;
// if (t < 0) t = 0;
// segment.showNumberDec(t, true);
// if (t == 0) {
// medState = YELLOW_ON;
// timerState = millis();
// }
// } else {
// DateTime nextMed = nextMedicine(now);
// long diff = nextMed.unixtime() - now.unixtime();
// if (diff <= 0) {
// medState = YELLOW_ON;
// timerState = millis();
// } else {
// int h = diff / 3600;
// int m = (diff % 3600) / 60;
// segment.showNumberDecEx(h * 100 + m, 0b01000000, true);
// }
// }
// break;
// case YELLOW_ON:
// digitalWrite(LED_YELLOW, HIGH);
// if (millis() - timerState >= 5000) {
// digitalWrite(LED_YELLOW, LOW);
// medState = RED_ALERT;
// }
// break;
// case RED_ALERT:
// if (millis() - blinkTimer > 500) {
// blinkState = !blinkState;
// blinkTimer = millis();
// }
// digitalWrite(LED_RED, blinkState);
// break;
// }
// // ===== Daily Timer =====
// unsigned long dayInterval = mode ? 10000UL : 86400000UL;
// if (millis() - lastDayTimer > dayInterval) {
// lastDayTimer = millis();
// if (!mode) {
// dailySugar[dayIndex] = sugar;
// dayIndex = (dayIndex + 1) % MAX_DAYS_REAL;
// daysPassed++;
// }
// }
// // ===== Ultrasonic Fall Detection =====
// currentDistance = readUltrasonic();
// if (currentDistance > 0) {
// // اگر فاصله در محدوده تخت است → ذخیره کن
// if (currentDistance >= BED_DISTANCE_MIN && currentDistance <= BED_DISTANCE_MAX) {
// lastDistance = currentDistance;
// fallTimer = 0;
// fallDetected = false;
// }
// // اگر فاصله ناگهانی زیاد شد (احتمال سقوط)
// else if (currentDistance > FALL_DISTANCE &&
// lastDistance >= BED_DISTANCE_MIN &&
// lastDistance <= BED_DISTANCE_MAX) {
// if (fallTimer == 0) {
// fallTimer = millis();
// }
// if (millis() - fallTimer >= FALL_CONFIRM_MS) {
// fallDetected = true;
// }
// }
// // اگر فاصله برگشت به حالت عادی
// else {
// fallTimer = 0;
// }
// }
// // ===== Buzzer =====
// if (fallDetected) {
// if (millis() - buzzerTimer > 300) {
// buzzerTimer = millis();
// buzzerOn = !buzzerOn;
// if (buzzerOn)
// ledcWrite(BUZZER_PIN, 128); // صدا
// else
// ledcWrite(BUZZER_PIN, 0); // قطع
// }
// } else {
// ledcWrite(BUZZER_PIN, 0);
// buzzerOn = false;
// }
// if (gasAlert) {
// // فعال کردن بازر
// ledcWrite(BUZZER_PIN, 128); // صدا
// } else {
// // خاموش کردن بازر
// ledcWrite(BUZZER_PIN, 0);
// }
// // ===== Silence Button =====
// if (fallDetected) {
// if (digitalRead(BTN_SILENCE) == LOW) { // دکمه نگه داشته شده
// if (silencePressTime == 0) {
// silencePressTime = millis();
// }
// if (millis() - silencePressTime >= 1000) { // 2 ثانیه
// fallDetected = false;
// ledcWrite(BUZZER_PIN, 0);
// buzzerOn = false;
// silencePressTime = 0;
// }
// } else {
// silencePressTime = 0;
// }
// }
// // ===== LCD (Patient) =====
// lcd.clear();
// lcd.setCursor(0, 0);
// lcd.print("BS:");
// lcd.print(sugar);
// lcd.print(sugar > 180 ? " H" : sugar < 70 ? " L" : " OK");
// lcd.setCursor(10, 0);
// if (now.hour() < 10) lcd.print("0");
// lcd.print(now.hour());
// lcd.print(":");
// if (now.minute() < 10) lcd.print("0");
// lcd.print(now.minute());
// lcd.setCursor(0, 1);
// if (needInsulin)
// lcd.print("Need Insulin!");
// else if (medState == YELLOW_ON)
// lcd.print("Take Medicine");
// else if (medState == RED_ALERT)
// lcd.print("Medicine Miss!");
// else
// lcd.print("Waiting...");
// if (lowActivityAlert)
// lcd.print("Please Move :) ");
// else
// lcd.print("Activity OK ");
// // ===== OLED (Doctor) =====
// oled.clearDisplay();
// oled.setCursor(0, 0);
// // oled.println("Doctor Monitor");
// if (now.hour() < 10)
// oled.print("0");
// oled.print(now.hour());
// oled.print(":");
// if (now.minute() < 10)
// oled.print("0");
// oled.print(now.minute());
// oled.print("Mode: ");
// oled.println(mode ? "TEST" : "REAL");
// // oled.print("Sugar: ");
// // oled.println(sugar);
// oled.print("Med State: ");
// oled.println(
// medState == COUNTDOWN ? "WAIT" :
// medState == YELLOW_ON ? "DUE" : "OVERDUE"
// );
// oled.print("Day: ");
// oled.print(daysPassed);
// oled.print(" / ");
// oled.println(mode ? MAX_DAYS_TEST : MAX_DAYS_REAL);
// oled.print("Avg: ");
// oled.println(daysPassed < (mode ? MAX_DAYS_TEST : MAX_DAYS_REAL) ? 0 : (int)calcWeightedAverage(mode ? MAX_DAYS_TEST : MAX_DAYS_REAL));
// // oled.print("Insulin: ");
// // oled.println(needInsulin ? "REQUIRED" : "NO");
// // oled.print("Activity: ");
// // oled.println(lowActivityAlert ? "LOW" : "NORMAL");
// // oled.print("Last Move: ");
// // if (hasMotionRecord) {
// // if (lastMotionRTC.hour() < 10) oled.print("0");
// // oled.print(lastMotionRTC.hour());
// // oled.print(":");
// // if (lastMotionRTC.minute() < 10) oled.print("0");
// // oled.println(lastMotionRTC.minute());
// // oled.print("moves today:");
// // oled.print(dailyMotionCount);
// // } else {
// // oled.println("--:--");
// // }
// // oled.print("Distance: ");
// // oled.print(currentDistance);
// // oled.println(" cm");
// // oled.print("Fall: ");
// // oled.println(fallDetected ? "YES" : "NO");
// // oled.print("Temp: ");
// // oled.print(roomTemp, 1);
// // oled.println(" C");
// // oled.print("Hum: ");
// // oled.print(roomHum, 0);
// // oled.println(" %");
// // if (coldAlert) oled.println("COLD ALERT"); // سرمازدگی
// // else if (heatAlert) oled.println("HEAT ALERT"); // گرمازدگی
// // else oled.println("OK"); // دما نرمال
// oled.print("Gas: ");
// if (gasAlert) oled.println("ALERT!");
// else oled.println("OK");
// oled.display();
// delay(200);
// }
void loop() {
bool coldAlert = false; // هشدار دمای پایین
bool heatAlert = false; // هشدار دمای بالا
bool humAlert = false; // هشدار رطوبت
DateTime now = rtc.now();
// ===== Reset daily motion count =====
if (now.day() != lastMotionDay) {
lastMotionDay = now.day();
dailyMotionCount = 0;
}
// ===== Read DHT22 =====
if (millis() - lastDHTRead > 2000) {
lastDHTRead = millis();
float t = dht.readTemperature(); // °C
float h = dht.readHumidity(); // %
if (!isnan(t) && !isnan(h)) {
roomTemp = t;
roomHum = h;
}
}
bool envAlert = false;
if (roomTemp < TEMP_MIN || roomTemp > TEMP_MAX ||
roomHum < HUM_MIN || roomHum > HUM_MAX) {
envAlert = true;
}
// بررسی دمای پایین و بالا
if (roomTemp < TEMP_MIN) coldAlert = true;
if (roomTemp > TEMP_MAX) heatAlert = true;
// بررسی رطوبت
if (roomHum < HUM_MIN || roomHum > HUM_MAX) humAlert = true;
// روشن/خاموش کردن LED هشدار محیط
digitalWrite(LED_ENV, envAlert ? HIGH : LOW);
// ===== Read MQ2 =====
int mq2Value = analogRead(MQ2_PIN);
if (mq2Value > MQ2_THRESHOLD) gasAlert = true;
else gasAlert = false;
// ===== PIR Activity Check =====
if (digitalRead(PIR_PIN) == HIGH) {
unsigned long nowTime = millis();
lastMotionRTC = rtc.now();
hasMotionRecord = true;
unsigned long gap = nowTime - lastMotionDetected;
if (gap > MIN_VALID_GAP) dailyMotionCount++;
lastMotionDetected = nowTime;
lastMotionTime = nowTime;
lowActivityAlert = false;
}
// ===== Mode Toggle =====
if (buttonPressed(BTN_MODE)) {
mode = !mode;
medState = COUNTDOWN;
timerState = millis();
medPressCount = 0;
}
// ===== Read Sugar =====
sugar = map(analogRead(POT_PIN), 0, 4095, 40, 300);
digitalWrite(LED_BLUE, (sugar < 70 || sugar > 180));
// ===== Medicine Taken =====
if (buttonPressed(BTN_MED)) {
medTaken[currentMed] = true;
medState = COUNTDOWN;
timerState = millis();
digitalWrite(LED_RED, LOW);
digitalWrite(LED_YELLOW, LOW);
if (mode) {
medPressCount++;
if (medPressCount == 3) {
medPressCount = 0;
dailySugar[dayIndex++] = sugar;
daysPassed++;
if (dayIndex >= MAX_DAYS_TEST) dayIndex = 0;
if (daysPassed >= MAX_DAYS_TEST) {
float avg = calcWeightedAverage(MAX_DAYS_TEST);
float highPercent = highSugarPercent(MAX_DAYS_TEST);
float lowPercent = lowSugarPercent(MAX_DAYS_TEST);
needInsulin = (avg > 160) || (highPercent > 30.0) || (lowPercent > 20.0);
}
}
}
}
// ===== Reset REAL daily =====
if (!mode && now.hour() == 0 && now.minute() == 0) {
for (int i = 0; i < 3; i++) medTaken[i] = false;
}
// ===== STATE MACHINE =====
switch (medState) {
case COUNTDOWN:
digitalWrite(LED_RED, LOW);
digitalWrite(LED_YELLOW, LOW);
if (mode) {
int t = 10 - (millis() - timerState) / 1000;
if (t < 0) t = 0;
segment.showNumberDec(t, true);
if (t == 0) {
medState = YELLOW_ON;
timerState = millis();
}
} else {
DateTime nextMed = nextMedicine(now);
long diff = nextMed.unixtime() - now.unixtime();
if (diff <= 0) {
medState = YELLOW_ON;
timerState = millis();
} else {
int h = diff / 3600;
int m = (diff % 3600) / 60;
segment.showNumberDecEx(h * 100 + m, 0b01000000, true);
}
}
break;
case YELLOW_ON:
digitalWrite(LED_YELLOW, HIGH);
if (millis() - timerState >= 5000) {
digitalWrite(LED_YELLOW, LOW);
medState = RED_ALERT;
}
break;
case RED_ALERT:
if (millis() - blinkTimer > 500) {
blinkState = !blinkState;
blinkTimer = millis();
}
digitalWrite(LED_RED, blinkState);
break;
}
// ===== Daily Timer =====
unsigned long dayInterval = mode ? 10000UL : 86400000UL;
if (millis() - lastDayTimer > dayInterval) {
lastDayTimer = millis();
if (!mode) {
dailySugar[dayIndex] = sugar;
dayIndex = (dayIndex + 1) % MAX_DAYS_REAL;
daysPassed++;
}
}
// ===== Ultrasonic Fall Detection =====
currentDistance = readUltrasonic();
if (currentDistance > 0) {
if (currentDistance >= BED_DISTANCE_MIN && currentDistance <= BED_DISTANCE_MAX) {
lastDistance = currentDistance;
fallTimer = 0;
fallDetected = false;
} else if (currentDistance > FALL_DISTANCE &&
lastDistance >= BED_DISTANCE_MIN &&
lastDistance <= BED_DISTANCE_MAX) {
if (fallTimer == 0) fallTimer = millis();
if (millis() - fallTimer >= FALL_CONFIRM_MS) fallDetected = true;
} else fallTimer = 0;
}
// ===== Buzzer Unified =====
// تعیین حالت بازر
enum BuzzerMode { BUZZER_OFF, BUZZER_FALL, BUZZER_GAS };
static BuzzerMode buzzerMode = BUZZER_OFF;
if (fallDetected) buzzerMode = BUZZER_FALL;
else if (gasAlert) buzzerMode = BUZZER_GAS;
else buzzerMode = BUZZER_OFF;
unsigned long nowMillis = millis();
static bool buzzerState = false;
static unsigned long buzzerTimer = 0;
#define GAS_BUZZER_ON_TIME 200
#define GAS_BUZZER_OFF_TIME 200
switch (buzzerMode) {
case BUZZER_OFF:
ledcWrite(BUZZER_CHANNEL, 0);
buzzerState = false;
break;
case BUZZER_FALL:
if (nowMillis - buzzerTimer > 300) {
buzzerTimer = nowMillis;
buzzerState = !buzzerState;
ledcWrite(BUZZER_CHANNEL, buzzerState ? 128 : 0);
}
break;
case BUZZER_GAS:
if (buzzerState) {
if (nowMillis - buzzerTimer >= GAS_BUZZER_ON_TIME) {
buzzerTimer = nowMillis;
buzzerState = false;
ledcWrite(BUZZER_CHANNEL, 0);
}
} else {
if (nowMillis - buzzerTimer >= GAS_BUZZER_OFF_TIME) {
buzzerTimer = nowMillis;
buzzerState = true;
static bool toggleFreq = false;
toggleFreq = !toggleFreq;
int freq = toggleFreq ? 2000 : 2500;
ledcWriteTone(BUZZER_CHANNEL, freq);
}
}
break;
}
// ===== Silence Button =====
if (fallDetected) {
if (digitalRead(BTN_SILENCE) == LOW) {
static unsigned long silencePressTime = 0;
if (silencePressTime == 0) silencePressTime = millis();
if (millis() - silencePressTime >= 1000) {
fallDetected = false;
buzzerState = false;
ledcWrite(BUZZER_CHANNEL, 0);
silencePressTime = 0;
}
} else silencePressTime = 0;
}
ldrValue = analogRead(LDR_PIN);
if (ldrValue < LDR_NIGHT_THRESHOLD) {
isNight = true;
} else {
isNight = false;
}
if (isNight) {
lcd.noBacklight(); // شب → کم نور یا خاموش
} else {
lcd.backlight(); // روز → روشن
}
bool pirActive = digitalRead(PIR_PIN);
if (
isNight && // شب باشد
pirActive && // حرکت تشخیص داده شود
(ldrValue - lastLdrValue > LDR_JUMP_THRESHOLD) // نور ناگهانی زیاد شود
) {
patientAwakeAtNight = true;
awakeDetectTimer = millis();
}
int pirState = digitalRead(PIR_PIN); // ✅ اینجا تعریف شد
// ===== LDR + PIR : Night Wake Detection =====
// زمان فعلی
// خواندن LDR و تشخیص شب
int ldrValue = analogRead(LDR_PIN);
bool isNight = (ldrValue < LDR_NIGHT_THRESHOLD);
// خواندن PIR
// ریست شمارنده در شروع هر شب جدید
if (isNight && now.day() != lastNightDay) {
nightWakeCount = 0;
lastNightDay = now.day();
nightMovementActive = false;
}
// تشخیص بیدار شدن بیمار
if (isNight && pirState == HIGH && !nightMovementActive) {
nightWakeCount++; // شمارش یک بیداری
nightMovementActive = true; // جلوگیری از شمارش تکراری
}
// آزاد شدن قفل وقتی حرکت تمام شد
if (pirState == LOW) {
nightMovementActive = false;
}
// ===== LCD (Patient) =====
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("BS:");
lcd.print(sugar);
lcd.print(sugar > 180 ? " H" : sugar < 70 ? " L" : " OK");
lcd.setCursor(10, 0);
if (now.hour() < 10) lcd.print("0");
lcd.print(now.hour());
lcd.print(":");
if (now.minute() < 10) lcd.print("0");
lcd.print(now.minute());
lcd.setCursor(0, 1);
if (needInsulin)
lcd.print("Need Insulin!");
else if (medState == YELLOW_ON)
lcd.print("Take Medicine");
else if (medState == RED_ALERT)
lcd.print("Medicine Miss!");
else
lcd.print("Waiting...");
if (lowActivityAlert)
lcd.print("Please Move :) ");
else
lcd.print("Activity OK ");
// ===== OLED (Doctor) =====
oled.clearDisplay();
oled.setCursor(0, 0);
if (now.hour() < 10) oled.print("0");
oled.print(now.hour());
oled.print(":");
if (now.minute() < 10) oled.print("0");
oled.print(now.minute());
oled.print("Mode: ");
oled.println(mode ? "TEST" : "REAL");
oled.print("Med State: ");
oled.println(
medState == COUNTDOWN ? "WAIT" :
medState == YELLOW_ON ? "DUE" : "OVERDUE"
);
oled.print("Day: ");
oled.print(daysPassed);
oled.print(" / ");
oled.println(mode ? MAX_DAYS_TEST : MAX_DAYS_REAL);
oled.print("Avg: ");
oled.println(daysPassed < (mode ? MAX_DAYS_TEST : MAX_DAYS_REAL) ? 0 : (int)calcWeightedAverage(mode ? MAX_DAYS_TEST : MAX_DAYS_REAL));
oled.print("Gas: ");
oled.println(gasAlert ? "ALERT!" : "OK");
oled.print("Night Awake: ");
oled.println(patientAwakeAtNight ? "YES" : "NO");
// ===== تشخیص بیدار شدن بیمار در شب =====
if (isNight && pirState == HIGH) {
oled.println("ALERT!");
oled.println("Patient Awake");
oled.println("Night Movement");
oled.print("Night Wakes: ");
oled.println(nightWakeCount);
}
oled.display();
delay(200);
}