diff --git a/docs/superpowers/plans/2026-08-02-led-stable-connection.md b/docs/superpowers/plans/2026-08-02-led-stable-connection.md deleted file mode 100644 index 17ecb21..0000000 --- a/docs/superpowers/plans/2026-08-02-led-stable-connection.md +++ /dev/null @@ -1,292 +0,0 @@ -# LED Stable Connection Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Uniform LED signaling for server and client — LED acts as debug indicator, turns off after 60s stable connection. - -**Architecture:** Shared LED state machine: OTA overrides all, then relay-flash overlay, then connection-status base color (Red=disconnected, Yellow=connected<60s, Off=stable>=60s). Both firmwares use identical logic. - -**Tech Stack:** C++/Arduino, FastLED, NimBLE, ESP32-S3 (PlatformIO) - ---- - -## File Map - -| File | Action | Purpose | -|------|--------|---------| -| `src/main.cpp` | Modify | Server LED logic, new globals, remove setup blinks | -| `src/client.cpp` | Modify | Client LED logic, new globals, OTA placeholders | -| `src/version.h` | Modify | Version bumps (server 15→16, client 1→2) | - ---- - -### Task 1: Server LED logic - -**Files:** -- Modify: `src/main.cpp` - -- [ ] **Step 1: Add new global variables after line 56 (after `int connectedCount = 0;`)** - -```cpp -#define STABLE_LED_TIMEOUT_MS 60000 -uint32_t connectionStableMs = 0; -uint32_t relayFlashUntil = 0; -``` - -- [ ] **Step 2: Remove `ledOtaBusy()` function (lines 181-183)** - -Delete this function — it will be inlined in the new LED logic: - -```cpp -static void ledOtaBusy() { - leds[0] = (millis() / 100) % 2 ? CRGB::Green : CRGB::Black; -} -``` - -Remove all 3 lines. - -- [ ] **Step 3: Replace setup LED blinks (lines 414-422) with solid red** - -Replace: - -```cpp - for (int i = 0; i < 3; i++) { - leds[0] = CRGB::Green; - FastLED.show(); - delay(200); - leds[0] = CRGB::Black; - FastLED.show(); - delay(200); - } - Serial.println("LED blink done, starting BLE..."); -``` - -With: - -```cpp - leds[0] = CRGB::Red; - FastLED.show(); - Serial.println("LED red (no connection), starting BLE..."); -``` - -- [ ] **Step 4: Remove `leds[0] = CRGB::Yellow;` from otaReady handler (line 553)** - -In the otaReady block (lines 542-558), remove line 553 `leds[0] = CRGB::Yellow;`. The wait-for-confirm LED is now handled by the main LED logic checking `otaInProgress && otaEnded`. - -- [ ] **Step 5: Replace the LED block in loop() (lines 599-615) with new logic** - -Replace lines 599-615: - -```cpp - if (!otaInProgress) { - relay1State = t1Active || btnActive || - (connActive[0] && bleRelay1[0]) || (connActive[1] && bleRelay1[1]); - relay2State = t2Active || - (connActive[0] && bleRelay2[0]) || (connActive[1] && bleRelay2[1]); - - leds[0] = (connectedCount > 0) ? CRGB::Green : CRGB::Black; - } else { - relay1State = false; - relay2State = false; - ledOtaBusy(); - } - - digitalWrite(RELAY1_PIN, relay1State ? HIGH : LOW); - digitalWrite(RELAY2_PIN, relay2State ? HIGH : LOW); - - FastLED.show(); -``` - -With: - -```cpp - if (!otaInProgress) { - relay1State = t1Active || btnActive || - (connActive[0] && bleRelay1[0]) || (connActive[1] && bleRelay1[1]); - relay2State = t2Active || - (connActive[0] && bleRelay2[0]) || (connActive[1] && bleRelay2[1]); - } else { - relay1State = false; - relay2State = false; - } - - // --- LED logic --- - CRGB ledColor; - - if (otaInProgress) { - ledColor = otaEnded - ? CRGB::Yellow - : (((millis() / 100) % 2) ? CRGB::Green : CRGB::Black); - } else { - if (connectedCount > 0) { - if (connectionStableMs == 0) connectionStableMs = millis(); - } else { - connectionStableMs = 0; - } - - if (connectedCount > 0) { - ledColor = (millis() - connectionStableMs >= STABLE_LED_TIMEOUT_MS) - ? CRGB::Black - : CRGB::Yellow; - } else { - ledColor = CRGB::Red; - } - - static bool lastRelayActive = false; - bool relayActive = relay1State || relay2State; - if (relayActive != lastRelayActive) { - relayFlashUntil = millis() + 150; - } - lastRelayActive = relayActive; - - if (millis() < relayFlashUntil) { - ledColor = CRGB::Green; - } - } - - leds[0] = ledColor; - - digitalWrite(RELAY1_PIN, relay1State ? HIGH : LOW); - digitalWrite(RELAY2_PIN, relay2State ? HIGH : LOW); - - FastLED.show(); -``` - -- [ ] **Step 6: Commit server changes** - -```bash -git add src/main.cpp -git commit -m "feat(server): uniform LED signaling — off after 60s stable connection" -``` - ---- - -### Task 2: Client LED logic - -**Files:** -- Modify: `src/client.cpp` - -- [ ] **Step 1: Add new global variables after line 48 (after `static uint32_t seq = 0;`)** - -```cpp -#define STABLE_LED_TIMEOUT_MS 60000 -uint32_t connectionStableMs = 0; -uint32_t relayFlashUntil = 0; -bool otaInProgress = false; -bool otaEnded = false; -``` - -- [ ] **Step 2: Replace the LED block in loop() (lines 368-375) with new logic** - -Replace lines 368-375: - -```cpp - if (relay1State || relay2State) { - leds[0] = CRGB::Green; - } else if (connected) { - leds[0] = CRGB::Blue; - } else { - leds[0] = CRGB::Red; - } - FastLED.show(); -``` - -With: - -```cpp - // --- LED logic --- - CRGB ledColor; - - if (otaInProgress) { - ledColor = otaEnded - ? CRGB::Yellow - : (((millis() / 100) % 2) ? CRGB::Green : CRGB::Black); - } else { - if (connected) { - if (connectionStableMs == 0) connectionStableMs = millis(); - } else { - connectionStableMs = 0; - } - - if (connected) { - ledColor = (millis() - connectionStableMs >= STABLE_LED_TIMEOUT_MS) - ? CRGB::Black - : CRGB::Yellow; - } else { - ledColor = CRGB::Red; - } - - static bool lastRelayActive = false; - bool relayActive = relay1State || relay2State; - if (relayActive != lastRelayActive) { - relayFlashUntil = millis() + 150; - } - lastRelayActive = relayActive; - - if (millis() < relayFlashUntil) { - ledColor = CRGB::Green; - } - } - - leds[0] = ledColor; - FastLED.show(); -``` - -- [ ] **Step 3: Commit client changes** - -```bash -git add src/client.cpp -git commit -m "feat(client): uniform LED signaling — off after 60s stable connection" -``` - ---- - -### Task 3: Version bumps - -**Files:** -- Modify: `src/version.h` - -- [ ] **Step 1: Bump versions** - -Replace: - -```cpp -#define FIRMWARE_VERSION_SERVER "15" -#define FIRMWARE_VERSION_CLIENT "1" -``` - -With: - -```cpp -#define FIRMWARE_VERSION_SERVER "16" -#define FIRMWARE_VERSION_CLIENT "2" -``` - -- [ ] **Step 2: Commit version bump** - -```bash -git add src/version.h -git commit -m "chore: bump server v16, client v2" -``` - ---- - -### Task 4: Build verification - -**Files:** -- None (build only) - -- [ ] **Step 1: Build server firmware** - -Run: `pio run -e s3lite-server` -Expected: SUCCESS, no errors. - -- [ ] **Step 2: Build client firmware** - -Run: `pio run -e s3lite-client` -Expected: SUCCESS, no errors. - -- [ ] **Step 3: Verify both targets compile cleanly** - -Run: `pio run` -Expected: Both environments compile SUCCESS. diff --git a/docs/superpowers/specs/2026-08-02-led-stable-connection-design.md b/docs/superpowers/specs/2026-08-02-led-stable-connection-design.md deleted file mode 100644 index 04ff8e0..0000000 --- a/docs/superpowers/specs/2026-08-02-led-stable-connection-design.md +++ /dev/null @@ -1,97 +0,0 @@ -# LED-signalering: stabiele verbinding - -**Issue:** [#4](https://git.eddydevink.nl/eddy/Project_SylDa/issues/4) — Leds mogen uit na een minuut stabiele verbinding -**Datum:** 2026-08-02 -**Status:** goedgekeurd - -## Doel - -Uniforme LED-signalering voor zowel server als client. De LED dient als debug-indicator tijdens opstarten en bij verbindingsproblemen. Na 60 seconden continue stabiele verbinding schakelt de LED uit. - -## Kleurenschema - -Identiek voor server (`main.cpp`) en client (`client.cpp`): - -| State | LED | Prioriteit | -|-------|-----|-----------| -| OTA in progress | Groen knipperend (100ms interval) | 1 (hoogst) | -| OTA completed, waiting confirm | Geel vast | 1 | -| Geen verbinding | Rood | 2 | -| Verbinding actief (< 60s) | Geel | 3 | -| Verbinding stabiel (>= 60s) | **Uit** | 4 (laagst) | -| Relais-flash (overlay) | Groen 150ms | overlay | - -Relais-flash is een overlay: toont kort groen bij verandering in relais-status, daarna keert LED terug naar de kleur die hoort bij de huidige verbindingsstatus. - -## Timer-logica - -- Timer (`connectionStableMs`) start zodra de **eerste** verbinding tot stand komt -- `connectionStableMs` telt op zolang er minstens 1 verbinding is (connectedCount > 0 voor server, connected == true voor client) -- Nieuwe verbindingen die later bijkomen resetten de timer **niet** -- Bij disconnect van een verbinding terwijl er nog andere verbindingen zijn: timer loopt door -- Pas als **alle** verbindingen wegvallen: `connectionStableMs` = 0, LED direct rood -- Drempel: `STABLE_LED_TIMEOUT_MS = 60000` - -### Server-specifiek - -- `connectedCount`: aantal actieve BLE-clients (0-2) -- Timer loopt als `connectedCount > 0` -- Alle clients disconnect = reset - -### Client-specifiek - -- `connected`: boolean, of client verbonden is met server -- Timer loopt als `connected == true` -- Disconnect = reset - -## Relais-flash mechanisme - -- Detectie: vergelijk huidige relais-state met vorige loop-iteratie -- Bij verandering: zet `relayFlashUntil = millis() + 150` -- Zolang `millis() < relayFlashUntil`: LED groen (overschrijft verbindingskleur) -- Flash reset de 60s timer **niet** -- **Server:** detecteert verandering in `relay1State || relay2State` (combinatie van touch, button, en BLE-commands) -- **Client:** detecteert verandering in `relay1State || relay2State` (alleen touch/button) - -## OTA (beide firmwares) - -OTA heeft hoogste prioriteit en override alle andere LED-states: - -- **OTA in progress** (`otaInProgress == true`): groen knipperend, 100ms interval -- **OTA completed, waiting confirm** (`otaEnded == true && !otaConfirmed`): geel vast -- **OTA confirm timeout** of **OTA error**: valt terug naar normale verbindings-LED -- **OTA success + reboot**: groen vast, dan reboot - -Client heeft OTA-logica nog niet geïmplementeerd, maar de LED-code wordt alvast voorbereid met dezelfde OTA-override structuur zodat toekomstige OTA-implementatie direct past. - -## LED-update volgorde in loop() - -``` -1. Bepaal base-kleur op basis van verbindingsstatus (rood/geel/uit) -2. Controleer relais-flash: als actief -> groen -3. Controleer OTA-override: als actief -> OTA-kleur -4. FastLED.show() -``` - -## Wijzigingen per bestand - -### `src/main.cpp` (server) - -- Toevoegen: `STABLE_LED_TIMEOUT_MS`, `connectionStableMs`, `relayFlashUntil`, `lastRelayActive` -- Aanpassen: LED-logica in `loop()` — base-kleur uit `connectionStableMs` i.p.v. `connectedCount > 0` -- Toevoegen: relais-flash detectie -- Behouden: OTA LED-override (werkt al, prioriteit blijft hoogst) -- Verwijderen: setup LED-blinks (drie groene knipperingen) — vervangen door rood (geen verbinding) - -### `src/client.cpp` (client) - -- Toevoegen: `STABLE_LED_TIMEOUT_MS`, `connectionStableMs`, `relayFlashUntil`, `lastRelayActive` -- Aanpassen: LED-logica in `loop()` — base-kleur uit `connectionStableMs`, geen aparte relais-kleur meer -- Toevoegen: relais-flash detectie -- Toevoegen: OTA-override structuur (placeholder, `otaInProgress` default `false`) -- Setup: LED rood i.p.v. direct rood (blijft rood, maar nu als onderdeel van uniform schema) - -### `src/version.h` - -- Server versie bump naar "16" -- Client versie bump naar "2" diff --git a/src/client.cpp b/src/client.cpp index addf69a..59757b7 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -47,12 +47,6 @@ static bool relay1SendState = false; static bool relay2SendState = false; static uint32_t seq = 0; -#define STABLE_LED_TIMEOUT_MS 60000 -uint32_t connectionStableMs = 0; -uint32_t relayFlashUntil = 0; -bool otaInProgress = false; -bool otaEnded = false; - static bool bleReady = false; static bool serverFound = false; static NimBLEAddress serverAddress; @@ -371,40 +365,13 @@ void loop() { bool relay1State = t1Active || btnActive; bool relay2State = t2Active; - CRGB ledColor; - - if (otaInProgress) { - ledColor = otaEnded - ? CRGB::Yellow - : (((millis() / 100) % 2) ? CRGB::Green : CRGB::Black); + if (relay1State || relay2State) { + leds[0] = CRGB::Green; + } else if (connected) { + leds[0] = CRGB::Blue; } else { - if (connected) { - if (connectionStableMs == 0) connectionStableMs = millis(); - } else { - connectionStableMs = 0; - } - - if (connected) { - ledColor = (millis() - connectionStableMs >= STABLE_LED_TIMEOUT_MS) - ? CRGB::Black - : CRGB::Yellow; - } else { - ledColor = CRGB::Red; - } - - static bool lastRelayActive = false; - bool relayActive = relay1State || relay2State; - if (relayActive != lastRelayActive) { - relayFlashUntil = millis() + 150; - } - lastRelayActive = relayActive; - - if (millis() < relayFlashUntil) { - ledColor = CRGB::Green; - } + leds[0] = CRGB::Red; } - - leds[0] = ledColor; FastLED.show(); if (connected && pCmdChar != nullptr) { diff --git a/src/main.cpp b/src/main.cpp index 1b1b728..ea51c1a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -55,10 +55,6 @@ uint32_t lastSeq[MAX_CLIENTS] = {0, 0}; uint16_t connHandles[MAX_CLIENTS] = {0xFFFF, 0xFFFF}; int connectedCount = 0; -#define STABLE_LED_TIMEOUT_MS 60000 -uint32_t connectionStableMs = 0; -uint32_t relayFlashUntil = 0; - bool otaInProgress = false; esp_ota_handle_t otaHandle = 0; const esp_partition_t *otaPartition = nullptr; @@ -182,6 +178,10 @@ static void parseHexKey(const String &hex, uint8_t *out) { } } +static void ledOtaBusy() { + leds[0] = (millis() / 100) % 2 ? CRGB::Green : CRGB::Black; +} + static void otaReset() { otaInProgress = false; otaHandle = 0; @@ -411,9 +411,15 @@ void setup() { pinMode(RELAY1_PIN, OUTPUT); pinMode(RELAY2_PIN, OUTPUT); - leds[0] = CRGB::Red; - FastLED.show(); - Serial.println("LED red (no connection), starting BLE..."); + for (int i = 0; i < 3; i++) { + leds[0] = CRGB::Green; + FastLED.show(); + delay(200); + leds[0] = CRGB::Black; + FastLED.show(); + delay(200); + } + Serial.println("LED blink done, starting BLE..."); NimBLEDevice::init("SylDa_Server"); NimBLEDevice::setMTU(248); @@ -544,6 +550,8 @@ void loop() { Serial.println("OTA: esp_ota_end OK, setting otaEnded=true, waiting for 0xFF confirm"); otaEnded = true; otaEndedAt = millis(); + leds[0] = CRGB::Yellow; + FastLED.show(); } otaReady = false; Serial.printf("OTA: otaReady cleared — otaReady=%d, otaEnded=%d, otaConfirmed=%d, otaInProgress=%d\n", @@ -593,46 +601,14 @@ void loop() { (connActive[0] && bleRelay1[0]) || (connActive[1] && bleRelay1[1]); relay2State = t2Active || (connActive[0] && bleRelay2[0]) || (connActive[1] && bleRelay2[1]); + + leds[0] = (connectedCount > 0) ? CRGB::Green : CRGB::Black; } else { relay1State = false; relay2State = false; + ledOtaBusy(); } - CRGB ledColor; - - if (otaInProgress) { - ledColor = otaEnded - ? CRGB::Yellow - : (((millis() / 100) % 2) ? CRGB::Green : CRGB::Black); - } else { - if (connectedCount > 0) { - if (connectionStableMs == 0) connectionStableMs = millis(); - } else { - connectionStableMs = 0; - } - - if (connectedCount > 0) { - ledColor = (millis() - connectionStableMs >= STABLE_LED_TIMEOUT_MS) - ? CRGB::Black - : CRGB::Yellow; - } else { - ledColor = CRGB::Red; - } - - static bool lastRelayActive = false; - bool relayActive = relay1State || relay2State; - if (relayActive != lastRelayActive) { - relayFlashUntil = millis() + 150; - } - lastRelayActive = relayActive; - - if (millis() < relayFlashUntil) { - ledColor = CRGB::Green; - } - } - - leds[0] = ledColor; - digitalWrite(RELAY1_PIN, relay1State ? HIGH : LOW); digitalWrite(RELAY2_PIN, relay2State ? HIGH : LOW); diff --git a/src/version.h b/src/version.h deleted file mode 100644 index 9418701..0000000 --- a/src/version.h +++ /dev/null @@ -1,3 +0,0 @@ -// Auto-generated from version.txt — DO NOT EDIT -#define FIRMWARE_VERSION_SERVER "16" -#define FIRMWARE_VERSION_CLIENT "2"