#include #include #include // ============== CONFIGURATION ============== const char* ssid = "iot-2GHz"; const char* password = "lesson-greater"; const char* alertTopic = "http://ntfy.sh/ALERT_klubhaus_topic/json?poll=1"; const char* silenceTopic = "http://ntfy.sh/SILENCE_klubhaus_topic/json?poll=1"; const unsigned long POLL_INTERVAL = 15000; const unsigned long SILENCE_POLL_INTERVAL = 15000; const unsigned long BLINK_DURATION = 180000; // 3 minutes const unsigned long BLINK_PERIOD = 1000; // 1 second on/off // Hardware pins #define BACKLIGHT_PIN 22 // GPIO 22 - backlight control #define BUTTON_PIN 9 // GPIO 9 - BOOT button // Backlight brightness levels (0-255) #define BRIGHTNESS_OFF 0 #define BRIGHTNESS_DIM 50 #define BRIGHTNESS_FULL 255 // =========================================== // STATE MACHINE enum State { STATE_SILENT, // Backlight off or dim STATE_ALARM // Backlight blinking full brightness }; State currentState = STATE_SILENT; unsigned long lastPoll = 0; unsigned long lastSilencePoll = 0; unsigned long blinkStartTime = 0; bool blinkState = false; // false = off, true = on String lastAlertId = ""; String lastSilenceId = ""; bool buttonWasPressed = false; void setup() { Serial.begin(115200); delay(3000); Serial.println("\n=== BACKLIGHT DOORBELL ==="); // Initialize backlight PWM pinMode(BACKLIGHT_PIN, OUTPUT); analogWrite(BACKLIGHT_PIN, BRIGHTNESS_OFF); Serial.println("Backlight PWM ready on GPIO 22"); // Initialize button pinMode(BUTTON_PIN, INPUT_PULLUP); Serial.println("Button ready on GPIO 9"); // Test sequence: flash backlight to confirm working Serial.println("Backlight test sequence..."); analogWrite(BACKLIGHT_PIN, BRIGHTNESS_FULL); delay(200); analogWrite(BACKLIGHT_PIN, BRIGHTNESS_OFF); delay(200); analogWrite(BACKLIGHT_PIN, BRIGHTNESS_FULL); delay(200); analogWrite(BACKLIGHT_PIN, BRIGHTNESS_OFF); Serial.println("Test complete"); // Start in silent state transitionTo(STATE_SILENT); // WiFi Serial.println("Connecting WiFi..."); WiFi.begin(ssid, password); int wifiTimeout = 0; while (WiFi.status() != WL_CONNECTED && wifiTimeout < 40) { delay(500); Serial.print("."); wifiTimeout++; } if (WiFi.status() != WL_CONNECTED) { Serial.println("\nWiFi FAILED - will retry"); // Flash error pattern for (int i = 0; i < 5; i++) { analogWrite(BACKLIGHT_PIN, BRIGHTNESS_FULL); delay(100); analogWrite(BACKLIGHT_PIN, BRIGHTNESS_OFF); delay(100); } } else { Serial.println("\nWiFi OK: " + WiFi.localIP().toString()); // Brief success flash analogWrite(BACKLIGHT_PIN, BRIGHTNESS_DIM); delay(500); analogWrite(BACKLIGHT_PIN, BRIGHTNESS_OFF); } Serial.println("=== SETUP COMPLETE ==="); Serial.println(String("State: ") + getStateName(currentState)); } void loop() { unsigned long now = millis(); handleButton(); switch (currentState) { case STATE_SILENT: // Check silence topic frequently if (now - lastSilencePoll >= SILENCE_POLL_INTERVAL) { lastSilencePoll = now; checkSilenceTopic(); } // Check alert topic periodically if (now - lastPoll >= POLL_INTERVAL) { lastPoll = now; checkAlertTopic(); } break; case STATE_ALARM: // Check silence topic for early stop if (now - lastSilencePoll >= SILENCE_POLL_INTERVAL) { lastSilencePoll = now; checkSilenceTopic(); } // Update blinking updateBlink(now); // Auto-timeout if (now - blinkStartTime >= BLINK_DURATION) { Serial.println("ALARM TIMEOUT - auto silence"); transitionTo(STATE_SILENT); } break; } delay(10); } // ============== STATE MANAGEMENT ============== void transitionTo(State newState) { if (newState == currentState) return; Serial.print("STATE: "); Serial.print(getStateName(currentState)); Serial.print(" -> "); Serial.println(getStateName(newState)); currentState = newState; switch (currentState) { case STATE_SILENT: analogWrite(BACKLIGHT_PIN, BRIGHTNESS_OFF); Serial.println("Backlight OFF"); break; case STATE_ALARM: blinkStartTime = millis(); blinkState = false; analogWrite(BACKLIGHT_PIN, BRIGHTNESS_FULL); Serial.println("Backlight BLINK started"); break; } } const char* getStateName(State s) { return (s == STATE_SILENT) ? "SILENT" : "ALARM"; } // ============== BUTTON HANDLING ============== void handleButton() { bool pressed = (digitalRead(BUTTON_PIN) == LOW); // Active low if (pressed && !buttonWasPressed) { // Button just pressed Serial.println("BUTTON PRESSED - test mode"); buttonWasPressed = true; analogWrite(BACKLIGHT_PIN, BRIGHTNESS_FULL); Serial.println("Backlight FULL (held)"); } else if (!pressed && buttonWasPressed) { // Button released Serial.println("BUTTON RELEASED"); buttonWasPressed = false; // Restore state if (currentState == STATE_SILENT) { analogWrite(BACKLIGHT_PIN, BRIGHTNESS_OFF); Serial.println("Backlight OFF (silent)"); } else { // Let updateBlink handle it on next loop Serial.println("Backlight returns to blink mode"); } } } // ============== NETWORK FUNCTIONS ============== void checkAlertTopic() { Serial.println("Poll alert..."); String response = fetchNtfy(alertTopic); if (response.length() == 0) { Serial.println(" No response"); return; } StaticJsonDocument<512> doc; DeserializationError error = deserializeJson(doc, response); if (error) { Serial.print(" JSON error: "); Serial.println(error.c_str()); return; } String id = doc["id"] | ""; String message = doc["message"] | ""; if (id == lastAlertId) { Serial.println(" Same ID, skip"); return; } lastAlertId = id; Serial.print(" New message: "); Serial.println(message); if (message.equalsIgnoreCase("SILENCE")) { Serial.println(" -> SILENCE command"); transitionTo(STATE_SILENT); // Brief green-like flash (dim) to confirm analogWrite(BACKLIGHT_PIN, BRIGHTNESS_DIM); delay(500); analogWrite(BACKLIGHT_PIN, BRIGHTNESS_OFF); } else { Serial.println(" -> TRIGGER ALARM"); transitionTo(STATE_ALARM); } } void checkSilenceTopic() { String response = fetchNtfy(silenceTopic); if (response.length() == 0) return; StaticJsonDocument<512> doc; DeserializationError error = deserializeJson(doc, response); if (error) return; String id = doc["id"] | ""; if (id == lastSilenceId) return; lastSilenceId = id; Serial.print("Silence topic: "); Serial.println((const char*)doc["message"]); if (currentState == STATE_ALARM) { Serial.println(" -> Stopping alarm"); transitionTo(STATE_SILENT); // Brief dim flash to confirm analogWrite(BACKLIGHT_PIN, BRIGHTNESS_DIM); delay(500); analogWrite(BACKLIGHT_PIN, BRIGHTNESS_OFF); } } String fetchNtfy(const char* url) { HTTPClient http; http.begin(url); http.setTimeout(5000); int code = http.GET(); Serial.print(" HTTP "); Serial.println(code); if (code != 200) { http.end(); return ""; } String payload = http.getString(); http.end(); int newline = payload.indexOf('\n'); if (newline > 0) payload = payload.substring(0, newline); return payload; } // ============== BLINK CONTROL ============== void updateBlink(unsigned long now) { unsigned long elapsed = now - blinkStartTime; bool newState = ((elapsed / BLINK_PERIOD) % 2) == 1; if (newState != blinkState) { blinkState = newState; analogWrite(BACKLIGHT_PIN, blinkState ? BRIGHTNESS_FULL : BRIGHTNESS_OFF); Serial.println(blinkState ? "BLINK: ON" : "BLINK: OFF"); } }