feat: implement modular configurable RGB LED status indicators
This commit is contained in:
parent
cfda8dbd14
commit
f9a08d8aa5
|
|
@ -2,7 +2,7 @@ set(SRCS
|
||||||
"main.c" "csi_collector.c" "stream_sender.c" "nvs_config.c"
|
"main.c" "csi_collector.c" "stream_sender.c" "nvs_config.c"
|
||||||
"edge_processing.c" "ota_update.c" "power_mgmt.c"
|
"edge_processing.c" "ota_update.c" "power_mgmt.c"
|
||||||
"wasm_runtime.c" "wasm_upload.c" "rvf_parser.c"
|
"wasm_runtime.c" "wasm_upload.c" "rvf_parser.c"
|
||||||
"mmwave_sensor.c"
|
"mmwave_sensor.c" "led_indicator.c"
|
||||||
"swarm_bridge.c"
|
"swarm_bridge.c"
|
||||||
# ADR-081 — adaptive CSI mesh firmware kernel
|
# ADR-081 — adaptive CSI mesh firmware kernel
|
||||||
"rv_radio_ops_esp32.c"
|
"rv_radio_ops_esp32.c"
|
||||||
|
|
|
||||||
|
|
@ -287,6 +287,25 @@ menu "WASM Programmable Sensing (ADR-040)"
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
|
menu "RGB Status Indicator LED"
|
||||||
|
|
||||||
|
config RGB_LED_ENABLED
|
||||||
|
bool "Enable onboard WS2812 status LED"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Compiles the LED state machine to indicate boot, wifi,
|
||||||
|
and streaming status using the ESP32's onboard RGB LED.
|
||||||
|
|
||||||
|
config RGB_LED_GPIO
|
||||||
|
int "RGB LED GPIO Pin"
|
||||||
|
depends on RGB_LED_ENABLED
|
||||||
|
default 38
|
||||||
|
help
|
||||||
|
The GPIO pin connected to the NeoPixel (WS2812/SK6812).
|
||||||
|
Commonly 38 on generic S3 boards or 48 on older boards.
|
||||||
|
|
||||||
|
endmenu
|
||||||
|
|
||||||
menu "Mock CSI (QEMU Testing)"
|
menu "Mock CSI (QEMU Testing)"
|
||||||
config CSI_MOCK_ENABLED
|
config CSI_MOCK_ENABLED
|
||||||
bool "Enable mock CSI generator (for QEMU testing)"
|
bool "Enable mock CSI generator (for QEMU testing)"
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,5 @@ dependencies:
|
||||||
## LCD touch abstraction
|
## LCD touch abstraction
|
||||||
espressif/esp_lcd_touch: "^1.0"
|
espressif/esp_lcd_touch: "^1.0"
|
||||||
|
|
||||||
## Onboard WS2812 LED Disabling
|
## Onboard WS2812 LED (Disabling & Modular Indicators)
|
||||||
espressif/led_strip: "^3.0.0"
|
espressif/led_strip: "^3.0.0"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
/**
|
||||||
|
* @file led_indicator.c
|
||||||
|
* @brief Configurable RGB LED Status Indicator for ESP32 CSI Node
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "led_indicator.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "nvs_config.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_RGB_LED_ENABLED
|
||||||
|
#include "led_strip.h"
|
||||||
|
|
||||||
|
static const char *TAG = "led_indicator";
|
||||||
|
extern nvs_config_t g_nvs_config;
|
||||||
|
|
||||||
|
static led_strip_handle_t s_led_strip = NULL;
|
||||||
|
static led_indicator_state_t s_current_state = LED_STATE_BOOTING;
|
||||||
|
static TaskHandle_t s_led_task = NULL;
|
||||||
|
|
||||||
|
static void led_task(void *arg)
|
||||||
|
{
|
||||||
|
uint8_t pulse = 0;
|
||||||
|
int8_t dir = 5;
|
||||||
|
bool toggle = false;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (!s_led_strip) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (s_current_state) {
|
||||||
|
case LED_STATE_BOOTING:
|
||||||
|
/* Solid White */
|
||||||
|
led_strip_set_pixel(s_led_strip, 0, 50, 50, 50);
|
||||||
|
led_strip_refresh(s_led_strip);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LED_STATE_WIFI_CONNECTING:
|
||||||
|
/* Fast Blinking Blue */
|
||||||
|
toggle = !toggle;
|
||||||
|
if (toggle) {
|
||||||
|
led_strip_set_pixel(s_led_strip, 0, 0, 0, 100);
|
||||||
|
} else {
|
||||||
|
led_strip_clear(s_led_strip);
|
||||||
|
}
|
||||||
|
led_strip_refresh(s_led_strip);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(200));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LED_STATE_WIFI_ERROR:
|
||||||
|
/* Solid Red */
|
||||||
|
led_strip_set_pixel(s_led_strip, 0, 100, 0, 0);
|
||||||
|
led_strip_refresh(s_led_strip);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LED_STATE_CONNECTED:
|
||||||
|
/* Slow Pulsing Green */
|
||||||
|
pulse += dir;
|
||||||
|
if (pulse >= 100 || pulse <= 0) {
|
||||||
|
dir = -dir;
|
||||||
|
}
|
||||||
|
led_strip_set_pixel(s_led_strip, 0, 0, pulse, 0);
|
||||||
|
led_strip_refresh(s_led_strip);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(50));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LED_STATE_MOCK_MODE:
|
||||||
|
/* Blinking Yellow */
|
||||||
|
toggle = !toggle;
|
||||||
|
if (toggle) {
|
||||||
|
led_strip_set_pixel(s_led_strip, 0, 100, 100, 0);
|
||||||
|
} else {
|
||||||
|
led_strip_clear(s_led_strip);
|
||||||
|
}
|
||||||
|
led_strip_refresh(s_led_strip);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(500));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void led_indicator_init(void)
|
||||||
|
{
|
||||||
|
led_strip_config_t strip_config = {
|
||||||
|
.strip_gpio_num = CONFIG_RGB_LED_GPIO,
|
||||||
|
.max_leds = 1,
|
||||||
|
.led_model = LED_MODEL_WS2812,
|
||||||
|
.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB,
|
||||||
|
.flags.invert_out = false,
|
||||||
|
};
|
||||||
|
led_strip_rmt_config_t rmt_config = {
|
||||||
|
.resolution_hz = 10 * 1000 * 1000, /* 10MHz */
|
||||||
|
.flags.with_dma = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (led_strip_new_rmt_device(&strip_config, &rmt_config, &s_led_strip) == ESP_OK) {
|
||||||
|
led_strip_clear(s_led_strip);
|
||||||
|
|
||||||
|
if (!g_nvs_config.status_led) {
|
||||||
|
ESP_LOGI(TAG, "Status LED disabled by NVS configuration. Cleared and stopped.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
xTaskCreate(led_task, "led_indicator_task", 2048, NULL, 5, &s_led_task);
|
||||||
|
ESP_LOGI(TAG, "RGB LED Indicator initialized on GPIO %d", CONFIG_RGB_LED_GPIO);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Failed to initialize RGB LED on GPIO %d", CONFIG_RGB_LED_GPIO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void led_indicator_set_state(led_indicator_state_t state)
|
||||||
|
{
|
||||||
|
if (!g_nvs_config.status_led || !s_led_strip) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s_current_state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Stubs when disabled via Kconfig */
|
||||||
|
void led_indicator_init(void) {}
|
||||||
|
void led_indicator_set_state(led_indicator_state_t state) {}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
* @file led_indicator.h
|
||||||
|
* @brief Configurable RGB LED Status Indicator for ESP32 CSI Node
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LED_INDICATOR_H
|
||||||
|
#define LED_INDICATOR_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** State of the system to indicate via the LED */
|
||||||
|
typedef enum {
|
||||||
|
LED_STATE_BOOTING = 0,
|
||||||
|
LED_STATE_WIFI_CONNECTING,
|
||||||
|
LED_STATE_CONNECTED,
|
||||||
|
LED_STATE_WIFI_ERROR,
|
||||||
|
LED_STATE_MOCK_MODE,
|
||||||
|
} led_indicator_state_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the LED indicator system if enabled via NVS and Kconfig.
|
||||||
|
* Starts the background FreeRTOS task to drive the NeoPixel animations.
|
||||||
|
*/
|
||||||
|
void led_indicator_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the current system state, changing the LED animation.
|
||||||
|
* @param state The new system state to indicate.
|
||||||
|
*/
|
||||||
|
void led_indicator_set_state(led_indicator_state_t state);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* LED_INDICATOR_H */
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
#include "mock_csi.h"
|
#include "mock_csi.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "led_indicator.h"
|
||||||
#include "esp_timer.h"
|
#include "esp_timer.h"
|
||||||
|
|
||||||
static const char *TAG = "main";
|
static const char *TAG = "main";
|
||||||
|
|
@ -60,19 +61,23 @@ static void event_handler(void *arg, esp_event_base_t event_base,
|
||||||
int32_t event_id, void *event_data)
|
int32_t event_id, void *event_data)
|
||||||
{
|
{
|
||||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||||
|
led_indicator_set_state(LED_STATE_WIFI_CONNECTING);
|
||||||
esp_wifi_connect();
|
esp_wifi_connect();
|
||||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||||
if (s_retry_num < MAX_RETRY) {
|
if (s_retry_num < MAX_RETRY) {
|
||||||
|
led_indicator_set_state(LED_STATE_WIFI_CONNECTING);
|
||||||
esp_wifi_connect();
|
esp_wifi_connect();
|
||||||
s_retry_num++;
|
s_retry_num++;
|
||||||
ESP_LOGI(TAG, "Retrying WiFi connection (%d/%d)", s_retry_num, MAX_RETRY);
|
ESP_LOGI(TAG, "Retrying WiFi connection (%d/%d)", s_retry_num, MAX_RETRY);
|
||||||
} else {
|
} else {
|
||||||
|
led_indicator_set_state(LED_STATE_WIFI_ERROR);
|
||||||
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||||
}
|
}
|
||||||
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||||
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
|
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
|
||||||
ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
|
ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
|
||||||
s_retry_num = 0;
|
s_retry_num = 0;
|
||||||
|
led_indicator_set_state(LED_STATE_CONNECTED);
|
||||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -150,10 +155,18 @@ void app_main(void)
|
||||||
ESP_LOGI(TAG, "ESP32-S3 CSI Node (ADR-018) — v%s — Node ID: %d",
|
ESP_LOGI(TAG, "ESP32-S3 CSI Node (ADR-018) — v%s — Node ID: %d",
|
||||||
app_desc->version, g_nvs_config.node_id);
|
app_desc->version, g_nvs_config.node_id);
|
||||||
|
|
||||||
/* Turn off onboard WS2812 LED on GPIO 38 */
|
#ifdef CONFIG_RGB_LED_ENABLED
|
||||||
|
/* Initialize modular RGB indicator (which inherently claims and clears the LED) */
|
||||||
|
led_indicator_init();
|
||||||
|
led_indicator_set_state(LED_STATE_BOOTING);
|
||||||
|
#else
|
||||||
|
/* Fallback #273 disable logic: explicitly turn off WS2812 LED to prevent Wi-Fi interference */
|
||||||
|
#ifndef CONFIG_RGB_LED_GPIO
|
||||||
|
#define CONFIG_RGB_LED_GPIO 38
|
||||||
|
#endif
|
||||||
led_strip_handle_t led_strip;
|
led_strip_handle_t led_strip;
|
||||||
led_strip_config_t strip_config = {
|
led_strip_config_t strip_config = {
|
||||||
.strip_gpio_num = 38,
|
.strip_gpio_num = CONFIG_RGB_LED_GPIO,
|
||||||
.max_leds = 1,
|
.max_leds = 1,
|
||||||
.led_model = LED_MODEL_WS2812,
|
.led_model = LED_MODEL_WS2812,
|
||||||
.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB,
|
.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB,
|
||||||
|
|
@ -166,6 +179,7 @@ void app_main(void)
|
||||||
if (led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip) == ESP_OK) {
|
if (led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip) == ESP_OK) {
|
||||||
led_strip_clear(led_strip);
|
led_strip_clear(led_strip);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Initialize WiFi STA (skip entirely under QEMU mock — no RF hardware) */
|
/* Initialize WiFi STA (skip entirely under QEMU mock — no RF hardware) */
|
||||||
#ifndef CONFIG_CSI_MOCK_SKIP_WIFI_CONNECT
|
#ifndef CONFIG_CSI_MOCK_SKIP_WIFI_CONNECT
|
||||||
|
|
@ -187,6 +201,7 @@ void app_main(void)
|
||||||
/* Initialize CSI collection */
|
/* Initialize CSI collection */
|
||||||
#ifdef CONFIG_CSI_MOCK_ENABLED
|
#ifdef CONFIG_CSI_MOCK_ENABLED
|
||||||
/* ADR-061: Start mock CSI generator (replaces real WiFi CSI in QEMU) */
|
/* ADR-061: Start mock CSI generator (replaces real WiFi CSI in QEMU) */
|
||||||
|
led_indicator_set_state(LED_STATE_MOCK_MODE);
|
||||||
esp_err_t mock_ret = mock_csi_init(CONFIG_CSI_MOCK_SCENARIO);
|
esp_err_t mock_ret = mock_csi_init(CONFIG_CSI_MOCK_SCENARIO);
|
||||||
if (mock_ret != ESP_OK) {
|
if (mock_ret != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Mock CSI init failed: %s", esp_err_to_name(mock_ret));
|
ESP_LOGE(TAG, "Mock CSI init failed: %s", esp_err_to_name(mock_ret));
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,9 @@ void nvs_config_load(nvs_config_t *cfg)
|
||||||
cfg->filter_mac_set = 0;
|
cfg->filter_mac_set = 0;
|
||||||
memset(cfg->filter_mac, 0, 6);
|
memset(cfg->filter_mac, 0, 6);
|
||||||
|
|
||||||
|
/* Indicator defaults */
|
||||||
|
cfg->status_led = 1;
|
||||||
|
|
||||||
/* Try to override from NVS */
|
/* Try to override from NVS */
|
||||||
nvs_handle_t handle;
|
nvs_handle_t handle;
|
||||||
esp_err_t err = nvs_open("csi_cfg", NVS_READONLY, &handle);
|
esp_err_t err = nvs_open("csi_cfg", NVS_READONLY, &handle);
|
||||||
|
|
@ -322,6 +325,13 @@ void nvs_config_load(nvs_config_t *cfg)
|
||||||
cfg->swarm_ingest_sec = 5;
|
cfg->swarm_ingest_sec = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Indicator LED override */
|
||||||
|
uint8_t status_led_val;
|
||||||
|
if (nvs_get_u8(handle, "status_led", &status_led_val) == ESP_OK) {
|
||||||
|
cfg->status_led = status_led_val ? 1 : 0;
|
||||||
|
ESP_LOGI(TAG, "NVS override: status_led=%u", (unsigned)cfg->status_led);
|
||||||
|
}
|
||||||
|
|
||||||
/* Validate tdm_slot_index < tdm_node_count */
|
/* Validate tdm_slot_index < tdm_node_count */
|
||||||
if (cfg->tdm_slot_index >= cfg->tdm_node_count) {
|
if (cfg->tdm_slot_index >= cfg->tdm_node_count) {
|
||||||
ESP_LOGW(TAG, "tdm_slot_index=%u >= tdm_node_count=%u, clamping to 0",
|
ESP_LOGW(TAG, "tdm_slot_index=%u >= tdm_node_count=%u, clamping to 0",
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,9 @@ typedef struct {
|
||||||
char zone_name[16]; /**< Zone name for this node (e.g. "lobby"). */
|
char zone_name[16]; /**< Zone name for this node (e.g. "lobby"). */
|
||||||
uint16_t swarm_heartbeat_sec; /**< Heartbeat interval (seconds, default 30). */
|
uint16_t swarm_heartbeat_sec; /**< Heartbeat interval (seconds, default 30). */
|
||||||
uint16_t swarm_ingest_sec; /**< Vector ingest interval (seconds, default 5). */
|
uint16_t swarm_ingest_sec; /**< Vector ingest interval (seconds, default 5). */
|
||||||
|
|
||||||
|
/* Generic Utility Settings */
|
||||||
|
uint8_t status_led; /**< 1 to enable the RGB status LED, 0 to disable. */
|
||||||
} nvs_config_t;
|
} nvs_config_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ CONFIG_VALUE_CHECKS = [
|
||||||
("zone", lambda value: value is not None),
|
("zone", lambda value: value is not None),
|
||||||
("swarm_hb", lambda value: value is not None),
|
("swarm_hb", lambda value: value is not None),
|
||||||
("swarm_ingest", lambda value: value is not None),
|
("swarm_ingest", lambda value: value is not None),
|
||||||
|
("status_led", lambda value: value is not None),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -108,6 +109,7 @@ MERGEABLE_ATTRS = [
|
||||||
"channel", "filter_mac",
|
"channel", "filter_mac",
|
||||||
"hop_channels", "hop_dwell",
|
"hop_channels", "hop_dwell",
|
||||||
"seed_url", "seed_token", "zone", "swarm_hb", "swarm_ingest",
|
"seed_url", "seed_token", "zone", "swarm_hb", "swarm_ingest",
|
||||||
|
"status_led",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -208,11 +210,17 @@ def build_nvs_csv(args):
|
||||||
writer.writerow(["vital_int", "data", "u16", str(args.vital_int)])
|
writer.writerow(["vital_int", "data", "u16", str(args.vital_int)])
|
||||||
if args.subk_count is not None:
|
if args.subk_count is not None:
|
||||||
writer.writerow(["subk_count", "data", "u8", str(args.subk_count)])
|
writer.writerow(["subk_count", "data", "u8", str(args.subk_count)])
|
||||||
|
|
||||||
|
# Generic Utility Settings
|
||||||
|
if args.status_led is not None:
|
||||||
|
writer.writerow(["status_led", "data", "u8", str(args.status_led)])
|
||||||
|
|
||||||
# ADR-060: Channel override and MAC filter
|
# ADR-060: Channel override and MAC filter
|
||||||
if args.channel is not None:
|
if args.channel is not None:
|
||||||
writer.writerow(["csi_channel", "data", "u8", str(args.channel)])
|
writer.writerow(["csi_channel", "data", "u8", str(args.channel)])
|
||||||
if args.filter_mac is not None:
|
if args.filter_mac is not None:
|
||||||
mac_bytes = bytes(int(b, 16) for b in args.filter_mac.split(":"))
|
mac_str = str(args.filter_mac)
|
||||||
|
mac_bytes = bytes(int(b, 16) for b in mac_str.split(":")) # pyre-ignore
|
||||||
# NVS blob: write as hex-encoded string for CSV compatibility
|
# NVS blob: write as hex-encoded string for CSV compatibility
|
||||||
writer.writerow(["filter_mac", "data", "hex2bin", mac_bytes.hex()])
|
writer.writerow(["filter_mac", "data", "hex2bin", mac_bytes.hex()])
|
||||||
# ADR-073: Multi-frequency channel hopping
|
# ADR-073: Multi-frequency channel hopping
|
||||||
|
|
@ -352,6 +360,8 @@ def main():
|
||||||
parser.add_argument("--zone", type=str, help="Zone name for this node (e.g. lobby, hallway)")
|
parser.add_argument("--zone", type=str, help="Zone name for this node (e.g. lobby, hallway)")
|
||||||
parser.add_argument("--swarm-hb", type=int, help="Swarm heartbeat interval in seconds (default 30)")
|
parser.add_argument("--swarm-hb", type=int, help="Swarm heartbeat interval in seconds (default 30)")
|
||||||
parser.add_argument("--swarm-ingest", type=int, help="Swarm vector ingest interval in seconds (default 5)")
|
parser.add_argument("--swarm-ingest", type=int, help="Swarm vector ingest interval in seconds (default 5)")
|
||||||
|
# Generic Utility
|
||||||
|
parser.add_argument("--status-led", type=int, choices=[0, 1], help="Enable (1) or disable (0) RGB status indicator (default: 1)")
|
||||||
parser.add_argument("--dry-run", action="store_true", help="Generate NVS binary but don't flash")
|
parser.add_argument("--dry-run", action="store_true", help="Generate NVS binary but don't flash")
|
||||||
parser.add_argument("--force-partial", action="store_true",
|
parser.add_argument("--force-partial", action="store_true",
|
||||||
help="[deprecated since #391/#574] Suppress the missing-WiFi-trio "
|
help="[deprecated since #391/#574] Suppress the missing-WiFi-trio "
|
||||||
|
|
@ -432,7 +442,7 @@ def main():
|
||||||
parser.error(f"--filter-mac must be in AA:BB:CC:DD:EE:FF format, got '{args.filter_mac}'")
|
parser.error(f"--filter-mac must be in AA:BB:CC:DD:EE:FF format, got '{args.filter_mac}'")
|
||||||
try:
|
try:
|
||||||
for p in parts:
|
for p in parts:
|
||||||
val = int(p, 16)
|
val = int(p, 16) # pyre-ignore
|
||||||
if val < 0 or val > 255:
|
if val < 0 or val > 255:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
|
@ -476,6 +486,8 @@ def main():
|
||||||
print(f" Swarm HB: {args.swarm_hb}s")
|
print(f" Swarm HB: {args.swarm_hb}s")
|
||||||
if args.swarm_ingest is not None:
|
if args.swarm_ingest is not None:
|
||||||
print(f" Swarm Ingest: {args.swarm_ingest}s")
|
print(f" Swarm Ingest: {args.swarm_ingest}s")
|
||||||
|
if args.status_led is not None:
|
||||||
|
print(f" Status LED: {'On (1)' if args.status_led else 'Off (0)'}")
|
||||||
|
|
||||||
csv_content = build_nvs_csv(args)
|
csv_content = build_nvs_csv(args)
|
||||||
|
|
||||||
|
|
@ -493,6 +505,12 @@ def main():
|
||||||
f"{fallback_path} nvs.bin 0x6000", file=sys.stderr)
|
f"{fallback_path} nvs.bin 0x6000", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
if not nvs_bin:
|
||||||
|
print("Failed to generate NVS binary", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
assert isinstance(nvs_bin, bytes)
|
||||||
|
|
||||||
if args.dry_run:
|
if args.dry_run:
|
||||||
out = "nvs_provision.bin"
|
out = "nvs_provision.bin"
|
||||||
with open(out, "wb") as f:
|
with open(out, "wb") as f:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue