7.1 KiB
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;)
#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:
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:
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:
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:
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:
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
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;)
#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:
if (relay1State || relay2State) {
leds[0] = CRGB::Green;
} else if (connected) {
leds[0] = CRGB::Blue;
} else {
leds[0] = CRGB::Red;
}
FastLED.show();
With:
// --- 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
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:
#define FIRMWARE_VERSION_SERVER "15"
#define FIRMWARE_VERSION_CLIENT "1"
With:
#define FIRMWARE_VERSION_SERVER "16"
#define FIRMWARE_VERSION_CLIENT "2"
- Step 2: Commit version bump
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.