From bd769d24429f512e88f4a1813ee320d111c04476 Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Thu, 8 Jan 2026 10:46:27 +0100 Subject: [PATCH 1/3] Enhance safety checks in Helios codebase: prevent removal of the last color in colorset, validate pattern integrity before loading, and ensure LED brightness is not set to zero. Update comments for clarity. --- Helios/Colorset.cpp | 3 ++- Helios/Helios.cpp | 13 ++++++++++++- Helios/Led.cpp | 4 ++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Helios/Colorset.cpp b/Helios/Colorset.cpp index 64f764f..7cb852b 100644 --- a/Helios/Colorset.cpp +++ b/Helios/Colorset.cpp @@ -147,7 +147,8 @@ void colorset_add_color_with_value_style(colorset_t *set, random_t *ctx, uint8_t void colorset_remove_color(colorset_t *set, uint8_t index) { - if (index >= set->m_numColors) { + // Prevent removing the last color (pattern would become disabled) + if (index >= set->m_numColors || set->m_numColors <= 1) { return; } uint8_t i; diff --git a/Helios/Helios.cpp b/Helios/Helios.cpp index de97d3e..fd9f322 100644 --- a/Helios/Helios.cpp +++ b/Helios/Helios.cpp @@ -222,6 +222,12 @@ void helios_load_cur_mode(void) // try to write it out because storage was corrupt storage_write_pattern(g_cur_mode, &g_pat); } + // Validate pattern won't be disabled (no colors or no on/dash duration) + if (colorset_num_colors(&g_pat.m_colorset) == 0 || + (g_pat.m_args.on_dur == 0 && g_pat.m_args.dash_dur == 0)) { + patterns_make_default(g_cur_mode, &g_pat); + storage_write_pattern(g_cur_mode, &g_pat); + } // then re-initialize the pattern pattern_init_state(&g_pat); } @@ -325,6 +331,11 @@ static void helios_handle_state(void) } break; #endif + default: + // Recovery from corrupted state - reset to known good state + g_cur_state = STATE_MODES; + helios_load_cur_mode(); + break; } } @@ -854,7 +865,7 @@ static void helios_handle_state_set_global_brightness(void) } // show different levels of green for each selection uint8_t col = 0; - uint8_t brightness = 0; + uint8_t brightness = BRIGHTNESS_HIGH; // Safe default instead of 0 switch (g_menu_selection) { case 0: col = 0xFF; diff --git a/Helios/Led.cpp b/Helios/Led.cpp index 9bce4e0..f746d0b 100644 --- a/Helios/Led.cpp +++ b/Helios/Led.cpp @@ -161,6 +161,10 @@ uint8_t led_get_brightness(void) void led_set_brightness(uint8_t brightness) { + // Prevent brightness from being set to 0 (LED would be invisible) + if (brightness == 0) { + brightness = BRIGHTNESS_HIGH; + } m_brightness = brightness; } From 22117ad8ec8bd515d2b722b4d4186de50d021cc8 Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Thu, 8 Jan 2026 10:57:41 +0100 Subject: [PATCH 2/3] Refactor color randomization logic in Colorset and enhance mode index validation in Helios. Ensure at least one color is set to prevent disabled patterns, and add bounds checking for mode index during loading and setting. Update comments for clarity. --- Helios/Colorset.cpp | 4 ++++ Helios/Helios.cpp | 22 +++++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Helios/Colorset.cpp b/Helios/Colorset.cpp index 7cb852b..82358b4 100644 --- a/Helios/Colorset.cpp +++ b/Helios/Colorset.cpp @@ -165,9 +165,13 @@ void colorset_randomize_colors(colorset_t *set, random_t *ctx, uint8_t numColors mode = (enum colorset_color_mode)(random_next8(ctx, 0, 255) % COLOR_MODE_COUNT); } colorset_clear(set); + // Ensure at least 1 color (prevent disabled pattern) if (!numColors) { numColors = random_next8(ctx, mode == COLOR_MODE_MONOCHROMATIC ? 2 : 1, 9); } + if (numColors == 0) { + numColors = 1; + } uint8_t randomizedHue = random_next8(ctx, 0, 255); uint8_t colorGap = 0; if (mode == COLOR_MODE_COLOR_THEORY && numColors > 1) { diff --git a/Helios/Helios.cpp b/Helios/Helios.cpp index fd9f322..1832fbd 100644 --- a/Helios/Helios.cpp +++ b/Helios/Helios.cpp @@ -175,6 +175,9 @@ void helios_enter_sleep(void) DDRB |= (1 << DDB0) | (1 << DDB1) | (1 << DDB4); // wakeup here, re-init helios_init_components(); + // Brief flash to confirm device is alive (visible even if pattern is dim) + led_set_rgb3(0x20, 0x20, 0x20); + led_update(); #else g_cur_state = STATE_SLEEP; // enable the sleep uint8_t @@ -244,6 +247,10 @@ void helios_load_global_flags(void) if (helios_has_flags(FLAG_CONJURE)) { // if conjure is enabled then load the current mode index from storage g_cur_mode = storage_read_current_mode(); + // Validate mode index is in bounds + if (g_cur_mode >= NUM_MODE_SLOTS) { + g_cur_mode = 0; + } } // read the global brightness from index 2 config uint8_t saved_brightness = storage_read_brightness(); @@ -252,9 +259,7 @@ void helios_load_global_flags(void) uint8_t is_valid = !helios_has_any_flags(FLAGS_INVALID) && saved_brightness > 0; if (is_valid) { led_set_brightness(saved_brightness); - } - - if (!is_valid) { + } else { // if the brightness was 0 and the flags are invalid then the storage was likely // uninitialized or corrupt so write out the defaults helios_factory_reset(); @@ -269,7 +274,11 @@ void helios_save_global_flags(void) void helios_set_mode_index(uint8_t mode_index) { - g_cur_mode = (uint8_t)mode_index % NUM_MODE_SLOTS; + // Bounds check the mode index + if (mode_index >= NUM_MODE_SLOTS) { + mode_index = 0; + } + g_cur_mode = mode_index; // now load current mode again helios_load_cur_mode(); } @@ -916,7 +925,10 @@ static void helios_handle_state_randomize(void) random_t ctx; random_init_seed(&ctx, pattern_crc32(&g_pat)); uint8_t randVal = random_next8(&ctx, 0, 255); - colorset_randomize_colors(cur_set, &ctx, (randVal + 1) % NUM_COLOR_SLOTS, COLOR_MODE_RANDOMLY_PICK); + // Ensure at least 1 color (prevent 0 colors which disables pattern) + uint8_t numColors = ((randVal + 1) % NUM_COLOR_SLOTS); + if (numColors == 0) numColors = 1; + colorset_randomize_colors(cur_set, &ctx, numColors, COLOR_MODE_RANDOMLY_PICK); patterns_make_pattern((enum pattern_id)(randVal % PATTERN_COUNT), &g_pat); pattern_init_state(&g_pat); } From 422273765fa63a30c3f5225d47971a71623aeee0 Mon Sep 17 00:00:00 2001 From: Kurt LaVacque Date: Thu, 8 Jan 2026 17:05:35 +0100 Subject: [PATCH 3/3] Refactor Helios sleep handling and LED brightness logic: remove unnecessary LED confirmation flash on sleep entry, adjust state recovery comment for clarity, and allow brightness to be set to zero for improved flexibility. --- Helios/Helios.cpp | 9 ++------- Helios/Led.cpp | 4 ---- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/Helios/Helios.cpp b/Helios/Helios.cpp index 1832fbd..a3765c4 100644 --- a/Helios/Helios.cpp +++ b/Helios/Helios.cpp @@ -175,9 +175,6 @@ void helios_enter_sleep(void) DDRB |= (1 << DDB0) | (1 << DDB1) | (1 << DDB4); // wakeup here, re-init helios_init_components(); - // Brief flash to confirm device is alive (visible even if pattern is dim) - led_set_rgb3(0x20, 0x20, 0x20); - led_update(); #else g_cur_state = STATE_SLEEP; // enable the sleep uint8_t @@ -341,9 +338,7 @@ static void helios_handle_state(void) break; #endif default: - // Recovery from corrupted state - reset to known good state - g_cur_state = STATE_MODES; - helios_load_cur_mode(); + // Fallthrough to STATE_MODES for any unexpected state value break; } } @@ -874,7 +869,7 @@ static void helios_handle_state_set_global_brightness(void) } // show different levels of green for each selection uint8_t col = 0; - uint8_t brightness = BRIGHTNESS_HIGH; // Safe default instead of 0 + uint8_t brightness = 0; switch (g_menu_selection) { case 0: col = 0xFF; diff --git a/Helios/Led.cpp b/Helios/Led.cpp index f746d0b..9bce4e0 100644 --- a/Helios/Led.cpp +++ b/Helios/Led.cpp @@ -161,10 +161,6 @@ uint8_t led_get_brightness(void) void led_set_brightness(uint8_t brightness) { - // Prevent brightness from being set to 0 (LED would be invisible) - if (brightness == 0) { - brightness = BRIGHTNESS_HIGH; - } m_brightness = brightness; }