Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
/**
* @file led.c
* @brief LED control system implementation
*
* This file implements the LED control system for the AirCube device.
* It manages WS2812 addressable LEDs with thread-safe color and intensity control.
* All 3 LEDs are kept at the same color and intensity.
*
* @author StuckAtPrototype, LLC
* @version 4.0
*/
#include "led.h"
#include "esp_log.h"
#include "freertos/semphr.h"
#include "led_color_lib.h"
#include <math.h>
// Number of LEDs to control (using first 3 LEDs)
#define NUM_CONTROLLED_LEDS 3
// LED task cadence and brightness ramp timing
#define LED_TASK_PERIOD_MS 20
#define INTENSITY_RAMP_MS 500 // Time to traverse full 0-100% range
// Mutex to protect LED color and intensity updates
static SemaphoreHandle_t led_mutex = NULL;
// Global LED color and intensity variables (GRB format for WS2812 LEDs)
static uint32_t led_color = LED_COLOR_OFF; // Current LED color
static float led_intensity = 0.0f; // Current displayed intensity (0.0 to 1.0)
static float led_intensity_target = 0.0f; // Target intensity; led_task ramps toward this
// LED state structure for WS2812 driver
static struct led_state led_new_state = {0};
static void intensity_ramp_tick_locked(void)
{
float diff = led_intensity_target - led_intensity;
if (fabsf(diff) < 0.002f) {
led_intensity = led_intensity_target;
return;
}
const float step = 1.0f / ((float)INTENSITY_RAMP_MS / (float)LED_TASK_PERIOD_MS);
if (diff > 0.0f) {
led_intensity += (diff < step) ? diff : step;
} else {
led_intensity += (diff > -step) ? diff : -step;
}
}
static void led_render_frame(uint32_t color, float intensity)
{
uint32_t final_color = apply_color_intensity(color, intensity);
for (int i = 0; i < NUM_CONTROLLED_LEDS; i++) {
led_new_state.leds[i] = final_color;
}
for (int i = NUM_CONTROLLED_LEDS; i < NUM_LEDS; i++) {
led_new_state.leds[i] = LED_COLOR_OFF;
}
ws2812_write_leds(led_new_state);
}
/**
* @brief LED control task
*
* This task continuously updates the WS2812 LEDs with the current color
* and intensity settings. It reads the color and intensity in a thread-safe
* manner and applies them to all 3 LEDs.
*
* @param pvParameters Task parameters (unused)
*/
static void led_task(void *pvParameters)
{
while (1) {
if (led_mutex != NULL && xSemaphoreTake(led_mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
intensity_ramp_tick_locked();
uint32_t current_color = led_color;
float current_intensity = led_intensity;
xSemaphoreGive(led_mutex);
led_render_frame(current_color, current_intensity);
} else if (led_mutex == NULL) {
ESP_LOGW("led", "LED mutex not available, using direct access");
intensity_ramp_tick_locked();
led_render_frame(led_color, led_intensity);
} else {
ESP_LOGW("led", "Failed to take LED mutex - skipping update");
}
vTaskDelay(pdMS_TO_TICKS(LED_TASK_PERIOD_MS));
}
}
/**
* @brief Initialize the LED control system
*
* This function initializes the WS2812 LED driver, creates the LED mutex
* for thread-safe operations, and starts the LED control task.
*/
void led_init(void) {
// Initialize WS2812 LED driver
ws2812_control_init();
// Create mutex for thread-safe LED color and intensity updates
led_mutex = xSemaphoreCreateMutex();
if (led_mutex == NULL) {
ESP_LOGE("led", "Failed to create LED mutex - system will be unstable");
// Continue execution but log the error - system will use fallback mode
}
// Create LED control task
BaseType_t ret = xTaskCreate(led_task, "led_task", 4096, NULL, 10, NULL);
if (ret != pdPASS) {
ESP_LOGE("led", "Failed to create LED task");
}
}
/**
* @brief Set LED color
*
* This function sets the color of all LEDs in a thread-safe manner.
* The color should be in GRB format. Use led_color_lib functions
* to generate colors (e.g., get_color_green_to_red()).
*
* @param color Color value in GRB format (0x00RRGGBB)
*/
void led_set_color(uint32_t color) {
if (led_mutex != NULL && xSemaphoreTake(led_mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
led_color = color;
xSemaphoreGive(led_mutex);
} else if (led_mutex == NULL) {
// Fallback: direct assignment if mutex not available
led_color = color;
}
}
/**
* @brief Set LED intensity target
*
* Sets the target brightness (0.0 off to 1.0 full). The LED task ramps the
* displayed intensity smoothly toward this value over INTENSITY_RAMP_MS.
*/
void led_set_intensity(float intensity) {
if (intensity < 0.0f) intensity = 0.0f;
if (intensity > 1.0f) intensity = 1.0f;
if (led_mutex != NULL && xSemaphoreTake(led_mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
led_intensity_target = intensity;
xSemaphoreGive(led_mutex);
} else if (led_mutex == NULL) {
led_intensity_target = intensity;
}
}
/**
* @brief Get current LED color
*
* This function returns the current LED color in a thread-safe manner.
*
* @return Current color value in GRB format
*/
uint32_t led_get_color(void) {
uint32_t color = LED_COLOR_OFF;
if (led_mutex != NULL && xSemaphoreTake(led_mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
color = led_color;
xSemaphoreGive(led_mutex);
} else if (led_mutex == NULL) {
color = led_color;
}
return color;
}
/**
* @brief Get current displayed LED intensity
*
* Returns the ramped (animated) intensity, which may be between the previous
* and target values while a transition is in progress.
*/
float led_get_intensity(void) {
float intensity = 0.0f;
if (led_mutex != NULL && xSemaphoreTake(led_mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
intensity = led_intensity;
xSemaphoreGive(led_mutex);
} else if (led_mutex == NULL) {
intensity = led_intensity;
}
return intensity;
}
0e3b0bd1dThis file is pinned to the selected release. Download the original to open it in your design tool.