#include #include #include #include #include #include #include #include #include // LCD Configuration (0x27 or 0x3F) #define LCD_ADDRESS 0x27 #define LCD_COLUMNS 16 #define LCD_ROWS 2 LiquidCrystal_I2C lcd(LCD_ADDRESS, LCD_COLUMNS, LCD_ROWS); // Pin Configuration (Updated per your specs) const int STATUS_RELAY = 2; // GPIO2 - Status indicator relay const int SIREN_RELAY = 15; // GPIO15 - Siren relay const int BUZZER_PIN = 18; // GPIO18 - Built-in buzzer const int SWITCH1_PIN = 36; // GPIO36 - Door2 const int SWITCH2_PIN = 39; // GPIO39 - Fire/smoke const int SWITCH3_PIN = 27; // GPIO27 - Water const int SWITCH4_PIN = 14; // GPIO14 - Power const int RESET_PIN = 32; // GPIO32 - Reset button const int DOOR_PIN = 35; // GPIO35 - Door sensor const int DHT_PIN = 13; // GPIO13 - DHT sensor const int CONFIG_PIN = 0; // GPIO0 - Config button const int DHT_TYPE = DHT11; #define DEVICE_SERIAL_NUMBER "24000002" // System Configuration struct SystemConfig { float tempThreshold; char serverURL[100]; char deviceName[32]; unsigned long sirenDuration; unsigned long doorCountdown; unsigned long buzzerDuration; }; // System States enum SystemState { STATE_NORMAL, STATE_PRE_ALARM, STATE_ALARM, STATE_SILENCED, STATE_ERROR }; // Global Variables SystemConfig config; SystemState currentState = STATE_NORMAL; unsigned long stateEnterTime = 0; unsigned long lastBuzzerToggle = 0; bool buzzerState = false; float temperature = 0; float humidity = 0; bool doorOpen = false; bool smokeDetected = false; bool waterLeakage = false; bool powerFailure = false; Preferences preferences; WiFiManager wifiManager; DHT dht(DHT_PIN, DHT_TYPE); // Debounced Button Class class DebouncedButton { private: uint8_t pin; bool lastState; unsigned long lastDebounceTime; unsigned long debounceDelay; public: DebouncedButton(uint8_t pin, unsigned long delay = 50) : pin(pin), debounceDelay(delay), lastState(LOW), lastDebounceTime(0) { pinMode(pin, INPUT_PULLUP); } bool pressed() { bool reading = digitalRead(pin); if (reading != lastState) { lastDebounceTime = millis(); } if ((millis() - lastDebounceTime) > debounceDelay) { if (reading != lastState) { lastState = reading; return (reading == LOW); // Return true only when pressed (LOW) } } return false; } }; DebouncedButton resetButton(RESET_PIN); DebouncedButton configButton(CONFIG_PIN); void setup() { Serial.begin(115200); // Initialize LCD lcd.init(); lcd.backlight(); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Initializing..."); // Initialize relays and buzzer (critical for boot) pinMode(STATUS_RELAY, OUTPUT); pinMode(SIREN_RELAY, OUTPUT); pinMode(BUZZER_PIN, OUTPUT); digitalWrite(STATUS_RELAY, LOW); digitalWrite(SIREN_RELAY, LOW); digitalWrite(BUZZER_PIN, LOW); // Initialize inputs pinMode(SWITCH1_PIN, INPUT); pinMode(SWITCH2_PIN, INPUT); pinMode(DOOR_PIN, INPUT); pinMode(SWITCH3_PIN, INPUT_PULLUP); pinMode(SWITCH4_PIN, INPUT_PULLUP); // Load configuration loadConfig(); // Initialize DHT sensor dht.begin(); // Check for config mode if (configButton.pressed()) { startConfigurationPortal(); } // Connect to WiFi initWiFi(); // Setup OTA updates setupOTA(); updateLCD("System Ready", WiFi.SSID(), 2000); } void loop() { ArduinoOTA.handle(); unsigned long currentMillis = millis(); // Read sensor data every 2 seconds static unsigned long lastSensorRead = 0; if (currentMillis - lastSensorRead >= 2000) { lastSensorRead = currentMillis; readSensorData(); } // Update system state updateSystemState(); // Handle state-specific logic handleState(); // Handle buzzer patterns handleBuzzer(); // Check for manual reset if (resetButton.pressed()) { handleReset(); } // Update LCD display every 3 seconds static unsigned long lastLCDUpdate = 0; if (currentMillis - lastLCDUpdate >= 3000) { lastLCDUpdate = currentMillis; updateLCDDisplay(); } // Send status updates every 30 seconds static unsigned long lastStatusUpdate = 0; if (currentMillis - lastStatusUpdate >= 30000) { lastStatusUpdate = currentMillis; sendStatusUpdate(); } delay(100); // Main loop delay } // LCD Functions void updateLCD(String line1, String line2, unsigned long delayTime = 0) { lcd.clear(); lcd.setCursor(0, 0); lcd.print(truncateLCDString(line1)); lcd.setCursor(0, 1); lcd.print(truncateLCDString(line2)); if (delayTime > 0) delay(delayTime); } String truncateLCDString(String input) { return (input.length() > LCD_COLUMNS) ? input.substring(0, LCD_COLUMNS) : input; } void updateLCDDisplay() { static byte displayMode = 0; switch(displayMode) { case 0: // Network info if (WiFi.status() == WL_CONNECTED) { updateLCD("WiFi: " + WiFi.SSID(), "IP: " + WiFi.localIP().toString()); } else { updateLCD("WiFi Disconnected", "Config Mode"); } break; case 1: // Sensor data updateLCD("Temp: " + String(temperature) + "C", "Humid: " + String(humidity) + "%"); break; case 2: // System status String stateStr; switch(currentState) { case STATE_NORMAL: stateStr = "Normal"; break; case STATE_PRE_ALARM: stateStr = "Door Open!"; break; case STATE_ALARM: stateStr = "ALARM ACTIVE!"; break; case STATE_SILENCED: stateStr = "Alarm Silenced"; break; case STATE_ERROR: stateStr = "SYSTEM ERROR"; break; } updateLCD("Status:", stateStr); break; } displayMode = (displayMode + 1) % 3; } // Sensor Functions bool readSensorData() { // Read DHT sensor float newTemp = dht.readTemperature(); float newHum = dht.readHumidity(); if (isnan(newTemp) || isnan(newHum)) { updateLCD("Sensor Error!", "Check DHT22"); log("ERROR", "DHT read failed"); changeState(STATE_ERROR); return false; } temperature = newTemp; humidity = newHum; // Read other sensors doorOpen = digitalRead(DOOR_PIN) == HIGH; smokeDetected = digitalRead(SWITCH2_PIN) == HIGH; waterLeakage = digitalRead(SWITCH3_PIN) == HIGH; powerFailure = digitalRead(SWITCH4_PIN) == HIGH; return true; } // Alarm Functions void handleBuzzer() { if (currentState == STATE_ALARM) { // Fast beeping during alarm (0.2s interval) if (millis() - lastBuzzerToggle >= 200) { lastBuzzerToggle = millis(); buzzerState = !buzzerState; digitalWrite(BUZZER_PIN, buzzerState); } } else if (currentState == STATE_PRE_ALARM) { // Slow beeping during pre-alarm (0.5s interval) if (millis() - lastBuzzerToggle >= 500) { lastBuzzerToggle = millis(); buzzerState = !buzzerState; digitalWrite(BUZZER_PIN, buzzerState); } } else if (currentState == STATE_ERROR) { // Error pattern (0.1s on, 0.2s off) if (millis() - lastBuzzerToggle >= (buzzerState ? 100 : 200)) { lastBuzzerToggle = millis(); buzzerState = !buzzerState; digitalWrite(BUZZER_PIN, buzzerState); } } else { digitalWrite(BUZZER_PIN, LOW); } } void handleReset() { if (currentState == STATE_ALARM) { changeState(STATE_SILENCED); updateLCD("Alarm Silenced", "System Active"); } else if (currentState == STATE_SILENCED) { changeState(STATE_ALARM); updateLCD("Alarm Reactivated", "Check System"); } } // State Management void updateSystemState() { bool alarmConditions = smokeDetected || waterLeakage || powerFailure || (temperature >= config.tempThreshold); switch(currentState) { case STATE_NORMAL: if (alarmConditions) { changeState(STATE_ALARM); } else if (doorOpen) { changeState(STATE_PRE_ALARM); } break; case STATE_PRE_ALARM: if (alarmConditions) { changeState(STATE_ALARM); } else if (!doorOpen) { changeState(STATE_NORMAL); } else if (millis() - stateEnterTime >= config.doorCountdown) { changeState(STATE_ALARM); } break; case STATE_ALARM: if (!alarmConditions && !doorOpen) { changeState(STATE_NORMAL); } break; case STATE_SILENCED: if (!alarmConditions && !doorOpen) { changeState(STATE_NORMAL); } break; case STATE_ERROR: if (millis() - stateEnterTime >= 60000) { ESP.restart(); } break; } } void handleState() { switch(currentState) { case STATE_NORMAL: digitalWrite(STATUS_RELAY, LOW); digitalWrite(SIREN_RELAY, LOW); break; case STATE_PRE_ALARM: digitalWrite(STATUS_RELAY, (millis() / 500) % 2); // Blink digitalWrite(SIREN_RELAY, LOW); break; case STATE_ALARM: digitalWrite(STATUS_RELAY, HIGH); // Siren timeout after configured duration if (millis() - stateEnterTime < config.sirenDuration) { digitalWrite(SIREN_RELAY, HIGH); } else { digitalWrite(SIREN_RELAY, LOW); } break; case STATE_SILENCED: digitalWrite(STATUS_RELAY, HIGH); digitalWrite(SIREN_RELAY, LOW); break; case STATE_ERROR: digitalWrite(STATUS_RELAY, (millis() / 100) % 2); // Fast blink digitalWrite(SIREN_RELAY, LOW); break; } } void changeState(SystemState newState) { if (currentState != newState) { currentState = newState; stateEnterTime = millis(); log("STATE", "Changed to: " + String(currentState)); } } // Configuration Management void loadConfig() { preferences.begin("config", true); if (preferences.getBytesLength("config") == sizeof(SystemConfig)) { preferences.getBytes("config", &config, sizeof(config)); } else { // Default configuration config.tempThreshold = 30.0; strncpy(config.serverURL, "https://backend.xtremeguard.org/api/alarm_status", sizeof(config.serverURL)); strncpy(config.deviceName, "XtremeGuard", sizeof(config.deviceName)); config.sirenDuration = 900000; // 15 minutes config.doorCountdown = 28000; // 28 seconds config.buzzerDuration = 300000; // 5 minutes saveConfig(); } preferences.end(); } void saveConfig() { preferences.begin("config", false); preferences.putBytes("config", &config, sizeof(config)); preferences.end(); } void startConfigurationPortal() { WiFiManagerParameter tempThreshold("threshold", "Temp Threshold (C)", String(config.tempThreshold).c_str(), 6); WiFiManagerParameter serverURL("server", "Server URL", config.serverURL, 100); WiFiManagerParameter deviceName("name", "Device Name", config.deviceName, 32); wifiManager.addParameter(&tempThreshold); wifiManager.addParameter(&serverURL); wifiManager.addParameter(&deviceName); wifiManager.setConfigPortalTimeout(180); wifiManager.setClass("invert"); updateLCD("Config Mode", "Connect to AP"); if (!wifiManager.startConfigPortal(config.deviceName)) { updateLCD("Config Failed", "Restarting...", 2000); ESP.restart(); } // Save new configuration config.tempThreshold = atof(tempThreshold.getValue()); strncpy(config.serverURL, serverURL.getValue(), sizeof(config.serverURL)); strncpy(config.deviceName, deviceName.getValue(), sizeof(config.deviceName)); saveConfig(); updateLCD("Config Saved", "Restarting...", 2000); ESP.restart(); } // WiFi Management void initWiFi() { WiFi.mode(WIFI_STA); WiFi.setAutoReconnect(true); updateLCD("Connecting to", WiFi.SSID()); if (!wifiManager.autoConnect(config.deviceName)) { updateLCD("Connection Failed", "Entering Config", 2000); startConfigurationPortal(); } updateLCD("WiFi Connected", WiFi.localIP().toString(), 2000); log("WiFi", "Connected. IP: " + WiFi.localIP().toString()); } // OTA Updates void setupOTA() { ArduinoOTA.setHostname(config.deviceName); ArduinoOTA.onStart([]() { updateLCD("OTA Update", "Starting..."); log("OTA", "Update started"); }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { lcd.setCursor(0, 1); lcd.print("Progress: "); lcd.print((progress * 100) / total); lcd.print("% "); }); ArduinoOTA.onEnd([]() { updateLCD("OTA Update", "Complete!"); log("OTA", "Update finished"); }); ArduinoOTA.onError([](ota_error_t error) { updateLCD("OTA Error", String(error).c_str()); log("OTA", "Error: " + String(error)); }); ArduinoOTA.begin(); } // Status Reporting void sendStatusUpdate() { if (WiFi.status() != WL_CONNECTED) { log("WARNING", "WiFi disconnected"); return; } WiFiClientSecure client; client.setInsecure(); // For testing - use proper cert in production HTTPClient https; if (https.begin(client, config.serverURL)) { String jsonData = "{"; jsonData += "\"serialNumber\":\"" + String(DEVICE_SERIAL_NUMBER) + "\""; jsonData += ",\"state\":\"" + String(currentState) + "\""; jsonData += ",\"temperature\":\"" + String(temperature) + "\""; jsonData += ",\"humidity\":\"" + String(humidity) + "\""; jsonData += ",\"doorOpen\":\"" + String(doorOpen) + "\""; jsonData += ",\"smokeDetected\":\"" + String(smokeDetected) + "\""; jsonData += ",\"waterLeakage\":\"" + String(waterLeakage) + "\""; jsonData += ",\"powerFailure\":\"" + String(powerFailure) + "\""; jsonData += "}"; https.addHeader("Content-Type", "application/json"); int httpCode = https.POST(jsonData); if (httpCode > 0) { log("HTTP", "Status sent. Code: " + String(httpCode)); } else { log("HTTP", "Error: " + https.errorToString(httpCode)); } https.end(); } else { log("HTTP", "Failed to begin connection"); } } // Logging void log(String tag, String message) { String logEntry = "[" + tag + "] " + message; Serial.println(logEntry); }