Dim LED ring, add blink mode, and signal OTA outcome on the ring.

Default brightness is ~5%; UART blink mode and green/red pulses mark OTA success or failure. Failed UART uploads skip ESP-NOW distribution.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-19 21:53:10 +02:00
co-authored by Cursor
parent 508b684fdf
commit 8931912583
13 changed files with 324 additions and 71 deletions
+100 -8
View File
@@ -3,6 +3,8 @@
#include "driver/i2c_types.h"
#include "esp_err.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "led_strip.h"
#include <stdint.h>
@@ -16,6 +18,8 @@ static led_strip_handle_t led_ring;
#define RING_LEDS 95
#define LED_RING_PIN 7
#define LED_RING_BLINK_ON_MS 350
#define LED_RING_BLINK_OFF_MS 150
static QueueHandle_t led_queue;
@@ -43,20 +47,33 @@ const uint8_t d9[] = {19, 20, 21, 22, 23, 24, 25, 26, 27, 46, 47, 58,
const uint8_t d10[] = {46, 50, 57, 61, 65, 72, 76, 78, 80,
82, 84, 86, 88, 90, 92, 93, 94, 95};
// Lookup Array for the Digits
const digit_definition_t digit_lookup[] = {
{d0, sizeof(d0)}, {d1, sizeof(d1)}, {d2, sizeof(d2)}, {d3, sizeof(d3)},
{d4, sizeof(d4)}, {d5, sizeof(d5)}, {d6, sizeof(d6)}, {d7, sizeof(d7)},
{d8, sizeof(d8)}, {d9, sizeof(d9)}, {d10, sizeof(d10)}};
void led_ring_scale_rgb(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t intensity) {
if (intensity == 0) {
intensity = LED_RING_DEFAULT_INTENSITY;
}
*r = (uint16_t)(*r) * intensity / 255;
*g = (uint16_t)(*g) * intensity / 255;
*b = (uint16_t)(*b) * intensity / 255;
}
static void ring_fill_color(uint8_t r, uint8_t g, uint8_t b) {
for (uint32_t i = 0; i < RING_LEDS; i++) {
led_strip_set_pixel(led_ring, i, r, g, b);
}
}
void vTaskLedRing(void *pvParameters) {
/* LED Ring config */
led_strip_config_t ring_config = {
.strip_gpio_num = LED_RING_PIN,
.max_leds = RING_LEDS,
};
led_strip_rmt_config_t rmt_ring_config = {
.resolution_hz = 10 * 1000 * 1000, // 10 MHz
.resolution_hz = 10 * 1000 * 1000,
};
esp_err_t err =
led_strip_new_rmt_device(&ring_config, &rmt_ring_config, &led_ring);
@@ -67,17 +84,19 @@ void vTaskLedRing(void *pvParameters) {
led_command_t cmd;
while (1) {
if (xQueueReceive(led_queue, &cmd, portMAX_DELAY)) {
uint8_t r = cmd.r;
uint8_t g = cmd.g;
uint8_t b = cmd.b;
led_ring_scale_rgb(&r, &g, &b, cmd.intensity);
led_strip_clear(led_ring);
if (cmd.mode == LED_CMD_CLEAR) {
/* ring already cleared */
} else if (cmd.mode == LED_CMD_SET_DIGIT && cmd.value <= 10) {
digit_definition_t digit = digit_lookup[cmd.value];
for (int i = 0; i < digit.count; i++) {
// Invert LED Counting for Now
led_strip_set_pixel(led_ring, RING_LEDS - digit.leds[i], cmd.r, cmd.g,
cmd.b);
led_strip_set_pixel(led_ring, RING_LEDS - digit.leds[i], r, g, b);
}
} else if (cmd.mode == LED_CMD_PROGRESS) {
uint32_t lit = ((uint32_t)cmd.progress * RING_LEDS + 50) / 100;
@@ -85,8 +104,23 @@ void vTaskLedRing(void *pvParameters) {
lit = RING_LEDS;
}
for (uint32_t i = 0; i < lit; i++) {
led_strip_set_pixel(led_ring, i, cmd.r, cmd.g, cmd.b);
led_strip_set_pixel(led_ring, i, r, g, b);
}
} else if (cmd.mode == LED_CMD_BLINK) {
uint16_t on_ms = cmd.blink_ms > 0 ? cmd.blink_ms : LED_RING_BLINK_ON_MS;
uint8_t count = cmd.blink_count > 0 ? cmd.blink_count : 1;
for (uint8_t n = 0; n < count; n++) {
ring_fill_color(r, g, b);
led_strip_refresh(led_ring);
vTaskDelay(pdMS_TO_TICKS(on_ms));
led_strip_clear(led_ring);
led_strip_refresh(led_ring);
if (n + 1 < count) {
vTaskDelay(pdMS_TO_TICKS(LED_RING_BLINK_OFF_MS));
}
}
continue;
}
led_strip_refresh(led_ring);
}
@@ -103,3 +137,61 @@ void led_ring_send_command(led_command_t *cmd) {
xQueueSend(led_queue, cmd, portMAX_DELAY);
}
}
void led_ring_show_ota_clear(void) {
led_command_t cmd = {.mode = LED_CMD_CLEAR};
led_ring_send_command(&cmd);
}
void led_ring_show_ota_progress(uint32_t bytes_done, uint32_t total_bytes,
uint8_t r, uint8_t g, uint8_t b) {
static struct {
uint8_t pct;
uint8_t r, g, b;
} last = {255, 0, 0, 0};
if (total_bytes == 0) {
return;
}
uint32_t pct32 = (bytes_done * 100u + total_bytes / 2) / total_bytes;
if (pct32 > 100) {
pct32 = 100;
}
uint8_t pct = (uint8_t)pct32;
if (pct == last.pct && r == last.r && g == last.g && b == last.b) {
return;
}
last.pct = pct;
last.r = r;
last.g = g;
last.b = b;
led_command_t cmd = {
.mode = LED_CMD_PROGRESS,
.progress = pct,
.r = r,
.g = g,
.b = b,
.intensity = LED_RING_DEFAULT_INTENSITY,
};
led_ring_send_command(&cmd);
}
void led_ring_blink_once(uint8_t r, uint8_t g, uint8_t b) {
led_command_t cmd = {
.mode = LED_CMD_BLINK,
.r = r,
.g = g,
.b = b,
.intensity = LED_RING_DEFAULT_INTENSITY,
.blink_ms = LED_RING_BLINK_ON_MS,
.blink_count = 1,
};
led_ring_send_command(&cmd);
}
void led_ring_ota_success(void) { led_ring_blink_once(0, 255, 0); }
void led_ring_ota_failed(void) { led_ring_blink_once(255, 0, 0); }