diff --git a/main/esp_now_slave.c b/main/esp_now_slave.c index 5e352fd..8e5dd3c 100644 --- a/main/esp_now_slave.c +++ b/main/esp_now_slave.c @@ -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. */ } diff --git a/main/led_ring.c b/main/led_ring.c index 299a38a..7543f3b 100644 --- a/main/led_ring.c +++ b/main/led_ring.c @@ -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); +} diff --git a/main/led_ring.h b/main/led_ring.h index bdae9f5..a3dbf26 100644 --- a/main/led_ring.h +++ b/main/led_ring.h @@ -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 diff --git a/main/powerpod.c b/main/powerpod.c index 82c11ef..d555f9a 100644 --- a/main/powerpod.c +++ b/main/powerpod.c @@ -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();