diff --git a/src/config.c b/src/config.c index a9f80a8..fece7f6 100644 --- a/src/config.c +++ b/src/config.c @@ -6,7 +6,7 @@ #define NVS_ID_CONFIG 1 #define CONFIG_ID 0xDEADBEEF -#define CONFIG_VERSION 2 +#define CONFIG_VERSION 7 static struct nvs_fs nvs; static app_config_t ram_config; @@ -18,6 +18,17 @@ static const app_config_t default_config = { .phase_a_ms = (20 * 60 * 1000), .phase_b_ms = (20 * 1000), }, + .light_config = { + .min = 5, + .max = 600, + .gain = 0b00000100, + }, + .color = { + .r = 1, + .g = 0.2, + .b = 0, + }, + .color_duration = 2000, }; int config_init(void) diff --git a/src/config.h b/src/config.h index 1c4cca5..7af0d7c 100644 --- a/src/config.h +++ b/src/config.h @@ -4,11 +4,21 @@ #include #include #include "mode.h" +#include "led.h" + +struct light_config_t { + uint16_t min; + uint16_t max; + uint8_t gain; +}; typedef struct { uint32_t id; uint16_t version; - struct mode_config mode_config; + struct mode_config_t mode_config; + struct light_config_t light_config; + struct color_t color; + uint16_t color_duration; } app_config_t; int config_init(void); diff --git a/src/debug_shell.c b/src/debug_shell.c index 081b032..495201e 100644 --- a/src/debug_shell.c +++ b/src/debug_shell.c @@ -88,11 +88,24 @@ static int cmd_dbg_leds_fade_to(const struct shell *sh, size_t argc, char **argv atoi(argv[4]) < 0 ? -1.0f : atoi(argv[4]) / 255.0f); } +static int cmd_dbg_led_color(const struct shell *sh, size_t argc, char **argv) +{ + struct color_t color; + if (led_color_from_str(argv[1], &color) != 0) { + shell_error(sh, "Unknown color: %s", argv[1]); + return -EINVAL; + } + leds_set_all(color.r, color.g, color.b); + leds_update(); + return 0; +} + SHELL_STATIC_SUBCMD_SET_CREATE(debug_leds_cmds, SHELL_CMD(all, NULL, "Set all LEDs .", cmd_dbg_leds_all), SHELL_CMD(clear, NULL, "Clear all LEDs.", cmd_dbg_leds_clear), SHELL_CMD(fade, NULL, "Fade in/out .", cmd_dbg_leds_fade), SHELL_CMD(fade_to, NULL, "Fade to color .", cmd_dbg_leds_fade_to), + SHELL_CMD(color, NULL, "Set to color . eg. yellow", cmd_dbg_led_color), SHELL_SUBCMD_SET_END ); diff --git a/src/helpers.c b/src/helpers.c new file mode 100644 index 0000000..939ec06 --- /dev/null +++ b/src/helpers.c @@ -0,0 +1,6 @@ +#include "helpers.h" + + +float map(float x, float in_min, float in_max, float out_min, float out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} \ No newline at end of file diff --git a/src/helpers.h b/src/helpers.h new file mode 100644 index 0000000..b735741 --- /dev/null +++ b/src/helpers.h @@ -0,0 +1,6 @@ +#ifndef HELPERS_H +#define HELPERS_H + +float map(float x, float in_min, float in_max, float out_min, float out_max); + +#endif // HELPERS_H \ No newline at end of file diff --git a/src/led.c b/src/led.c index 6d1a714..bb43b1b 100644 --- a/src/led.c +++ b/src/led.c @@ -1,4 +1,7 @@ #include "led.h" +#include "light.h" +#include "config.h" +#include "helpers.h" #include #include @@ -15,11 +18,15 @@ LOG_MODULE_REGISTER(leds, LOG_LEVEL_INF); static const struct device *const strip = DEVICE_DT_GET(STRIP_NODE); static struct led_rgb pixels[STRIP_NUM_PIXELS]; -/* Saved strip color for fade_to interpolation */ +/* Saved strip color for fade_to interpolation (logical, pre-brightness) */ static float saved_r = 0.0f; static float saved_g = 0.0f; static float saved_b = 0.0f; +/* Light sensor config loaded during init */ +static uint16_t light_min = 0; +static uint16_t light_max = 1023; + #define NR_LEDS 8 #define PWM_RESOLUTION 255 @@ -36,6 +43,88 @@ static const struct pwm_dt_spec *leds[NR_LEDS] = { &led1, &led2, &led3, &led4, &led5, &led6, &led7, &led8 }; +static const led_color_entry_t color_table[] = { + { "red", { LED_COLOR_RED } }, + { "green", { LED_COLOR_GREEN } }, + { "blue", { LED_COLOR_BLUE } }, + { "yellow", { LED_COLOR_YELLOW } }, + { "cyan", { LED_COLOR_CYAN } }, + { "magenta", { LED_COLOR_MAGENTA } }, + { "white", { LED_COLOR_WHITE } }, + { "warm_white", { LED_COLOR_WARM_WHITE } }, + { "cold_white", { LED_COLOR_COLD_WHITE } }, + { "black", { LED_COLOR_BLACK } }, + { "orange", { LED_COLOR_ORANGE } }, + { "deep_orange", { LED_COLOR_DEEP_ORANGE } }, + { "amber", { LED_COLOR_AMBER } }, + { "coral", { LED_COLOR_CORAL } }, + { "tomato", { LED_COLOR_TOMATO } }, + { "crimson", { LED_COLOR_CRIMSON } }, + { "scarlet", { LED_COLOR_SCARLET } }, + { "pink", { LED_COLOR_PINK } }, + { "hot_pink", { LED_COLOR_HOT_PINK } }, + { "deep_pink", { LED_COLOR_DEEP_PINK } }, + { "purple", { LED_COLOR_PURPLE } }, + { "deep_purple", { LED_COLOR_DEEP_PURPLE } }, + { "violet", { LED_COLOR_VIOLET } }, + { "indigo", { LED_COLOR_INDIGO } }, + { "lavender", { LED_COLOR_LAVENDER } }, + { "lime", { LED_COLOR_LIME } }, + { "chartreuse", { LED_COLOR_CHARTREUSE } }, + { "mint", { LED_COLOR_MINT } }, + { "teal", { LED_COLOR_TEAL } }, + { "forest_green", { LED_COLOR_FOREST_GREEN } }, + { "olive", { LED_COLOR_OLIVE } }, + { "sky_blue", { LED_COLOR_SKY_BLUE } }, + { "deep_sky_blue", { LED_COLOR_DEEP_SKY_BLUE } }, + { "dodger_blue", { LED_COLOR_DODGER_BLUE } }, + { "royal_blue", { LED_COLOR_ROYAL_BLUE } }, + { "navy", { LED_COLOR_NAVY } }, + { "steel_blue", { LED_COLOR_STEEL_BLUE } }, + { "ice_blue", { LED_COLOR_ICE_BLUE } }, + { "gold", { LED_COLOR_GOLD } }, + { "silver", { LED_COLOR_SILVER } }, +}; +const int color_table_size = ARRAY_SIZE(color_table); + +int led_color_from_str(const char *name, struct color_t *out) +{ + for (int i = 0; i < ARRAY_SIZE(color_table); i++) { + if (strcasecmp(color_table[i].name, name) == 0) { + *out = color_table[i].color; + return 0; + } + } + return -EINVAL; +} + +const char *led_color_to_str(const struct color_t *color) +{ + for (int i = 0; i < ARRAY_SIZE(color_table); i++) { + if (color_table[i].color.r == color->r && + color_table[i].color.g == color->g && + color_table[i].color.b == color->b) { + return color_table[i].name; + } + } + return "custom"; +} + +int led_color_count(void) +{ + return ARRAY_SIZE(color_table); +} + +const led_color_entry_t *led_color_get(int index) +{ + if (index < 0 || index >= ARRAY_SIZE(color_table)) { + return NULL; + } + return &color_table[index]; +} + +/* ---------- PWM helpers ---------- */ + static int set_pwm(const struct pwm_dt_spec *dev, uint32_t value, uint32_t max_brightness) { if (value <= max_brightness && max_brightness > 0) { @@ -45,36 +134,96 @@ static int set_pwm(const struct pwm_dt_spec *dev, uint32_t value, uint32_t max_b return -EINVAL; } +/* ---------- brightness helpers ---------- */ + +/** + * Read the light sensor and return a brightness multiplier in [0.0, 1.0]. + * The raw value is mapped from [light_min, light_max] → [0.0, 1.0] and + * clamped so out-of-range readings never over- or under-drive the LEDs. + */ +static float get_brightness(void) +{ + struct light_data_t data; + light_read(&data); + float b = map((float)data.value, + (float)light_min, (float)light_max, + 0.0f, 1.0f); + if (b < 0.0f) b = 0.0f; + if (b > 1.0f) b = 1.0f; + return b; +} + +static float get_brightness_pwm(void) +{ + for (int j = 0; j < NR_LEDS; j++) { + set_pwm(leds[j], 0, PWM_RESOLUTION); + } + return get_brightness(); +} + +/** + * Like set_pwm() but scales `value` by the current ambient brightness before + * writing. All PWM-facing call sites go through here. + */ +static int set_pwm_bright(const struct pwm_dt_spec *dev, uint32_t value, + uint32_t max_brightness, float brightness) +{ + uint32_t scaled = (uint32_t)((float)value * brightness); + return set_pwm(dev, scaled, max_brightness); +} + +/* ---------- init ---------- */ + int led_init(void) { - for (int i = 0; i < NR_LEDS; i++) { - if (!device_is_ready(leds[i]->dev)) { - LOG_ERR("PWM LED %d not ready", i); - return -ENODEV; - } - } - LOG_INF("PWM LEDs initialized"); + /* Load light config so brightness mapping is ready before first use */ + app_config_t cfg; + config_get(&cfg); + light_min = cfg.light_config.min; + light_max = cfg.light_config.max; - if (!device_is_ready(strip)) { - LOG_ERR("LED strip device not ready"); - return -ENODEV; - } - LOG_INF("LED strip initialized with %d pixels", STRIP_NUM_PIXELS); + /* Sanity-check: inverted or zero range would make map() degenerate */ + if (light_min >= light_max) { + LOG_WRN("Invalid light_config range [%u, %u], using defaults", + light_min, light_max); + light_min = 0; + light_max = 1023; + } + LOG_INF("Light config: min=%u max=%u", light_min, light_max); + + for (int i = 0; i < NR_LEDS; i++) { + if (!device_is_ready(leds[i]->dev)) { + LOG_ERR("PWM LED %d not ready", i); + return -ENODEV; + } + set_pwm(leds[i], 0, PWM_RESOLUTION); + } + LOG_INF("PWM LEDs initialized"); + + if (!device_is_ready(strip)) { + LOG_ERR("LED strip device not ready"); + return -ENODEV; + } + LOG_INF("LED strip initialized with %d pixels", STRIP_NUM_PIXELS); + leds_clear(); return 0; } -/* Fade all PWM LEDs in then out over duration ms */ +/* ---------- PWM LED effects ---------- */ + int led_fade(uint32_t duration) { uint32_t step = (duration * 1000) / (PWM_RESOLUTION * 2); for (int i = 0; i < PWM_RESOLUTION; i++) { - for (int j = 0; j < NR_LEDS; j++) { set_pwm(leds[j], i, PWM_RESOLUTION); } + float b = get_brightness(); + for (int j = 0; j < NR_LEDS; j++) { set_pwm_bright(leds[j], i, PWM_RESOLUTION, b); } k_sleep(K_USEC(step)); } for (int i = 0; i < PWM_RESOLUTION; i++) { - for (int j = 0; j < NR_LEDS; j++) { set_pwm(leds[j], PWM_RESOLUTION - i - 1, PWM_RESOLUTION); } + float b = get_brightness(); + for (int j = 0; j < NR_LEDS; j++) { set_pwm_bright(leds[j], PWM_RESOLUTION - i - 1, PWM_RESOLUTION, b); } k_sleep(K_USEC(step)); } return 0; @@ -84,7 +233,8 @@ int led_fade_in(uint32_t duration) { uint32_t step = (duration * 1000) / PWM_RESOLUTION; for (int i = 0; i < PWM_RESOLUTION; i++) { - for (int j = 0; j < NR_LEDS; j++) { set_pwm(leds[j], i, PWM_RESOLUTION); } + float b = get_brightness(); + for (int j = 0; j < NR_LEDS; j++) { set_pwm_bright(leds[j], i, PWM_RESOLUTION, b); } k_sleep(K_USEC(step)); } return 0; @@ -94,68 +244,79 @@ int led_fade_out(uint32_t duration) { uint32_t step = (duration * 1000) / PWM_RESOLUTION; for (int i = 0; i < PWM_RESOLUTION; i++) { - for (int j = 0; j < NR_LEDS; j++) { set_pwm(leds[j], PWM_RESOLUTION - i - 1, PWM_RESOLUTION); } + float b = get_brightness(); + for (int j = 0; j < NR_LEDS; j++) { set_pwm_bright(leds[j], PWM_RESOLUTION - i - 1, PWM_RESOLUTION, b); } k_sleep(K_USEC(step)); } return 0; } -/* Light N+fraction LEDs to represent progress [0.0, 1.0] */ int led_set_progress(float progress) { + float b = get_brightness(); /* LEDs blanked; write real values below */ uint32_t total = (uint32_t)(progress * NR_LEDS * PWM_RESOLUTION); for (int i = 0; i < NR_LEDS; i++) { + uint32_t val; if (total > PWM_RESOLUTION) { - set_pwm(leds[i], PWM_RESOLUTION, PWM_RESOLUTION); + val = PWM_RESOLUTION; total -= PWM_RESOLUTION; } else { - set_pwm(leds[i], total, PWM_RESOLUTION); + val = total; total = 0; } + set_pwm_bright(leds[i], val, PWM_RESOLUTION, b); } return 0; } -/* --- Addressable LED strip --- */ +/* ---------- addressable strip ---------- */ + +/** + * Write r/g/b to a pixel after applying the ambient brightness multiplier. + * `r`, `g`, `b` are logical values in [0.0, 1.0]; `brightness` scales them. + */ +static void set_pixel_bright(int idx, float r, float g, float b, float brightness) +{ + pixels[idx].r = (uint8_t)(r * brightness * STRIP_MAX_BRIGHTNESS); + pixels[idx].g = (uint8_t)(g * brightness * STRIP_MAX_BRIGHTNESS); + pixels[idx].b = (uint8_t)(b * brightness * STRIP_MAX_BRIGHTNESS); +} int leds_set_color(uint8_t led_index, float r, float g, float b) { - if (led_index >= STRIP_NUM_PIXELS) { - LOG_ERR("LED index %d out of range (max %d)", led_index, STRIP_NUM_PIXELS - 1); - return -EINVAL; - } - pixels[led_index].r = r * STRIP_MAX_BRIGHTNESS; - pixels[led_index].g = g * STRIP_MAX_BRIGHTNESS; - pixels[led_index].b = b * STRIP_MAX_BRIGHTNESS; - return 0; + if (led_index >= STRIP_NUM_PIXELS) { + LOG_ERR("LED index %d out of range (max %d)", led_index, STRIP_NUM_PIXELS - 1); + return -EINVAL; + } + set_pixel_bright(led_index, r, g, b, get_brightness()); + return 0; } int leds_set_all(float r, float g, float b) { - for (int i = 0; i < STRIP_NUM_PIXELS; i++) { - pixels[i].r = r * STRIP_MAX_BRIGHTNESS; - pixels[i].g = g * STRIP_MAX_BRIGHTNESS; - pixels[i].b = b * STRIP_MAX_BRIGHTNESS; - } - saved_r = r; - saved_g = g; - saved_b = b; - return 0; + float brightness = get_brightness(); + for (int i = 0; i < STRIP_NUM_PIXELS; i++) { + set_pixel_bright(i, r, g, b, brightness); + } + saved_r = r; + saved_g = g; + saved_b = b; + return 0; } int leds_update(void) { - int ret = led_strip_update_rgb(strip, pixels, STRIP_NUM_PIXELS); - if (ret) { - LOG_ERR("Failed to update LED strip: %d", ret); - } - return ret; + int ret = led_strip_update_rgb(strip, pixels, STRIP_NUM_PIXELS); + if (ret) { + LOG_ERR("Failed to update LED strip: %d", ret); + } + return ret; } int leds_clear(void) { - return leds_set_all(0, 0, 0) || leds_update(); + return leds_set_all(0, 0, 0) || leds_update(); } int leds_fade(uint32_t duration, float r, float g, float b) @@ -174,10 +335,10 @@ int leds_fade(uint32_t duration, float r, float g, float b) leds_update(); k_sleep(K_USEC(step)); } - saved_r = 0.0f; - saved_g = 0.0f; - saved_b = 0.0f; - return 0; + saved_r = 0.0f; + saved_g = 0.0f; + saved_b = 0.0f; + return 0; } int leds_fade_in(uint32_t duration, float r, float g, float b) @@ -189,8 +350,8 @@ int leds_fade_in(uint32_t duration, float r, float g, float b) leds_update(); k_sleep(K_USEC(step)); } - saved_r = r; saved_g = g; saved_b = b; - return 0; + saved_r = r; saved_g = g; saved_b = b; + return 0; } int leds_fade_out(uint32_t duration, float r, float g, float b) @@ -202,11 +363,10 @@ int leds_fade_out(uint32_t duration, float r, float g, float b) leds_update(); k_sleep(K_USEC(step)); } - saved_r = 0.0f; saved_g = 0.0f; saved_b = 0.0f; - return 0; + saved_r = 0.0f; saved_g = 0.0f; saved_b = 0.0f; + return 0; } -/* Fade from current saved color to target; pass -1.0 to keep a channel unchanged */ int leds_fade_to(uint32_t duration, float r, float g, float b) { float target_r = (r < 0.0f) ? saved_r : r; @@ -228,4 +388,4 @@ int leds_fade_to(uint32_t duration, float r, float g, float b) saved_g = target_g; saved_b = target_b; return 0; -} +} \ No newline at end of file diff --git a/src/led.h b/src/led.h index 4af8922..61019eb 100644 --- a/src/led.h +++ b/src/led.h @@ -3,12 +3,92 @@ #include +/* ---------- predefined colors (r, g, b) in [0.0, 1.0] ---------- */ + +/* Primaries */ +#define LED_COLOR_RED 1.0f, 0.0f, 0.0f +#define LED_COLOR_GREEN 0.0f, 1.0f, 0.0f +#define LED_COLOR_BLUE 0.0f, 0.0f, 1.0f + +/* Secondaries */ +#define LED_COLOR_YELLOW 1.0f, 1.0f, 0.0f +#define LED_COLOR_CYAN 0.0f, 1.0f, 1.0f +#define LED_COLOR_MAGENTA 1.0f, 0.0f, 1.0f + +/* Whites */ +#define LED_COLOR_WHITE 1.0f, 1.0f, 1.0f +#define LED_COLOR_WARM_WHITE 1.0f, 0.89f, 0.70f +#define LED_COLOR_COLD_WHITE 0.90f, 0.95f, 1.0f +#define LED_COLOR_BLACK 0.0f, 0.0f, 0.0f + +/* Oranges & reds */ +#define LED_COLOR_ORANGE 1.0f, 0.50f, 0.0f +#define LED_COLOR_DEEP_ORANGE 1.0f, 0.25f, 0.0f +#define LED_COLOR_AMBER 1.0f, 0.75f, 0.0f +#define LED_COLOR_CORAL 1.0f, 0.31f, 0.19f +#define LED_COLOR_TOMATO 1.0f, 0.27f, 0.23f +#define LED_COLOR_CRIMSON 0.86f, 0.08f, 0.24f +#define LED_COLOR_SCARLET 1.0f, 0.14f, 0.0f + +/* Pinks & purples */ +#define LED_COLOR_PINK 1.0f, 0.40f, 0.70f +#define LED_COLOR_HOT_PINK 1.0f, 0.07f, 0.58f +#define LED_COLOR_DEEP_PINK 1.0f, 0.08f, 0.58f +#define LED_COLOR_PURPLE 0.50f, 0.0f, 1.0f +#define LED_COLOR_DEEP_PURPLE 0.40f, 0.0f, 0.80f +#define LED_COLOR_VIOLET 0.56f, 0.0f, 1.0f +#define LED_COLOR_INDIGO 0.29f, 0.0f, 0.51f +#define LED_COLOR_LAVENDER 0.71f, 0.49f, 0.86f + +/* Greens */ +#define LED_COLOR_LIME 0.75f, 1.0f, 0.0f +#define LED_COLOR_CHARTREUSE 0.50f, 1.0f, 0.0f +#define LED_COLOR_MINT 0.24f, 1.0f, 0.49f +#define LED_COLOR_TEAL 0.0f, 0.50f, 0.50f +#define LED_COLOR_FOREST_GREEN 0.13f, 0.55f, 0.13f +#define LED_COLOR_OLIVE 0.50f, 0.50f, 0.0f + +/* Blues */ +#define LED_COLOR_SKY_BLUE 0.53f, 0.81f, 0.98f +#define LED_COLOR_DEEP_SKY_BLUE 0.0f, 0.75f, 1.0f +#define LED_COLOR_DODGER_BLUE 0.12f, 0.56f, 1.0f +#define LED_COLOR_ROYAL_BLUE 0.25f, 0.41f, 0.88f +#define LED_COLOR_NAVY 0.0f, 0.0f, 0.50f +#define LED_COLOR_STEEL_BLUE 0.27f, 0.51f, 0.71f +#define LED_COLOR_ICE_BLUE 0.60f, 0.85f, 1.0f + +/* Neutrals */ +#define LED_COLOR_GOLD 1.0f, 0.84f, 0.0f +#define LED_COLOR_SILVER 0.75f, 0.75f, 0.75f + +/* ---------- color lookup by name ---------- */ + +struct color_t { + float r; + float g; + float b; +}; + +typedef struct { + const char *name; + struct color_t color; +} led_color_entry_t; + +int led_color_from_str(const char *name, struct color_t *out); +const char *led_color_to_str(const struct color_t *color); +int led_color_count(void); +const led_color_entry_t *led_color_get(int index); + +/* ---------- PWM LED functions ---------- */ + int led_init(void); int led_fade(uint32_t duration); int led_fade_in(uint32_t duration); int led_fade_out(uint32_t duration); int led_set_progress(float progress); +/* ---------- addressable LED strip functions ---------- */ + int leds_set_color(uint8_t led_index, float r, float g, float b); int leds_set_all(float r, float g, float b); int leds_update(void); @@ -18,4 +98,4 @@ int leds_fade_in(uint32_t duration, float r, float g, float b); int leds_fade_out(uint32_t duration, float r, float g, float b); int leds_fade_to(uint32_t duration, float r, float g, float b); -#endif // LED_H +#endif // LED_H \ No newline at end of file diff --git a/src/light.c b/src/light.c index 6ab1058..a07e637 100644 --- a/src/light.c +++ b/src/light.c @@ -1,5 +1,7 @@ #include "light.h" #include "zbus_channels.h" +#include "led.h" +#include "config.h" #include #include @@ -15,6 +17,8 @@ static const uint8_t i2c_address = 0x38; static const struct device *const i2c_dev = DEVICE_DT_GET(DT_ALIAS(i2c_1)); +static struct light_config_t cfg; + int light_init(void) { if (!device_is_ready(i2c_dev)) { @@ -22,10 +26,16 @@ int light_init(void) return -ENODEV; } + app_config_t app_cfg; + config_get(&app_cfg); + cfg = app_cfg.light_config; + const uint8_t RESET[2] = {0x00, 0b00000001}; // reset + const uint8_t ALS_GAIN[2] = {0x04, cfg.gain}; // configure ALS gain const uint8_t ENABLE_ALS[2] = {0x00, 0b00000001}; // enable ALS i2c_write(i2c_dev, RESET, sizeof(RESET), i2c_address); k_sleep(K_MSEC(100)); + i2c_write(i2c_dev, ALS_GAIN, sizeof(ALS_GAIN), i2c_address); k_sleep(K_MSEC(10)); i2c_write(i2c_dev, ENABLE_ALS, sizeof(ENABLE_ALS), i2c_address); k_sleep(K_MSEC(100)); return 0; diff --git a/src/mode.c b/src/mode.c index bcf89d9..e899034 100644 --- a/src/mode.c +++ b/src/mode.c @@ -13,7 +13,9 @@ K_THREAD_STACK_DEFINE(mode_stack, 1024); static struct k_thread mode_thread; static atomic_t running = ATOMIC_INIT(0); -static struct mode_config cfg; +static struct mode_config_t cfg; +static struct color_t color; +static uint16_t color_duration = 2000; static void mode_thread_fn(void *p1, void *p2, void *p3) { @@ -30,10 +32,8 @@ static void mode_thread_fn(void *p1, void *p2, void *p3) /* Phase B: color animation over phase_b_ms */ start = k_uptime_get(); while (atomic_get(&running) && k_uptime_get() - start < cfg.phase_b_ms) { - leds_fade_to(500, 1, -1, -1); - leds_fade_to(500, -1, 1, -1); - leds_fade_to(500, -1, 0, -1); - leds_fade_to(500, 0, -1, -1); + leds_fade_to(color_duration/2, color.r, color.g, color.b); + leds_fade_to(color_duration/2, 0, 0, 0); } } @@ -46,6 +46,8 @@ void mode_init(void) app_config_t app_cfg; config_get(&app_cfg); cfg = app_cfg.mode_config; + color = app_cfg.color; + color_duration = app_cfg.color_duration; LOG_INF("Mode init: phase_a=%d ms, phase_b=%d ms", cfg.phase_a_ms, cfg.phase_b_ms); } diff --git a/src/mode.h b/src/mode.h index 295a5bf..b7cee51 100644 --- a/src/mode.h +++ b/src/mode.h @@ -3,7 +3,7 @@ #include -struct mode_config { +struct mode_config_t { int32_t phase_a_ms; int32_t phase_b_ms; }; diff --git a/src/shell.c b/src/shell.c index 0b0c41d..e967ec1 100644 --- a/src/shell.c +++ b/src/shell.c @@ -1,13 +1,27 @@ // User-facing shell commands. // mode set — save config and restart +// mode color — save color by name and restart +// mode duration — save color duration and restart // mode status — show current config // #include "mode.h" #include "config.h" +#include "led.h" #include #include +/* Print a color as 0-255 integers — %f is unavailable without + CONFIG_CBPRINTF_FP_SUPPORT, and integer RGB is more readable anyway. */ +static void print_color(const struct shell *sh, const struct color_t *color) +{ + shell_print(sh, " color=%s rgb=(%d, %d, %d)", + led_color_to_str(color), + (int)(color->r * 255), + (int)(color->g * 255), + (int)(color->b * 255)); +} + static int cmd_mode_set(const struct shell *sh, size_t argc, char **argv) { app_config_t cfg; @@ -21,20 +35,76 @@ static int cmd_mode_set(const struct shell *sh, size_t argc, char **argv) return 0; } +static int cmd_mode_color(const struct shell *sh, size_t argc, char **argv) +{ + struct color_t color; + if (led_color_from_str(argv[1], &color) != 0) { + shell_error(sh, "Unknown color: %s", argv[1]); + return -EINVAL; + } + + app_config_t cfg; + config_get(&cfg); + cfg.color = color; + config_save(&cfg); + mode_init(); + mode_restart(); + shell_print(sh, "Color saved and restarting."); + print_color(sh, &color); + return 0; +} + +static int cmd_mode_duration(const struct shell *sh, size_t argc, char **argv) +{ + int val = atoi(argv[1]); + if (val <= 0) { + shell_error(sh, "Duration must be a positive integer (ms)"); + return -EINVAL; + } + + app_config_t cfg; + config_get(&cfg); + cfg.color_duration = (uint16_t)val; + config_save(&cfg); + mode_init(); + mode_restart(); + shell_print(sh, "Color duration saved: %d ms. Restarting.", cfg.color_duration); + return 0; +} + static int cmd_mode_status(const struct shell *sh, size_t argc, char **argv) { app_config_t cfg; config_get(&cfg); + shell_print(sh, "phase_a=%d ms, phase_b=%d ms", cfg.mode_config.phase_a_ms, cfg.mode_config.phase_b_ms); + print_color(sh, &cfg.color); + shell_print(sh, " duration=%d ms", cfg.color_duration); + return 0; +} + +static int cmd_mode_colors(const struct shell *sh, size_t argc, char **argv) +{ + for (int i = 0; i < led_color_count(); i++) { + const led_color_entry_t *entry = led_color_get(i); + shell_print(sh, " %-16s rgb=(%d, %d, %d)", + entry->name, + (int)(entry->color.r * 255), + (int)(entry->color.g * 255), + (int)(entry->color.b * 255)); + } return 0; } SHELL_STATIC_SUBCMD_SET_CREATE(mode_cmds, - SHELL_CMD_ARG(set, NULL, "Set , save and restart.", cmd_mode_set, 3, 0), - SHELL_CMD( status, NULL, "Show current config.", cmd_mode_status), + SHELL_CMD_ARG(set, NULL, "Set , save and restart.", cmd_mode_set, 3, 0), + SHELL_CMD_ARG(color, NULL, "Set color by name (e.g. deep_orange), save and restart.", cmd_mode_color, 2, 0), + SHELL_CMD_ARG(duration, NULL, "Set color duration , save and restart.", cmd_mode_duration, 2, 0), + SHELL_CMD( status, NULL, "Show current config.", cmd_mode_status), + SHELL_CMD( colors, NULL, "List all available colors.", cmd_mode_colors), SHELL_SUBCMD_SET_END ); -SHELL_CMD_REGISTER(mode, &mode_cmds, "Mode control.", NULL); +SHELL_CMD_REGISTER(mode, &mode_cmds, "Mode control.", NULL); \ No newline at end of file