diff --git a/drivers/leds.cpp b/drivers/leds.cpp index 5159755..2b3b08a 100644 --- a/drivers/leds.cpp +++ b/drivers/leds.cpp @@ -22,17 +22,31 @@ const LEDColor LEDColor::PINK_STRONG = LEDColor::from_rgb(0xFF, 0x00, 0x80); const LEDColor LEDColor::WHITE = LEDColor::from_rgb(0x04, 0x04, 0x04); const LEDColor LEDColor::WHITE_STRONG = LEDColor::from_rgb(0xFF, 0xFF, 0xFF); -const static char* TAG = "leds"; +static constexpr const char* TAG = "leds"; -static led_strip_handle_t led_strip = NULL; -static SemaphoreHandle_t led_mutex = NULL; +static led_strip_handle_t led_strip = nullptr; +static SemaphoreHandle_t led_mutex = nullptr; + +static bool leds_ready() { + if (led_strip != nullptr && led_mutex != nullptr) { + return true; + } + + ESP_LOGE(TAG, "LEDs used before successful initialization"); + return false; +} void init_leds() { - led_mutex = xSemaphoreCreateMutex(); - if (led_mutex == NULL) { - ESP_LOGE(TAG, "Failed to create LED mutex"); + if (led_strip != nullptr || led_mutex != nullptr) { + ESP_ERROR_CHECK(led_strip != nullptr && led_mutex != nullptr + ? ESP_OK + : ESP_ERR_INVALID_STATE); return; } + + led_mutex = xSemaphoreCreateMutex(); + ESP_ERROR_CHECK(led_mutex != nullptr ? ESP_OK : ESP_ERR_NO_MEM); + /// LED strip common configuration led_strip_config_t strip_config = { .strip_gpio_num = PIN_NEOPIXEL, @@ -58,24 +72,71 @@ void init_leds() { /// Create the LED strip object ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip)); + // Clear the strip once during initialization. Runtime clear() is lazy. ESP_ERROR_CHECK(led_strip_clear(led_strip)); } void LEDController::set_led(uint32_t led, uint32_t color) { - xSemaphoreTake(led_mutex, portMAX_DELAY); - ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, led, color >> 16 & 0xFF, color >> 8 & 0xFF, color & 0xFF)); + if (!leds_ready() || xSemaphoreTake(led_mutex, portMAX_DELAY) != pdTRUE) { + return; + } + + const esp_err_t err = led_strip_set_pixel( + led_strip, led, (color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to set LED %u: %s", led, esp_err_to_name(err)); + } xSemaphoreGive(led_mutex); } void LEDController::flush() { - xSemaphoreTake(led_mutex, portMAX_DELAY); - ESP_ERROR_CHECK(led_strip_refresh(led_strip)); + if (!leds_ready() || xSemaphoreTake(led_mutex, portMAX_DELAY) != pdTRUE) { + return; + } + + const esp_err_t err = led_strip_refresh(led_strip); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to refresh LEDs: %s", esp_err_to_name(err)); + } xSemaphoreGive(led_mutex); } void LEDController::clear() { - xSemaphoreTake(led_mutex, portMAX_DELAY); - ESP_ERROR_CHECK(led_strip_clear(led_strip)); + clear(LEDColor::OFF); +} + +void LEDController::clear(LEDColor color) { + clear_range(0, LED_COUNT, color); +} + +void LEDController::clear_indicators() { + clear_indicators(LEDColor::OFF); +} + +void LEDController::clear_indicators(LEDColor color) { + clear_range(LED_SHAPE_COUNT, LED_INDICATOR_COUNT, color); +} + +void LEDController::clear_shape_display() { + clear_shape_display(LEDColor::OFF); +} + +void LEDController::clear_shape_display(LEDColor color) { + clear_range(0, LED_SHAPE_COUNT, color); +} + +void LEDController::clear_range(uint32_t first_led, uint32_t led_count, LEDColor color) { + if (!leds_ready() || xSemaphoreTake(led_mutex, portMAX_DELAY) != pdTRUE) { + return; + } + + const uint32_t value = color.value(); + for (uint32_t led = first_led; led < first_led + led_count; ++led) { + const esp_err_t err = led_strip_set_pixel( + led_strip, led, (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to clear LED %u: %s", led, esp_err_to_name(err)); + } + } xSemaphoreGive(led_mutex); - } diff --git a/include/blk_box_drivers/leds.hpp b/include/blk_box_drivers/leds.hpp index c28270d..b4c0b06 100644 --- a/include/blk_box_drivers/leds.hpp +++ b/include/blk_box_drivers/leds.hpp @@ -84,6 +84,7 @@ void init_leds(); class LEDController { private: static void set_led(uint32_t led, uint32_t color); + static void clear_range(uint32_t first_led, uint32_t led_count, LEDColor color); public: /// Sets the color of an indicator LED. /// @@ -99,11 +100,26 @@ public: set_led(static_cast(led), color.value()); } - /// Outputs the data to the leds. + /// Outputs the pending data to the LEDs. static void flush(); - /// Clears the LEDs + /// Sets all LEDs to off without transmitting. static void clear(); + + /// Sets all LEDs to `color` without transmitting. + static void clear(LEDColor color); + + /// Sets all indicator LEDs to off without transmitting. + static void clear_indicators(); + + /// Sets all indicator LEDs to `color` without transmitting. + static void clear_indicators(LEDColor color); + + /// Sets all shape-display LEDs to off without transmitting. + static void clear_shape_display(); + + /// Sets all shape-display LEDs to `color` without transmitting. + static void clear_shape_display(LEDColor color); };