Add LED ring boot chase and connect flash animations.

Give pods quick visual feedback on power-up and when a slave joins the master.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-01 22:57:31 +01:00
co-authored by Cursor
parent 7f28a48a10
commit 73f48c8002
4 changed files with 59 additions and 2 deletions
+1
View File
@@ -718,6 +718,7 @@ static void handle_discover(const uint8_t *sender_mac,
(unsigned)discover->network, mac_str,
(unsigned long)preferred_id_for_master());
led_ring_connected_anim();
queue_tx(SLAVE_TX_SLAVE_INFO);
/* Battery after ASSIGN_ID so reports carry the definitive client_id. */
}
+47
View File
@@ -27,6 +27,16 @@ static led_strip_handle_t led_ring;
#define LED_RING_BATTERY_LOW_R 255
#define LED_RING_BATTERY_LOW_G 0
#define LED_RING_BATTERY_LOW_B 0
/** Boot chase: all RING_LEDS within ~1 s. */
#define LED_RING_BOOT_STEP_MS 10
#define LED_RING_BOOT_R 255
#define LED_RING_BOOT_G 255
#define LED_RING_BOOT_B 255
/** Connected: every other LED briefly blue. */
#define LED_RING_CONNECTED_HOLD_MS 120
#define LED_RING_CONNECTED_R 0
#define LED_RING_CONNECTED_G 0
#define LED_RING_CONNECTED_B 255
static QueueHandle_t led_queue;
static SemaphoreHandle_t s_battery_low_done;
@@ -166,6 +176,33 @@ void vTaskLedRing(void *pvParameters) {
xSemaphoreGive(s_battery_low_done);
}
continue;
} else if (cmd.mode == LED_CMD_BOOT) {
uint8_t br = LED_RING_BOOT_R;
uint8_t bg = LED_RING_BOOT_G;
uint8_t bb = LED_RING_BOOT_B;
led_ring_scale_rgb(&br, &bg, &bb, LED_RING_DEFAULT_INTENSITY);
for (uint32_t i = 0; i < RING_LEDS; i++) {
led_strip_clear(led_ring);
led_strip_set_pixel(led_ring, i, br, bg, bb);
led_strip_refresh(led_ring);
vTaskDelay(pdMS_TO_TICKS(LED_RING_BOOT_STEP_MS));
}
led_strip_clear(led_ring);
led_strip_refresh(led_ring);
continue;
} else if (cmd.mode == LED_CMD_CONNECTED) {
uint8_t cr = LED_RING_CONNECTED_R;
uint8_t cg = LED_RING_CONNECTED_G;
uint8_t cb = LED_RING_CONNECTED_B;
led_ring_scale_rgb(&cr, &cg, &cb, LED_RING_DEFAULT_INTENSITY);
for (uint32_t i = 0; i < RING_LEDS; i += 2) {
led_strip_set_pixel(led_ring, i, cr, cg, cb);
}
led_strip_refresh(led_ring);
vTaskDelay(pdMS_TO_TICKS(LED_RING_CONNECTED_HOLD_MS));
led_strip_clear(led_ring);
led_strip_refresh(led_ring);
continue;
}
led_strip_refresh(led_ring);
}
@@ -256,3 +293,13 @@ void led_ring_show_battery_low(void) {
led_ring_send_command(&cmd);
(void)xSemaphoreTake(s_battery_low_done, portMAX_DELAY);
}
void led_ring_boot_anim(void) {
led_command_t cmd = {.mode = LED_CMD_BOOT};
led_ring_send_command(&cmd);
}
void led_ring_connected_anim(void) {
led_command_t cmd = {.mode = LED_CMD_CONNECTED};
led_ring_send_command(&cmd);
}
+9 -1
View File
@@ -19,7 +19,9 @@ typedef enum {
LED_CMD_PROGRESS,
LED_CMD_BLINK,
LED_CMD_FIND_ME,
LED_CMD_BATTERY_LOW
LED_CMD_BATTERY_LOW,
LED_CMD_BOOT,
LED_CMD_CONNECTED
} led_mode_t;
typedef struct {
@@ -53,4 +55,10 @@ void led_ring_find_me(void);
/** First 4 LEDs red at ~10 % for 5 s, then off (blocking in LED task). */
void led_ring_show_battery_low(void);
/** Chase: each ring LED lights once in sequence (~1 s total). */
void led_ring_boot_anim(void);
/** Every second LED briefly blue (join / reconnect). */
void led_ring_connected_anim(void);
#endif
+2 -1
View File
@@ -207,6 +207,7 @@ void app_main(void) {
}
led_ring_init();
led_ring_boot_anim();
if (app_config.master) {
cmd_queue = xQueueCreate(64, sizeof(generic_msg_t));
@@ -240,7 +241,7 @@ void app_main(void) {
cmd_set_log_level_register();
}
ESP_LOGI(TAG, "LED ring: UART LED_RING commands only (no local demo loop)");
ESP_LOGI(TAG, "LED ring: boot anim + UART LED_RING commands");
/* After OTA: PENDING_VERIFY until confirm; crash before this → rollback. */
ota_confirm_schedule();