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:
parent
508b684fdf
commit
8931912583
@ -29,7 +29,7 @@ go run . -port /dev/ttyUSB0 clients
|
||||
| `serve` | — | Web dashboard at `http://localhost:8080` (WebSocket live updates) |
|
||||
| `ota` | 16–19 | UART firmware upload to master; firmware then pushes to slaves via ESP-NOW |
|
||||
| `ota-progress` | 21 | Query per-slave ESP-NOW OTA progress on the master (`-client N`, default all) |
|
||||
| `led-ring` | 8 | LED ring: `-mode clear\|progress\|digit`, `-progress`, `-digit`, RGB, `-intensity` |
|
||||
| `led-ring` | 8 | LED ring: `-mode clear\|progress\|digit\|blink`, `-progress`, `-digit`, RGB, `-intensity` (0 = ~5 %), `-blink-ms`, `-blink-count` |
|
||||
|
||||
`clients` requires slaves to have responded to master discover broadcasts first.
|
||||
|
||||
|
||||
@ -11,17 +11,20 @@ const (
|
||||
ledRingModeClear = 0
|
||||
ledRingModeProgress = 1
|
||||
ledRingModeDigit = 2
|
||||
ledRingModeBlink = 3
|
||||
)
|
||||
|
||||
func runLedRing(sp *serialPort, args []string) error {
|
||||
fs := flag.NewFlagSet("led-ring", flag.ExitOnError)
|
||||
mode := fs.String("mode", "progress", "clear, progress, or digit")
|
||||
mode := fs.String("mode", "progress", "clear, progress, digit, or blink")
|
||||
progress := fs.Uint("progress", 0, "fill level 0–100 (mode=progress)")
|
||||
digit := fs.Uint("digit", 0, "digit 0–10 (mode=digit)")
|
||||
r := fs.Uint("r", 0, "red 0–255")
|
||||
g := fs.Uint("g", 255, "green 0–255")
|
||||
b := fs.Uint("b", 0, "blue 0–255")
|
||||
intensity := fs.Uint("intensity", 255, "brightness 0–255")
|
||||
intensity := fs.Uint("intensity", 0, "brightness 0–255 (0 = device default ~5%)")
|
||||
blinkMs := fs.Uint("blink-ms", 350, "pulse length in ms (mode=blink)")
|
||||
blinkCount := fs.Uint("blink-count", 1, "number of pulses (mode=blink)")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -34,8 +37,10 @@ func runLedRing(sp *serialPort, args []string) error {
|
||||
modeVal = ledRingModeProgress
|
||||
case "digit":
|
||||
modeVal = ledRingModeDigit
|
||||
case "blink":
|
||||
modeVal = ledRingModeBlink
|
||||
default:
|
||||
return fmt.Errorf("unknown -mode %q (clear, progress, digit)", *mode)
|
||||
return fmt.Errorf("unknown -mode %q (clear, progress, digit, blink)", *mode)
|
||||
}
|
||||
|
||||
resp, err := sp.ledRingProgress(&pb.LedRingProgressRequest{
|
||||
@ -46,6 +51,8 @@ func runLedRing(sp *serialPort, args []string) error {
|
||||
G: uint32(*g),
|
||||
B: uint32(*b),
|
||||
Intensity: uint32(*intensity),
|
||||
BlinkMs: uint32(*blinkMs),
|
||||
BlinkCount: uint32(*blinkCount),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@ -1068,8 +1068,8 @@ func (x *EspNowUnicastTestResponse) GetSeq() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Host → device: LED ring display (progress bar, digit, or clear).
|
||||
// mode: 0=clear, 1=progress (0–100 %), 2=digit (0–10, same layout as firmware digit maps).
|
||||
// Host → device: LED ring display (progress bar, digit, clear, or blink).
|
||||
// mode: 0=clear, 1=progress (0–100 %), 2=digit (0–10), 3=blink full ring.
|
||||
type LedRingProgressRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Mode uint32 `protobuf:"varint,1,opt,name=mode,proto3" json:"mode,omitempty"`
|
||||
@ -1080,8 +1080,12 @@ type LedRingProgressRequest struct {
|
||||
R uint32 `protobuf:"varint,4,opt,name=r,proto3" json:"r,omitempty"`
|
||||
G uint32 `protobuf:"varint,5,opt,name=g,proto3" json:"g,omitempty"`
|
||||
B uint32 `protobuf:"varint,6,opt,name=b,proto3" json:"b,omitempty"`
|
||||
// * 0–255 brightness scale applied to r/g/b
|
||||
// * 0–255 brightness scale; 0 = firmware default (~5 %)
|
||||
Intensity uint32 `protobuf:"varint,7,opt,name=intensity,proto3" json:"intensity,omitempty"`
|
||||
// * Pulse length in ms (mode=blink, default 350)
|
||||
BlinkMs uint32 `protobuf:"varint,8,opt,name=blink_ms,json=blinkMs,proto3" json:"blink_ms,omitempty"`
|
||||
// * Number of pulses (mode=blink, default 1)
|
||||
BlinkCount uint32 `protobuf:"varint,9,opt,name=blink_count,json=blinkCount,proto3" json:"blink_count,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@ -1165,6 +1169,20 @@ func (x *LedRingProgressRequest) GetIntensity() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *LedRingProgressRequest) GetBlinkMs() uint32 {
|
||||
if x != nil {
|
||||
return x.BlinkMs
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *LedRingProgressRequest) GetBlinkCount() uint32 {
|
||||
if x != nil {
|
||||
return x.BlinkCount
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type LedRingProgressResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
|
||||
@ -1707,7 +1725,7 @@ const file_uart_messages_proto_rawDesc = "" +
|
||||
"\x03seq\x18\x02 \x01(\rR\x03seq\"G\n" +
|
||||
"\x19EspNowUnicastTestResponse\x12\x18\n" +
|
||||
"\asuccess\x18\x01 \x01(\bR\asuccess\x12\x10\n" +
|
||||
"\x03seq\x18\x02 \x01(\rR\x03seq\"\xa6\x01\n" +
|
||||
"\x03seq\x18\x02 \x01(\rR\x03seq\"\xe2\x01\n" +
|
||||
"\x16LedRingProgressRequest\x12\x12\n" +
|
||||
"\x04mode\x18\x01 \x01(\rR\x04mode\x12\x1a\n" +
|
||||
"\bprogress\x18\x02 \x01(\rR\bprogress\x12\x14\n" +
|
||||
@ -1715,7 +1733,10 @@ const file_uart_messages_proto_rawDesc = "" +
|
||||
"\x01r\x18\x04 \x01(\rR\x01r\x12\f\n" +
|
||||
"\x01g\x18\x05 \x01(\rR\x01g\x12\f\n" +
|
||||
"\x01b\x18\x06 \x01(\rR\x01b\x12\x1c\n" +
|
||||
"\tintensity\x18\a \x01(\rR\tintensity\"y\n" +
|
||||
"\tintensity\x18\a \x01(\rR\tintensity\x12\x19\n" +
|
||||
"\bblink_ms\x18\b \x01(\rR\ablinkMs\x12\x1f\n" +
|
||||
"\vblink_count\x18\t \x01(\rR\n" +
|
||||
"blinkCount\"y\n" +
|
||||
"\x17LedRingProgressResponse\x12\x18\n" +
|
||||
"\asuccess\x18\x01 \x01(\bR\asuccess\x12\x12\n" +
|
||||
"\x04mode\x18\x02 \x01(\rR\x04mode\x12\x1a\n" +
|
||||
|
||||
@ -253,6 +253,8 @@ Inactive app partition is selected with `esp_ota_get_next_update_partition()`; `
|
||||
|
||||
`OTA_END` can take a long time on the wire (slave flash + ESP-NOW); the host should use a generous read timeout.
|
||||
|
||||
During OTA the LED ring shows progress at ~5 % brightness: **blue** while the image is written (UART on master, ESP-NOW on slaves), **green** on the master while it forwards the image to slaves over ESP-NOW. On **success** the ring gives one short **green** blink; on **failure** one **red** blink and ESP-NOW distribution is not started (failed UART upload / `OTA_END` validation).
|
||||
|
||||
`OTA_START_ESPNOW` (type `20`): re-run ESP-NOW distribution from the last staged image without a new UART upload (no-op if nothing staged).
|
||||
|
||||
Implementation: `ota_uart.c` (4 KiB buffer, `esp_ota_write`), `ota_espnow.c`, `cmd_ota.c`.
|
||||
@ -319,11 +321,12 @@ Control the 95-LED ring from the host. The firmware **does not** animate digits
|
||||
|
||||
| Field | Meaning |
|
||||
|-------|---------|
|
||||
| `mode` | `0` = clear, `1` = progress bar, `2` = digit |
|
||||
| `mode` | `0` = clear, `1` = progress bar, `2` = digit, `3` = blink full ring |
|
||||
| `progress` | 0–100 (% of ring lit, mode `1`) |
|
||||
| `digit` | 0–10 (mode `2`, same segment maps as built-in digits) |
|
||||
| `r`, `g`, `b` | Color 0–255 |
|
||||
| `intensity` | Brightness 0–255 (scaled into RGB; `0` → 255) |
|
||||
| `intensity` | Brightness 0–255 (scaled into RGB; `0` → firmware default ~5 %) |
|
||||
| `blink_ms`, `blink_count` | Pulse length and count (mode `3`; defaults 350 ms, 1) |
|
||||
|
||||
**Response:** `led_ring_progress_response` (`success`, `mode`, `progress`, `digit`).
|
||||
|
||||
@ -331,6 +334,7 @@ Control the 95-LED ring from the host. The firmware **does not** animate digits
|
||||
go run . -port /dev/ttyUSB0 led-ring -mode progress -progress 75 -g 80 -b 255
|
||||
go run . -port /dev/ttyUSB0 led-ring -mode digit -digit 7 -r 255 -g 200
|
||||
go run . -port /dev/ttyUSB0 led-ring -mode clear
|
||||
go run . -port /dev/ttyUSB0 led-ring -mode blink -g 255 -blink-count 2
|
||||
```
|
||||
|
||||
### CLIENT_INFO command
|
||||
|
||||
@ -8,6 +8,7 @@ static const char *TAG = "[LED_RING_CMD]";
|
||||
#define LED_RING_MODE_CLEAR 0
|
||||
#define LED_RING_MODE_PROGRESS 1
|
||||
#define LED_RING_MODE_DIGIT 2
|
||||
#define LED_RING_MODE_BLINK 3
|
||||
|
||||
static uint8_t clamp_u8(uint32_t v) {
|
||||
if (v > 255) {
|
||||
@ -23,13 +24,11 @@ static uint8_t clamp_progress(uint32_t v) {
|
||||
return (uint8_t)v;
|
||||
}
|
||||
|
||||
static void apply_intensity(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t intensity) {
|
||||
static uint8_t resolve_intensity(uint32_t intensity) {
|
||||
if (intensity == 0) {
|
||||
return;
|
||||
return LED_RING_DEFAULT_INTENSITY;
|
||||
}
|
||||
*r = (uint16_t)(*r) * intensity / 255;
|
||||
*g = (uint16_t)(*g) * intensity / 255;
|
||||
*b = (uint16_t)(*b) * intensity / 255;
|
||||
return clamp_u8(intensity);
|
||||
}
|
||||
|
||||
static void reply(bool success, uint32_t mode, uint32_t progress, uint32_t digit) {
|
||||
@ -64,11 +63,7 @@ static void handle_led_ring(const uint8_t *data, size_t len) {
|
||||
uint8_t r = clamp_u8(req.r);
|
||||
uint8_t g = clamp_u8(req.g);
|
||||
uint8_t b = clamp_u8(req.b);
|
||||
uint8_t intensity = clamp_u8(req.intensity);
|
||||
if (intensity == 0) {
|
||||
intensity = 255;
|
||||
}
|
||||
apply_intensity(&r, &g, &b, intensity);
|
||||
uint8_t intensity = resolve_intensity(req.intensity);
|
||||
|
||||
led_command_t cmd = {0};
|
||||
|
||||
@ -106,6 +101,7 @@ static void handle_led_ring(const uint8_t *data, size_t len) {
|
||||
cmd.r = r;
|
||||
cmd.g = g;
|
||||
cmd.b = b;
|
||||
cmd.intensity = intensity;
|
||||
led_ring_send_command(&cmd);
|
||||
ESP_LOGI(TAG, "digit %u rgb=%u,%u,%u", (unsigned)cmd.value, (unsigned)r,
|
||||
(unsigned)g, (unsigned)b);
|
||||
@ -113,6 +109,24 @@ static void handle_led_ring(const uint8_t *data, size_t len) {
|
||||
return;
|
||||
}
|
||||
|
||||
case LED_RING_MODE_BLINK: {
|
||||
cmd.mode = LED_CMD_BLINK;
|
||||
cmd.r = r;
|
||||
cmd.g = g;
|
||||
cmd.b = b;
|
||||
cmd.intensity = intensity;
|
||||
cmd.blink_ms = (uint16_t)(req.blink_ms > 0 ? req.blink_ms : 350);
|
||||
cmd.blink_count = req.blink_count > 0 ? (uint8_t)req.blink_count : 1;
|
||||
if (cmd.blink_count == 0) {
|
||||
cmd.blink_count = 1;
|
||||
}
|
||||
led_ring_send_command(&cmd);
|
||||
ESP_LOGI(TAG, "blink x%u %u ms rgb=%u,%u,%u", (unsigned)cmd.blink_count,
|
||||
(unsigned)cmd.blink_ms, (unsigned)r, (unsigned)g, (unsigned)b);
|
||||
reply(true, mode, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
ESP_LOGW(TAG, "unknown mode %lu", (unsigned long)mode);
|
||||
reply(false, mode, 0, 0);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "cmd_ota.h"
|
||||
#include "led_ring.h"
|
||||
#include "ota_espnow.h"
|
||||
#include "ota_uart.h"
|
||||
#include "uart_cmd.h"
|
||||
@ -15,6 +16,15 @@ static const char *TAG = "[OTA_CMD]";
|
||||
#define OTA_DIST_STACK 8192
|
||||
#define OTA_DIST_PRIO 5
|
||||
|
||||
/** UART OTA upload to this node (master). */
|
||||
#define OTA_LED_UART_R 0
|
||||
#define OTA_LED_UART_G 0
|
||||
#define OTA_LED_UART_B 255
|
||||
/** ESP-NOW distribution from master to slaves. */
|
||||
#define OTA_LED_ESPNOW_TX_R 0
|
||||
#define OTA_LED_ESPNOW_TX_G 255
|
||||
#define OTA_LED_ESPNOW_TX_B 0
|
||||
|
||||
typedef struct {
|
||||
uint32_t written;
|
||||
int slot;
|
||||
@ -32,6 +42,11 @@ static void send_ota_status(ota_uart_status_t status, uint32_t err_code) {
|
||||
uart_cmd_send(&response, TAG);
|
||||
}
|
||||
|
||||
static void send_ota_failed(uint32_t err_code) {
|
||||
led_ring_ota_failed();
|
||||
send_ota_status(OTA_UART_ST_FAILED, err_code);
|
||||
}
|
||||
|
||||
static void send_ota_distributing(uint32_t kind, uint32_t bytes_done,
|
||||
uint32_t target_slot) {
|
||||
alox_UartMessage response;
|
||||
@ -46,7 +61,9 @@ static void send_ota_distributing(uint32_t kind, uint32_t bytes_done,
|
||||
|
||||
static void ota_dist_aggregate(uint32_t bytes_done, uint32_t total_bytes,
|
||||
uint8_t slave_count) {
|
||||
(void)total_bytes;
|
||||
(void)slave_count;
|
||||
led_ring_show_ota_progress(bytes_done, total_bytes, OTA_LED_ESPNOW_TX_R,
|
||||
OTA_LED_ESPNOW_TX_G, OTA_LED_ESPNOW_TX_B);
|
||||
send_ota_distributing(OTA_DIST_AGGREGATE, bytes_done, (uint32_t)slave_count);
|
||||
}
|
||||
|
||||
@ -68,7 +85,7 @@ static void ota_prepare_task(void *param) {
|
||||
|
||||
int slot = ota_uart_prepare(total_size);
|
||||
if (slot < 0) {
|
||||
send_ota_status(OTA_UART_ST_FAILED, 1);
|
||||
send_ota_failed(1);
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
}
|
||||
@ -82,6 +99,9 @@ static void ota_prepare_task(void *param) {
|
||||
response.payload.ota_status.error = 0;
|
||||
uart_cmd_send(&response, TAG);
|
||||
|
||||
led_ring_show_ota_progress(0, total_size, OTA_LED_UART_R, OTA_LED_UART_G,
|
||||
OTA_LED_UART_B);
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
@ -90,7 +110,7 @@ static void handle_ota_start(const uint8_t *data, size_t len) {
|
||||
alox_OtaStartPayload req = alox_OtaStartPayload_init_zero;
|
||||
|
||||
if (uart_cmd_decode(data, len, &uart_msg) != ESP_OK) {
|
||||
send_ota_status(OTA_UART_ST_FAILED, 2);
|
||||
send_ota_failed( 2);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -102,13 +122,13 @@ static void handle_ota_start(const uint8_t *data, size_t len) {
|
||||
|
||||
if (req.total_size == 0) {
|
||||
ESP_LOGW(TAG, "OTA_START: total_size required");
|
||||
send_ota_status(OTA_UART_ST_FAILED, 3);
|
||||
send_ota_failed( 3);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ota_uart_is_active()) {
|
||||
ESP_LOGW(TAG, "OTA_START while session active");
|
||||
send_ota_status(OTA_UART_ST_FAILED, 4);
|
||||
send_ota_failed( 4);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -116,7 +136,7 @@ static void handle_ota_start(const uint8_t *data, size_t len) {
|
||||
(void *)(uintptr_t)req.total_size, OTA_PREPARE_PRIO,
|
||||
NULL) != pdPASS) {
|
||||
ESP_LOGE(TAG, "failed to create ota_prepare task");
|
||||
send_ota_status(OTA_UART_ST_FAILED, 5);
|
||||
send_ota_failed( 5);
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,7 +145,7 @@ static void handle_ota_payload(const uint8_t *data, size_t len) {
|
||||
|
||||
if (uart_cmd_decode(data, len, &uart_msg) != ESP_OK) {
|
||||
ESP_LOGW(TAG, "OTA_PAYLOAD decode failed");
|
||||
send_ota_status(OTA_UART_ST_FAILED, 10);
|
||||
send_ota_failed( 10);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -134,34 +154,47 @@ static void handle_ota_payload(const uint8_t *data, size_t len) {
|
||||
if (req_ptr == NULL) {
|
||||
ESP_LOGW(TAG, "OTA_PAYLOAD: missing ota_payload (which=%u)",
|
||||
(unsigned)uart_msg.which_payload);
|
||||
send_ota_status(OTA_UART_ST_FAILED, 11);
|
||||
send_ota_failed( 11);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req_ptr->data.size == 0) {
|
||||
ESP_LOGW(TAG, "OTA_PAYLOAD: empty data (seq=%lu)",
|
||||
(unsigned long)req_ptr->seq);
|
||||
send_ota_status(OTA_UART_ST_FAILED, 11);
|
||||
send_ota_failed( 11);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ota_uart_is_active()) {
|
||||
ESP_LOGW(TAG, "OTA_PAYLOAD without active session (seq=%lu)",
|
||||
(unsigned long)req_ptr->seq);
|
||||
send_ota_status(OTA_UART_ST_FAILED, 12);
|
||||
send_ota_failed( 12);
|
||||
return;
|
||||
}
|
||||
|
||||
ota_feed_result_t r =
|
||||
ota_uart_feed(req_ptr->data.bytes, req_ptr->data.size);
|
||||
if (r == OTA_FEED_ERROR) {
|
||||
send_ota_status(OTA_UART_ST_FAILED, 13);
|
||||
send_ota_failed( 13);
|
||||
return;
|
||||
}
|
||||
if (r == OTA_FEED_BLOCK_WRITTEN) {
|
||||
uint32_t total = ota_uart_total_size();
|
||||
uint32_t done = ota_uart_bytes_written();
|
||||
ESP_LOGI(TAG, "OTA block ack (%lu bytes in flash)",
|
||||
(unsigned long)ota_uart_bytes_written());
|
||||
(unsigned long)done);
|
||||
led_ring_show_ota_progress(done, total, OTA_LED_UART_R, OTA_LED_UART_G,
|
||||
OTA_LED_UART_B);
|
||||
send_ota_status(OTA_UART_ST_BLOCK_ACK, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (r == OTA_FEED_OK) {
|
||||
uint32_t total = ota_uart_total_size();
|
||||
if (total > 0) {
|
||||
led_ring_show_ota_progress(ota_uart_bytes_received(), total,
|
||||
OTA_LED_UART_R, OTA_LED_UART_G, OTA_LED_UART_B);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,19 +208,21 @@ static void ota_distribute_task(void *param) {
|
||||
const esp_partition_t *part = NULL;
|
||||
uint32_t image_size = 0;
|
||||
if (!ota_uart_get_staged_image(&part, &image_size)) {
|
||||
send_ota_status(OTA_UART_ST_FAILED, 30);
|
||||
send_ota_failed( 30);
|
||||
free(job);
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
led_ring_show_ota_progress(0, image_size, OTA_LED_ESPNOW_TX_R, OTA_LED_ESPNOW_TX_G,
|
||||
OTA_LED_ESPNOW_TX_B);
|
||||
send_ota_distributing(OTA_DIST_AGGREGATE, 0, 0);
|
||||
|
||||
esp_err_t err = ota_espnow_distribute(part, image_size, &s_dist_progress);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "slave OTA distribution failed: %s", esp_err_to_name(err));
|
||||
ota_uart_clear_staged();
|
||||
send_ota_status(OTA_UART_ST_FAILED, 31);
|
||||
send_ota_failed(31);
|
||||
free(job);
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
@ -195,12 +230,15 @@ static void ota_distribute_task(void *param) {
|
||||
|
||||
err = ota_uart_apply_boot();
|
||||
if (err != ESP_OK) {
|
||||
send_ota_status(OTA_UART_ST_FAILED, (uint32_t)err);
|
||||
send_ota_failed((uint32_t)err);
|
||||
free(job);
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
led_ring_show_ota_progress(image_size, image_size, OTA_LED_ESPNOW_TX_R,
|
||||
OTA_LED_ESPNOW_TX_G, OTA_LED_ESPNOW_TX_B);
|
||||
|
||||
alox_UartMessage response;
|
||||
uart_cmd_init_response(&response, alox_MessageType_OTA_STATUS,
|
||||
alox_UartMessage_ota_status_tag);
|
||||
@ -211,6 +249,7 @@ static void ota_distribute_task(void *param) {
|
||||
response.payload.ota_status.error = 0;
|
||||
uart_cmd_send(&response, TAG);
|
||||
|
||||
led_ring_ota_success();
|
||||
free(job);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
@ -220,30 +259,36 @@ static void handle_ota_end(const uint8_t *data, size_t len) {
|
||||
(void)len;
|
||||
|
||||
if (!ota_uart_is_active()) {
|
||||
send_ota_status(OTA_UART_ST_FAILED, 20);
|
||||
send_ota_failed( 20);
|
||||
return;
|
||||
}
|
||||
|
||||
ota_dist_job_t *job = calloc(1, sizeof(*job));
|
||||
if (job == NULL) {
|
||||
send_ota_status(OTA_UART_ST_FAILED, 21);
|
||||
send_ota_failed( 21);
|
||||
return;
|
||||
}
|
||||
job->written = ota_uart_bytes_written();
|
||||
job->slot = ota_uart_target_slot();
|
||||
uint32_t uart_total = ota_uart_total_size();
|
||||
|
||||
bool success = false;
|
||||
esp_err_t err = ota_uart_finish(false, &success);
|
||||
if (err != ESP_OK || !success) {
|
||||
send_ota_status(OTA_UART_ST_FAILED, (uint32_t)err);
|
||||
send_ota_failed((uint32_t)err);
|
||||
free(job);
|
||||
return;
|
||||
}
|
||||
|
||||
if (uart_total > 0) {
|
||||
led_ring_show_ota_progress(job->written, uart_total, OTA_LED_UART_R,
|
||||
OTA_LED_UART_G, OTA_LED_UART_B);
|
||||
}
|
||||
|
||||
if (xTaskCreate(ota_distribute_task, "ota_dist", OTA_DIST_STACK, job,
|
||||
OTA_DIST_PRIO, NULL) != pdPASS) {
|
||||
ESP_LOGE(TAG, "failed to create ota_dist task");
|
||||
send_ota_status(OTA_UART_ST_FAILED, 22);
|
||||
send_ota_failed( 22);
|
||||
free(job);
|
||||
}
|
||||
}
|
||||
@ -253,26 +298,26 @@ static void handle_ota_start_espnow(const uint8_t *data, size_t len) {
|
||||
(void)len;
|
||||
|
||||
if (ota_uart_is_active()) {
|
||||
send_ota_status(OTA_UART_ST_FAILED, 40);
|
||||
send_ota_failed( 40);
|
||||
return;
|
||||
}
|
||||
|
||||
const esp_partition_t *part = NULL;
|
||||
uint32_t image_size = 0;
|
||||
if (!ota_uart_get_staged_image(&part, &image_size)) {
|
||||
send_ota_status(OTA_UART_ST_FAILED, 41);
|
||||
send_ota_failed( 41);
|
||||
return;
|
||||
}
|
||||
|
||||
esp_err_t err = ota_espnow_distribute(part, image_size, &s_dist_progress);
|
||||
if (err != ESP_OK) {
|
||||
send_ota_status(OTA_UART_ST_FAILED, 42);
|
||||
send_ota_failed( 42);
|
||||
return;
|
||||
}
|
||||
|
||||
err = ota_uart_apply_boot();
|
||||
if (err != ESP_OK) {
|
||||
send_ota_status(OTA_UART_ST_FAILED, (uint32_t)err);
|
||||
send_ota_failed( (uint32_t)err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -283,6 +328,7 @@ static void handle_ota_start_espnow(const uint8_t *data, size_t len) {
|
||||
response.payload.ota_status.bytes_written = image_size;
|
||||
response.payload.ota_status.error = 0;
|
||||
uart_cmd_send(&response, TAG);
|
||||
led_ring_ota_success();
|
||||
}
|
||||
|
||||
void cmd_ota_register(void) {
|
||||
|
||||
108
main/led_ring.c
108
main/led_ring.c
@ -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); }
|
||||
|
||||
@ -1,10 +1,17 @@
|
||||
#ifndef LED_RING_H
|
||||
#define LED_RING_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/** Default RGB scale (~5 % of full brightness). */
|
||||
#define LED_RING_DEFAULT_INTENSITY 13
|
||||
|
||||
typedef enum {
|
||||
LED_CMD_CLEAR,
|
||||
LED_CMD_SET_DIGIT,
|
||||
LED_CMD_SET_COLOR,
|
||||
LED_CMD_PROGRESS
|
||||
LED_CMD_PROGRESS,
|
||||
LED_CMD_BLINK
|
||||
} led_mode_t;
|
||||
|
||||
typedef struct {
|
||||
@ -13,7 +20,23 @@ typedef struct {
|
||||
uint8_t r, g, b;
|
||||
uint8_t intensity;
|
||||
uint8_t progress;
|
||||
uint16_t blink_ms;
|
||||
uint8_t blink_count;
|
||||
} led_command_t;
|
||||
|
||||
void led_ring_send_command(led_command_t *cmd);
|
||||
void led_ring_init(void);
|
||||
|
||||
void led_ring_scale_rgb(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t intensity);
|
||||
|
||||
/** OTA feedback: ring fill 0–100 % with RGB. */
|
||||
void led_ring_show_ota_progress(uint32_t bytes_done, uint32_t total_bytes,
|
||||
uint8_t r, uint8_t g, uint8_t b);
|
||||
void led_ring_show_ota_clear(void);
|
||||
|
||||
/** Single pulse on the full ring (blocking in LED task). */
|
||||
void led_ring_blink_once(uint8_t r, uint8_t g, uint8_t b);
|
||||
void led_ring_ota_success(void);
|
||||
void led_ring_ota_failed(void);
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#include "ota_espnow.h"
|
||||
#include "app_config.h"
|
||||
#include "led_ring.h"
|
||||
#include "client_registry.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_now_comm.h"
|
||||
@ -27,6 +28,11 @@ static const char *TAG = "[OTA_ESPNOW]";
|
||||
#define OTA_ST_SUCCESS 4u
|
||||
#define OTA_ST_FAILED 5u
|
||||
|
||||
/** ESP-NOW OTA receive on slave (blue progress bar). */
|
||||
#define OTA_LED_ESPNOW_RX_R 0
|
||||
#define OTA_LED_ESPNOW_RX_G 0
|
||||
#define OTA_LED_ESPNOW_RX_B 255
|
||||
|
||||
#define OTA_MAX_TARGETS CLIENT_REGISTRY_MAX
|
||||
|
||||
static EventGroupHandle_t s_eg;
|
||||
@ -170,6 +176,8 @@ static void ota_slave_prepare_task(void *param) {
|
||||
}
|
||||
|
||||
send_slave_status(master_mac, OTA_ST_READY, 0, 0);
|
||||
led_ring_show_ota_progress(0, total_size, OTA_LED_ESPNOW_RX_R, OTA_LED_ESPNOW_RX_G,
|
||||
OTA_LED_ESPNOW_RX_B);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
@ -214,13 +222,27 @@ void ota_espnow_slave_on_payload(const uint8_t master_mac[6],
|
||||
ota_feed_result_t r =
|
||||
ota_uart_feed(payload->data.bytes, payload->data.size);
|
||||
if (r == OTA_FEED_ERROR) {
|
||||
led_ring_ota_failed();
|
||||
send_slave_status(master_mac, OTA_ST_FAILED, ota_uart_bytes_written(), 13);
|
||||
return;
|
||||
}
|
||||
if (r == OTA_FEED_BLOCK_WRITTEN) {
|
||||
uint32_t written = ota_uart_bytes_written();
|
||||
uint32_t total = ota_uart_total_size();
|
||||
ESP_LOGI(TAG, "block written %lu bytes -> ack master", (unsigned long)written);
|
||||
led_ring_show_ota_progress(written, total, OTA_LED_ESPNOW_RX_R, OTA_LED_ESPNOW_RX_G,
|
||||
OTA_LED_ESPNOW_RX_B);
|
||||
send_slave_status(master_mac, OTA_ST_BLOCK_ACK, written, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (r == OTA_FEED_OK) {
|
||||
uint32_t total = ota_uart_total_size();
|
||||
if (total > 0) {
|
||||
led_ring_show_ota_progress(ota_uart_bytes_received(), total,
|
||||
OTA_LED_ESPNOW_RX_R, OTA_LED_ESPNOW_RX_G,
|
||||
OTA_LED_ESPNOW_RX_B);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,11 +257,13 @@ void ota_espnow_slave_on_end(const uint8_t master_mac[6]) {
|
||||
bool success = false;
|
||||
esp_err_t err = ota_uart_finish(true, &success);
|
||||
if (err != ESP_OK || !success) {
|
||||
led_ring_ota_failed();
|
||||
send_slave_status(master_mac, OTA_ST_FAILED, written, (uint32_t)err);
|
||||
return;
|
||||
}
|
||||
|
||||
send_slave_status(master_mac, OTA_ST_SUCCESS, written, 0);
|
||||
led_ring_ota_success();
|
||||
ESP_LOGI(TAG, "slave OTA success (%lu bytes), reboot to run",
|
||||
(unsigned long)written);
|
||||
}
|
||||
|
||||
@ -151,6 +151,10 @@ ota_feed_result_t ota_uart_feed(const uint8_t *data, size_t len) {
|
||||
|
||||
uint32_t ota_uart_bytes_written(void) { return s_ota.written; }
|
||||
|
||||
uint32_t ota_uart_bytes_received(void) { return s_ota.received; }
|
||||
|
||||
uint32_t ota_uart_total_size(void) { return s_ota.active ? s_ota.total_size : 0; }
|
||||
|
||||
bool ota_uart_get_staged_image(const esp_partition_t **partition_out,
|
||||
uint32_t *size_out) {
|
||||
if (!s_staged.valid || s_staged.partition == NULL) {
|
||||
|
||||
@ -46,6 +46,12 @@ ota_feed_result_t ota_uart_feed(const uint8_t *data, size_t len);
|
||||
|
||||
uint32_t ota_uart_bytes_written(void);
|
||||
|
||||
/** Bytes accepted in the current session (includes buffered block). */
|
||||
uint32_t ota_uart_bytes_received(void);
|
||||
|
||||
/** Image size from OTA_START / ESP-NOW OTA_START; 0 if inactive. */
|
||||
uint32_t ota_uart_total_size(void);
|
||||
|
||||
/**
|
||||
* Flush remainder and esp_ota_end. When set_boot is false, the staged image
|
||||
* remains readable via ota_uart_get_staged_image() until ota_uart_apply_boot().
|
||||
|
||||
@ -96,8 +96,8 @@ typedef struct _alox_EspNowUnicastTestResponse {
|
||||
uint32_t seq;
|
||||
} alox_EspNowUnicastTestResponse;
|
||||
|
||||
/* Host → device: LED ring display (progress bar, digit, or clear).
|
||||
mode: 0=clear, 1=progress (0–100 %), 2=digit (0–10, same layout as firmware digit maps). */
|
||||
/* Host → device: LED ring display (progress bar, digit, clear, or blink).
|
||||
mode: 0=clear, 1=progress (0–100 %), 2=digit (0–10), 3=blink full ring. */
|
||||
typedef struct _alox_LedRingProgressRequest {
|
||||
uint32_t mode;
|
||||
/* * 0–100: fraction of ring LEDs to light (mode=progress) */
|
||||
@ -107,8 +107,12 @@ typedef struct _alox_LedRingProgressRequest {
|
||||
uint32_t r;
|
||||
uint32_t g;
|
||||
uint32_t b;
|
||||
/* * 0–255 brightness scale applied to r/g/b */
|
||||
/* * 0–255 brightness scale; 0 = firmware default (~5 %) */
|
||||
uint32_t intensity;
|
||||
/* * Pulse length in ms (mode=blink, default 350) */
|
||||
uint32_t blink_ms;
|
||||
/* * Number of pulses (mode=blink, default 1) */
|
||||
uint32_t blink_count;
|
||||
} alox_LedRingProgressRequest;
|
||||
|
||||
typedef struct _alox_LedRingProgressResponse {
|
||||
@ -237,7 +241,7 @@ extern "C" {
|
||||
#define alox_AccelDeadzoneResponse_init_default {0, 0, 0, 0}
|
||||
#define alox_EspNowUnicastTestRequest_init_default {0, 0}
|
||||
#define alox_EspNowUnicastTestResponse_init_default {0, 0}
|
||||
#define alox_LedRingProgressRequest_init_default {0, 0, 0, 0, 0, 0, 0}
|
||||
#define alox_LedRingProgressRequest_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define alox_LedRingProgressResponse_init_default {0, 0, 0, 0}
|
||||
#define alox_OtaStartPayload_init_default {0}
|
||||
#define alox_OtaPayload_init_default {0, {0, {0}}}
|
||||
@ -258,7 +262,7 @@ extern "C" {
|
||||
#define alox_AccelDeadzoneResponse_init_zero {0, 0, 0, 0}
|
||||
#define alox_EspNowUnicastTestRequest_init_zero {0, 0}
|
||||
#define alox_EspNowUnicastTestResponse_init_zero {0, 0}
|
||||
#define alox_LedRingProgressRequest_init_zero {0, 0, 0, 0, 0, 0, 0}
|
||||
#define alox_LedRingProgressRequest_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define alox_LedRingProgressResponse_init_zero {0, 0, 0, 0}
|
||||
#define alox_OtaStartPayload_init_zero {0}
|
||||
#define alox_OtaPayload_init_zero {0, {0, {0}}}
|
||||
@ -305,6 +309,8 @@ extern "C" {
|
||||
#define alox_LedRingProgressRequest_g_tag 5
|
||||
#define alox_LedRingProgressRequest_b_tag 6
|
||||
#define alox_LedRingProgressRequest_intensity_tag 7
|
||||
#define alox_LedRingProgressRequest_blink_ms_tag 8
|
||||
#define alox_LedRingProgressRequest_blink_count_tag 9
|
||||
#define alox_LedRingProgressResponse_success_tag 1
|
||||
#define alox_LedRingProgressResponse_mode_tag 2
|
||||
#define alox_LedRingProgressResponse_progress_tag 3
|
||||
@ -469,7 +475,9 @@ X(a, STATIC, SINGULAR, UINT32, digit, 3) \
|
||||
X(a, STATIC, SINGULAR, UINT32, r, 4) \
|
||||
X(a, STATIC, SINGULAR, UINT32, g, 5) \
|
||||
X(a, STATIC, SINGULAR, UINT32, b, 6) \
|
||||
X(a, STATIC, SINGULAR, UINT32, intensity, 7)
|
||||
X(a, STATIC, SINGULAR, UINT32, intensity, 7) \
|
||||
X(a, STATIC, SINGULAR, UINT32, blink_ms, 8) \
|
||||
X(a, STATIC, SINGULAR, UINT32, blink_count, 9)
|
||||
#define alox_LedRingProgressRequest_CALLBACK NULL
|
||||
#define alox_LedRingProgressRequest_DEFAULT NULL
|
||||
|
||||
@ -588,7 +596,7 @@ extern const pb_msgdesc_t alox_OtaSlaveProgressResponse_msg;
|
||||
#define alox_ClientInput_size 22
|
||||
#define alox_EspNowUnicastTestRequest_size 12
|
||||
#define alox_EspNowUnicastTestResponse_size 8
|
||||
#define alox_LedRingProgressRequest_size 42
|
||||
#define alox_LedRingProgressRequest_size 54
|
||||
#define alox_LedRingProgressResponse_size 20
|
||||
#define alox_OtaEndPayload_size 0
|
||||
#define alox_OtaPayload_size 209
|
||||
|
||||
@ -110,8 +110,8 @@ message EspNowUnicastTestResponse {
|
||||
uint32 seq = 2;
|
||||
}
|
||||
|
||||
// Host → device: LED ring display (progress bar, digit, or clear).
|
||||
// mode: 0=clear, 1=progress (0–100 %), 2=digit (0–10, same layout as firmware digit maps).
|
||||
// Host → device: LED ring display (progress bar, digit, clear, or blink).
|
||||
// mode: 0=clear, 1=progress (0–100 %), 2=digit (0–10), 3=blink full ring.
|
||||
message LedRingProgressRequest {
|
||||
uint32 mode = 1;
|
||||
/** 0–100: fraction of ring LEDs to light (mode=progress) */
|
||||
@ -121,8 +121,12 @@ message LedRingProgressRequest {
|
||||
uint32 r = 4;
|
||||
uint32 g = 5;
|
||||
uint32 b = 6;
|
||||
/** 0–255 brightness scale applied to r/g/b */
|
||||
/** 0–255 brightness scale; 0 = firmware default (~5 %) */
|
||||
uint32 intensity = 7;
|
||||
/** Pulse length in ms (mode=blink, default 350) */
|
||||
uint32 blink_ms = 8;
|
||||
/** Number of pulses (mode=blink, default 1) */
|
||||
uint32 blink_count = 9;
|
||||
}
|
||||
|
||||
message LedRingProgressResponse {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user