#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// ------------------- PINS ------------------------
#define ZONE_PIN A0
#define BATT_PIN A1
#define BUZZER_PIN 2
#define ARM_BTN 3
#define DISARM_BTN 4
#define SILENCE_BTN 5
#define LED_ARMED 6
#define LED_DISARMED 7
#define LED_ALARM 8
// ------------------- ADC THRESHOLDS ----------------
#define ZONE_OK_MIN 420
#define ZONE_OK_MAX 520
// ------------------- STATE ----------------------
bool armed = false;
bool alarmTriggered = false;
bool silenced = false;
unsigned long lastBtnTime = 0;
const unsigned long debounceDelay = 50;
unsigned long lastLedBlink = 0;
const unsigned long ledBlinkInterval = 500;
bool ledState = false;
// ------------------- BATTERY ----------------------
const float R1 = 10000.0;
const float R2 = 6800.0;
const float BAT_MIN = 6.0;
const float BAT_MAX = 8.4;
const int LOW_BATTERY_THRESHOLD = 20;
bool lowBatLedState = false;
unsigned long lastLowBatBlink = 0;
const unsigned long lowBatBlinkInterval = 500;
// ------------------- LCD ------------------------
LiquidCrystal_I2C lcd(0x27, 16, 2);
String lastLine1 = "";
String lastLine2 = "";
unsigned long lastLCDUpdate = 0;
const unsigned long LCD_UPDATE_INTERVAL = 300;
// ------------------- SIREN SETTINGS ----------------
const int FREQ_MIN = 800;
const int FREQ_MAX = 2000;
const int STEP_UP = 30; // Increased from 15
const int STEP_DOWN = 10; // Increased from 5
const int STEP_DELAY = 2; // Decreased from 5
const int PAUSE_DELAY = 20; // Decreased from 50
// Add these variables for non-blocking siren
unsigned long lastSirenUpdate = 0;
int sirenFreq = FREQ_MIN;
bool sirenRising = true;
void setup() {
pinMode(ARM_BTN, INPUT_PULLUP);
pinMode(DISARM_BTN, INPUT_PULLUP);
pinMode(SILENCE_BTN, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(LED_ARMED, OUTPUT);
pinMode(LED_DISARMED, OUTPUT);
pinMode(LED_ALARM, OUTPUT);
noTone(BUZZER_PIN);
lcd.init();
lcd.backlight();
lcd.clear();
}
void loop() {
handleButtons();
monitorZone();
updateSiren(); // Changed from alarmSound()
updateLEDs();
updateLCD();
delay(10); // Keep this for LCD stability
}
// ------------------- BUTTON HANDLING -----------------
void handleButtons() {
if (millis() - lastBtnTime < debounceDelay) return;
int armState = digitalRead(ARM_BTN);
int disarmState = digitalRead(DISARM_BTN);
int silenceState = digitalRead(SILENCE_BTN);
if (armState == LOW && !armed) {
armed = true;
alarmTriggered = false;
silenced = false;
noTone(BUZZER_PIN);
lastBtnTime = millis();
}
if (disarmState == LOW) {
armed = false;
alarmTriggered = false;
silenced = false;
noTone(BUZZER_PIN);
sirenFreq = FREQ_MIN; // Reset siren
sirenRising = true; // Reset direction
lastBtnTime = millis();
}
if (silenceState == LOW && alarmTriggered) {
silenced = true;
noTone(BUZZER_PIN);
sirenFreq = FREQ_MIN; // Reset siren
sirenRising = true; // Reset direction
lastBtnTime = millis();
}
}
// ------------------- ZONE MONITOR -------------------
void monitorZone() {
if (!armed || alarmTriggered) return;
int zoneValue = analogRead(ZONE_PIN);
if (zoneValue < ZONE_OK_MIN || zoneValue > ZONE_OK_MAX) {
alarmTriggered = true;
sirenFreq = FREQ_MIN; // Start siren at minimum
sirenRising = true; // Start rising
}
}
// ------------------- NON-BLOCKING SIREN --------------------
void updateSiren() {
if (!alarmTriggered || silenced) {
return;
}
// Check if it's time to update the siren
if (millis() - lastSirenUpdate >= STEP_DELAY) {
lastSirenUpdate = millis();
if (sirenRising) {
sirenFreq += STEP_UP;
if (sirenFreq >= FREQ_MAX) {
sirenFreq = FREQ_MAX;
sirenRising = false;
delayMicroseconds(PAUSE_DELAY * 1000); // Short pause
}
} else {
sirenFreq -= STEP_DOWN;
if (sirenFreq <= FREQ_MIN) {
sirenFreq = FREQ_MIN;
sirenRising = true;
delayMicroseconds(PAUSE_DELAY * 1000); // Short pause
}
}
tone(BUZZER_PIN, sirenFreq);
}
}
// ------------------- LED CONTROL --------------------
void updateLEDs() {
digitalWrite(LED_ARMED, LOW);
digitalWrite(LED_DISARMED, LOW);
digitalWrite(LED_ALARM, LOW);
int adcBatt = analogRead(BATT_PIN);
float vA1 = adcBatt * (5.0 / 1023.0);
float vBat = vA1 * ((R1 + R2) / R2);
int battPercent = constrain((vBat - BAT_MIN) / (BAT_MAX - BAT_MIN) * 100, 0, 100);
if (battPercent < LOW_BATTERY_THRESHOLD) {
if (millis() - lastLowBatBlink >= lowBatBlinkInterval) {
lastLowBatBlink = millis();
lowBatLedState = !lowBatLedState;
}
digitalWrite(LED_ALARM, lowBatLedState);
return;
}
if (!armed) {
digitalWrite(LED_DISARMED, HIGH);
return;
}
if (armed && !alarmTriggered) {
digitalWrite(LED_ARMED, HIGH);
return;
}
if (alarmTriggered) {
if (silenced) {
digitalWrite(LED_ALARM, HIGH);
} else {
if (millis() - lastLedBlink >= ledBlinkInterval) {
lastLedBlink = millis();
ledState = !ledState;
}
digitalWrite(LED_ALARM, ledState);
}
}
}
// ------------------- LCD UPDATE ---------------------
void updateLCD() {
if (millis() - lastLCDUpdate < LCD_UPDATE_INTERVAL) {
return;
}
lastLCDUpdate = millis();
int zoneValue = analogRead(ZONE_PIN);
int adcBatt = analogRead(BATT_PIN);
float vA1 = adcBatt * (5.0 / 1023.0);
float vBat = vA1 * ((R1 + R2) / R2);
int battPercent = constrain((vBat - BAT_MIN) / (BAT_MAX - BAT_MIN) * 100, 0, 100);
String line1 = "";
String line2 = "";
if (!armed) line1 = "System: DISARMED";
else if (armed && !alarmTriggered) line1 = "System: ARMED";
else if (alarmTriggered) line1 = "System: ALARM!";
if (battPercent < LOW_BATTERY_THRESHOLD) {
line2 = "LOW BATTERY! ";
} else {
line2 = "ADC:" + String(zoneValue);
if (alarmTriggered) line2 += silenced ? " SIL" : " BUZ";
line2 += " B:" + String(battPercent) + "%";
}
if (line1 != lastLine1) {
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print(line1);
lastLine1 = line1;
}
if (line2 != lastLine2) {
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(line2);
lastLine2 = line2;
}
}