292 lines
7.1 KiB
Markdown
292 lines
7.1 KiB
Markdown
# 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.
|