diff --git a/sketches/doorbell/doorbell.ino b/sketches/doorbell/doorbell.ino index 7a8b221..09673be 100644 --- a/sketches/doorbell/doorbell.ino +++ b/sketches/doorbell/doorbell.ino @@ -11,66 +11,86 @@ 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 +const unsigned long BLINK_DURATION = 180000; +const unsigned long BLINK_PERIOD = 1000; -// 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 +#define RGB_LED_PIN 8 +#define BUTTON_PIN 9 // =========================================== -// STATE MACHINE -enum State { - STATE_SILENT, // Backlight off or dim - STATE_ALARM // Backlight blinking full brightness -}; +enum State { STATE_SILENT, STATE_ALARM }; State currentState = STATE_SILENT; unsigned long lastPoll = 0; unsigned long lastSilencePoll = 0; unsigned long blinkStartTime = 0; -bool blinkState = false; // false = off, true = on +bool blinkState = false; String lastAlertId = ""; String lastSilenceId = ""; bool buttonWasPressed = false; +bool lastButtonState = false; // For edge detection + +// RGB colors +struct Color { uint8_t r, g, b; }; +const Color COLOR_OFF = {0, 0, 0}; +const Color COLOR_RED = {255, 0, 0}; +const Color COLOR_GREEN = {0, 255, 0}; +const Color COLOR_BLUE = {0, 0, 255}; +const Color COLOR_WHITE = {255, 255, 255}; + +// Simple WS2812B bit-bang for single LED +void setRGB(Color c) { + uint32_t grb = ((uint32_t)c.g << 16) | ((uint32_t)c.r << 8) | c.b; + + noInterrupts(); + for (int i = 23; i >= 0; i--) { + if (grb & (1 << i)) { + digitalWrite(RGB_LED_PIN, HIGH); + delayMicroseconds(1); + digitalWrite(RGB_LED_PIN, LOW); + delayMicroseconds(1); + } else { + digitalWrite(RGB_LED_PIN, HIGH); + delayMicroseconds(1); + digitalWrite(RGB_LED_PIN, LOW); + delayMicroseconds(2); + } + } + interrupts(); + delay(1); // Reset time for WS2812B +} void setup() { Serial.begin(115200); delay(3000); - Serial.println("\n=== BACKLIGHT DOORBELL ==="); + Serial.println("\n=== RGB DOORBELL ==="); - // Initialize backlight PWM - pinMode(BACKLIGHT_PIN, OUTPUT); - analogWrite(BACKLIGHT_PIN, BRIGHTNESS_OFF); - Serial.println("Backlight PWM ready on GPIO 22"); + // Init RGB LED pin + pinMode(RGB_LED_PIN, OUTPUT); + digitalWrite(RGB_LED_PIN, LOW); - // Initialize button + // CRITICAL: Ensure LED is OFF before test + setRGB(COLOR_OFF); + delay(100); + + Serial.println("RGB LED on GPIO 8 ready"); + + // Init button pinMode(BUTTON_PIN, INPUT_PULLUP); - Serial.println("Button ready on GPIO 9"); + Serial.println("Button on GPIO 9 ready"); - // 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"); + // Test sequence with explicit OFF at end + Serial.println("RGB test..."); + setRGB(COLOR_RED); delay(200); + setRGB(COLOR_GREEN); delay(200); + setRGB(COLOR_BLUE); delay(200); + setRGB(COLOR_OFF); delay(100); // CRITICAL: return to off + Serial.println("Test complete - LED should be OFF"); - // Start in silent state - transitionTo(STATE_SILENT); - - // WiFi + // WiFi with explicit LED management Serial.println("Connecting WiFi..."); WiFi.begin(ssid, password); @@ -82,24 +102,29 @@ void setup() { } if (WiFi.status() != WL_CONNECTED) { - Serial.println("\nWiFi FAILED - will retry"); - // Flash error pattern + Serial.println("\nWiFi FAILED"); + // Error flash then OFF for (int i = 0; i < 5; i++) { - analogWrite(BACKLIGHT_PIN, BRIGHTNESS_FULL); - delay(100); - analogWrite(BACKLIGHT_PIN, BRIGHTNESS_OFF); - delay(100); + setRGB(COLOR_RED); delay(100); + setRGB(COLOR_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); + // Success flash then OFF + setRGB(COLOR_GREEN); delay(300); + setRGB(COLOR_OFF); delay(100); } + // CRITICAL: Force LED off before state transition + setRGB(COLOR_OFF); + Serial.println("LED forced OFF after WiFi"); + + // Now start in silent state + transitionTo(STATE_SILENT); + Serial.println("=== SETUP COMPLETE ==="); Serial.println(String("State: ") + getStateName(currentState)); + Serial.println(String("LED should be OFF, button ready")); } void loop() { @@ -108,12 +133,10 @@ void loop() { 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(); @@ -121,16 +144,13 @@ void loop() { 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"); + Serial.println("ALARM TIMEOUT"); transitionTo(STATE_SILENT); } break; @@ -139,8 +159,6 @@ void loop() { delay(10); } -// ============== STATE MANAGEMENT ============== - void transitionTo(State newState) { if (newState == currentState) return; @@ -153,15 +171,15 @@ void transitionTo(State newState) { switch (currentState) { case STATE_SILENT: - analogWrite(BACKLIGHT_PIN, BRIGHTNESS_OFF); - Serial.println("Backlight OFF"); + setRGB(COLOR_OFF); + Serial.println("LED OFF (silent)"); break; case STATE_ALARM: blinkStartTime = millis(); blinkState = false; - analogWrite(BACKLIGHT_PIN, BRIGHTNESS_FULL); - Serial.println("Backlight BLINK started"); + setRGB(COLOR_RED); + Serial.println("LED ALARM started"); break; } } @@ -170,40 +188,42 @@ const char* getStateName(State s) { return (s == STATE_SILENT) ? "SILENT" : "ALARM"; } -// ============== BUTTON HANDLING ============== - void handleButton() { - bool pressed = (digitalRead(BUTTON_PIN) == LOW); // Active low + bool pressed = (digitalRead(BUTTON_PIN) == LOW); - if (pressed && !buttonWasPressed) { + // Edge detection: only act on changes + if (pressed && !lastButtonState) { // Button just pressed - Serial.println("BUTTON PRESSED - test mode"); + Serial.println("BUTTON PRESSED"); buttonWasPressed = true; - analogWrite(BACKLIGHT_PIN, BRIGHTNESS_FULL); - Serial.println("Backlight FULL (held)"); + setRGB(COLOR_WHITE); + Serial.println("LED WHITE (held)"); } - else if (!pressed && buttonWasPressed) { - // Button released + else if (!pressed && lastButtonState) { + // Button just released Serial.println("BUTTON RELEASED"); buttonWasPressed = false; - // Restore state - if (currentState == STATE_SILENT) { - analogWrite(BACKLIGHT_PIN, BRIGHTNESS_OFF); - Serial.println("Backlight OFF (silent)"); + if (currentState == STATE_ALARM) { + Serial.println("Resetting ALARM -> SILENT"); + transitionTo(STATE_SILENT); + // Green confirmation + setRGB(COLOR_GREEN); + delay(300); + setRGB(COLOR_OFF); } else { - // Let updateBlink handle it on next loop - Serial.println("Backlight returns to blink mode"); + // Already silent, ensure off + setRGB(COLOR_OFF); + Serial.println("LED OFF (silent)"); } } -} -// ============== NETWORK FUNCTIONS ============== + lastButtonState = pressed; +} void checkAlertTopic() { Serial.println("Poll alert..."); String response = fetchNtfy(alertTopic); - if (response.length() == 0) { Serial.println(" No response"); return; @@ -226,18 +246,17 @@ void checkAlertTopic() { } lastAlertId = id; - Serial.print(" New message: "); + Serial.print(" New: "); Serial.println(message); if (message.equalsIgnoreCase("SILENCE")) { - Serial.println(" -> SILENCE command"); + Serial.println(" -> SILENCE"); transitionTo(STATE_SILENT); - // Brief green-like flash (dim) to confirm - analogWrite(BACKLIGHT_PIN, BRIGHTNESS_DIM); + setRGB(COLOR_GREEN); delay(500); - analogWrite(BACKLIGHT_PIN, BRIGHTNESS_OFF); + setRGB(COLOR_OFF); } else { - Serial.println(" -> TRIGGER ALARM"); + Serial.println(" -> ALARM"); transitionTo(STATE_ALARM); } } @@ -254,16 +273,15 @@ void checkSilenceTopic() { if (id == lastSilenceId) return; lastSilenceId = id; - Serial.print("Silence topic: "); + Serial.print("Silence: "); Serial.println((const char*)doc["message"]); if (currentState == STATE_ALARM) { - Serial.println(" -> Stopping alarm"); + Serial.println(" -> Stop alarm"); transitionTo(STATE_SILENT); - // Brief dim flash to confirm - analogWrite(BACKLIGHT_PIN, BRIGHTNESS_DIM); + setRGB(COLOR_GREEN); delay(500); - analogWrite(BACKLIGHT_PIN, BRIGHTNESS_OFF); + setRGB(COLOR_OFF); } } @@ -290,16 +308,14 @@ String fetchNtfy(const char* url) { 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"); + setRGB(blinkState ? COLOR_WHITE : COLOR_RED); + Serial.println(blinkState ? "WHITE" : "RED"); } } diff --git a/sketches/mise.toml b/sketches/mise.toml index 887b8b2..a8409f9 100644 --- a/sketches/mise.toml +++ b/sketches/mise.toml @@ -1,3 +1,7 @@ [tasks.upload] run = "arduino-cli compile --upload; arduino-cli monitor" dir = "{{cwd}}" + +[tasks.mon] +run = "arduino-cli monitor" +dir = "{{cwd}}"