#include <EEPROM.h>
#include <TimerOne.h>
#include <avr/wdt.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);
unsigned long lcdOnUntil = 0;
bool lcdIsOn = false;
bool lcdTemporarilyOverridden = false;
unsigned long sensorMessageEndTime = 0;
String lcdStatusLine1 = "";
int batteryPercentToDisplay = -1;
// تنظیمات
#define NUM_SENSORS 3
#define NUM_REMOTE_KEYS 4
// پینها
const int sensorPins[NUM_SENSORS] = {2, 3, 4};
const int remotePins[NUM_REMOTE_KEYS] = {6, 7, 8, 9};
const int buzzerPin = 10;
const int ledArmedPin = A3;
const int ledDisarmedPin = A4;
const int ledSilentPin = A5;
const int ledSensorPins[NUM_SENSORS] = {A0, A1, A2};
// باتری
const int batteryRelayPin = 11;
const int batteryAnalogPin = 13;
const float batteryStartCharge = 12.4;
const float batteryStopCharge = 12.6;
const float batteryDividerRatio = 2.6;
// EEPROM
#define EEPROM_ADDR_ARMED 0
#define EEPROM_ADDR_SILENT 1
#define EEPROM_MAGIC_ADDR 10
#define EEPROM_MAGIC_VALUE 0xAB
#define EEPROM_ADDR_SENSOR_LATCHED 20
// وضعیت سیستم
bool systemArmed = false;
bool silentMode = false;
bool buzzerActive = false;
bool sensorTriggered = false;
bool sensorLatched[NUM_SENSORS] = {false};
bool remoteLock[NUM_REMOTE_KEYS] = {false};
bool isCharging = false;
unsigned long lastMotionTime = 0;
const unsigned long alarmDuration = 20000;
int lastBatteryPercent = -1;
// PIR
int pirHitCount[NUM_SENSORS] = {0};
unsigned long pirLastWindow[NUM_SENSORS] = {0};
const unsigned long PIR_WINDOW = 600;
const int PIR_REQUIRED_HITS = 2;
// Beep
volatile bool beepRequested = false;
volatile int beepCount = 0;
// ==== LCD FUNCTIONS ====
void turnOnLCD(unsigned long duration = 15000) {
lcd.backlight();
lcdIsOn = true;
lcdOnUntil = millis() + duration;
}
void turnOffLCDIfExpired() {
if (lcdIsOn && millis() > lcdOnUntil && !lcdTemporarilyOverridden) {
lcd.noBacklight();
lcdIsOn = false;
}
}
void displayMainStatus() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(lcdStatusLine1);
lcd.setCursor(0, 1);
if (batteryPercentToDisplay >= 0) {
lcd.print("Battery: ");
lcd.print(batteryPercentToDisplay);
lcd.print("%");
} else {
lcd.print("Battery: ---");
}
}
void updateStatusTextLine() {
if (!systemArmed) {
lcdStatusLine1 = "Status: Disarmed";
} else if (silentMode) {
lcdStatusLine1 = "Armed: Silent";
} else {
lcdStatusLine1 = "Armed: Active";
}
if (!lcdTemporarilyOverridden) {
displayMainStatus();
}
}
void showSensorTriggeredMessage(int sensorIndex) {
lcdTemporarilyOverridden = true;
turnOnLCD(20000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Sensor ");
lcd.print(sensorIndex + 1);
lcd.print(" Triggered");
lcd.setCursor(0, 1);
if (silentMode)
lcd.print("Silent mode");
else
lcd.print("Alarm triggered");
sensorMessageEndTime = millis() + 20000;
}
// ==== EEPROM & STATE ====
void saveSystemState() {
EEPROM.write(EEPROM_ADDR_ARMED, systemArmed);
EEPROM.write(EEPROM_ADDR_SILENT, silentMode);
EEPROM.write(EEPROM_MAGIC_ADDR, EEPROM_MAGIC_VALUE);
}
void loadSystemState() {
byte magic = EEPROM.read(EEPROM_MAGIC_ADDR);
if (magic == EEPROM_MAGIC_VALUE) {
systemArmed = EEPROM.read(EEPROM_ADDR_ARMED);
silentMode = EEPROM.read(EEPROM_ADDR_SILENT);
Serial.println(systemArmed ? (silentMode ? "Silent mode ON" : "System Armed") : "System Disarmed");
} else {
systemArmed = false;
silentMode = false;
saveSystemState();
Serial.println("EEPROM reset to defaults");
}
}
void saveSensorLatchState() {
for (int i = 0; i < NUM_SENSORS; i++) {
EEPROM.write(EEPROM_ADDR_SENSOR_LATCHED + i, sensorLatched[i]);
}
}
void loadSensorLatchState() {
for (int i = 0; i < NUM_SENSORS; i++) {
sensorLatched[i] = EEPROM.read(EEPROM_ADDR_SENSOR_LATCHED + i);
digitalWrite(ledSensorPins[i], sensorLatched[i]);
}
}
// ==== SYSTEM LOGIC ====
bool isReliablePIR(int index) {
if (digitalRead(sensorPins[index]) == HIGH) {
unsigned long now = millis();
if (now - pirLastWindow[index] > PIR_WINDOW) {
pirHitCount[index] = 1;
pirLastWindow[index] = now;
} else {
pirHitCount[index]++;
pirLastWindow[index] = now;
if (pirHitCount[index] >= PIR_REQUIRED_HITS) {
pirHitCount[index] = 0;
return true;
}
}
}
return false;
}
void requestBeep(int times) {
if (!beepRequested) {
beepCount = times * 2;
beepRequested = true;
}
}
void timerISR() {
static bool buzzerState = false;
static int intervalCounter = 0;
if (beepRequested && beepCount > 0) {
if (intervalCounter == 0) {
buzzerState = !buzzerState;
digitalWrite(buzzerPin, buzzerState);
if (!buzzerState && --beepCount == 0)
beepRequested = false;
intervalCounter = 2;
} else intervalCounter--;
}
}
void activateAlarm() {
if (!silentMode) {
buzzerActive = true;
digitalWrite(buzzerPin, HIGH);
}
}
void updateStatusLEDs() {
digitalWrite(ledArmedPin, systemArmed && !silentMode);
digitalWrite(ledSilentPin, systemArmed && silentMode);
digitalWrite(ledDisarmedPin, !systemArmed);
for (int i = 0; i < NUM_SENSORS; i++) {
digitalWrite(ledSensorPins[i], sensorLatched[i]);
}
}
void resetSensorLEDs() {
for (int i = 0; i < NUM_SENSORS; i++) {
sensorLatched[i] = false;
digitalWrite(ledSensorPins[i], LOW);
}
saveSensorLatchState();
}
void handleRemoteCommand(int key) {
switch (key) {
case 0:
if (!systemArmed || silentMode) {
systemArmed = true; silentMode = false; sensorTriggered = false;
resetSensorLEDs(); saveSystemState(); requestBeep(1);
Serial.println("System Armed (normal)");
}
break;
case 1:
if (systemArmed) {
systemArmed = false; silentMode = false; buzzerActive = false;
sensorTriggered = false;
digitalWrite(buzzerPin, LOW); saveSystemState(); requestBeep(1);
Serial.println("System Disarmed");
}
break;
case 2:
if (!systemArmed || !silentMode) {
systemArmed = true; silentMode = true; sensorTriggered = false;
resetSensorLEDs(); saveSystemState(); requestBeep(1);
Serial.println("System Armed (silent)");
}
break;
case 3:
if (buzzerActive) {
buzzerActive = false;
digitalWrite(buzzerPin, LOW);
requestBeep(1);
Serial.println("Manual siren off");
}
break;
}
updateStatusLEDs();
turnOnLCD();
updateStatusTextLine();
}
void checkBattery() {
static bool lastChargingState = false;
int rawBattery = analogRead(batteryAnalogPin);
float voltageBattery = (rawBattery * 5.0 / 1023.0) * batteryDividerRatio;
if (!isCharging && voltageBattery < batteryStartCharge) {
digitalWrite(batteryRelayPin, HIGH);
isCharging = true;
} else if (isCharging && voltageBattery >= batteryStopCharge) {
digitalWrite(batteryRelayPin, LOW);
isCharging = false;
}
if (isCharging != lastChargingState) {
Serial.println(isCharging ? "Battery charging..." : "Battery charge stopped");
lastChargingState = isCharging;
}
float percentage = ((voltageBattery - 11.0) / (12.6 - 11.0)) * 100.0;
percentage = constrain(percentage, 0, 100);
int percentInt = round(percentage);
if (percentInt != lastBatteryPercent) {
lastBatteryPercent = percentInt;
batteryPercentToDisplay = percentInt;
if (!lcdTemporarilyOverridden) {
displayMainStatus();
}
}
}
// ==== SETUP & LOOP ====
void setup() {
wdt_enable(WDTO_8S);
Serial.begin(9600);
for (int i = 0; i < NUM_SENSORS; i++) {
pinMode(sensorPins[i], INPUT);
pinMode(ledSensorPins[i], OUTPUT);
}
for (int i = 0; i < NUM_REMOTE_KEYS; i++) pinMode(remotePins[i], INPUT);
pinMode(buzzerPin, OUTPUT);
pinMode(ledArmedPin, OUTPUT);
pinMode(ledDisarmedPin, OUTPUT);
pinMode(ledSilentPin, OUTPUT);
pinMode(batteryRelayPin, OUTPUT);
digitalWrite(batteryRelayPin, LOW);
Timer1.initialize(10000);
Timer1.attachInterrupt(timerISR);
lcd.begin(16, 2);
lcd.noBacklight();
loadSystemState();
loadSensorLatchState();
updateStatusLEDs();
updateStatusTextLine();
batteryPercentToDisplay = -1;
displayMainStatus();
}
void loop() {
wdt_reset();
turnOffLCDIfExpired();
if (lcdTemporarilyOverridden && millis() > sensorMessageEndTime) {
lcdTemporarilyOverridden = false;
displayMainStatus();
}
for (int i = 0; i < NUM_REMOTE_KEYS; i++) {
if (digitalRead(remotePins[i]) == HIGH && !remoteLock[i]) {
handleRemoteCommand(i);
remoteLock[i] = true;
} else if (digitalRead(remotePins[i]) == LOW) {
remoteLock[i] = false;
}
}
if (systemArmed) {
for (int i = 0; i < NUM_SENSORS; i++) {
if (isReliablePIR(i)) {
sensorTriggered = true;
lastMotionTime = millis();
activateAlarm();
if (!sensorLatched[i]) {
sensorLatched[i] = true;
saveSensorLatchState();
updateStatusLEDs();
}
showSensorTriggeredMessage(i);
}
}
}
if (buzzerActive && millis() - lastMotionTime > alarmDuration) {
buzzerActive = false;
digitalWrite(buzzerPin, LOW);
Serial.println("Auto siren off");
}
checkBattery();
}