- Add led_init() call during firmware initialization in app_main() - Add led_stop_blink() call on WebSocket disconnect - LED GPIO configurable via CONFIG_SPAXEL_LED_GPIO (default GPIO8) - Blink runs in FreeRTOS task at ~5Hz (100ms on/100ms off) - Any running blink is cancelled when new identify message received Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
130 lines
3.3 KiB
C
130 lines
3.3 KiB
C
#include "led.h"
|
|
#include "esp_log.h"
|
|
#include "esp_timer.h"
|
|
#include "esp_rom_sys.h"
|
|
#include "driver/gpio.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "soc/gpio_num.h"
|
|
|
|
static const char *TAG = "led";
|
|
|
|
// LED GPIO configuration (can be overridden via sdkconfig)
|
|
#ifndef CONFIG_SPAXEL_LED_GPIO
|
|
#define CONFIG_SPAXEL_LED_GPIO 8 // Default GPIO8 for ESP32-S3-DevKitC
|
|
#endif
|
|
|
|
#define LED_GPIO ((gpio_num_t)CONFIG_SPAXEL_LED_GPIO)
|
|
|
|
// Blink state
|
|
static struct {
|
|
bool initialized;
|
|
bool blinking;
|
|
uint32_t duration_ms;
|
|
TaskHandle_t task_handle;
|
|
} s_led_state = {
|
|
.initialized = false,
|
|
.blinking = false,
|
|
.task_handle = NULL,
|
|
};
|
|
|
|
// LED blink task
|
|
static void led_blink_task(void *arg) {
|
|
uint32_t duration_ms = (uint32_t)(uintptr_t)arg;
|
|
ESP_LOGI(TAG, "LED blink task started for %lu ms", duration_ms);
|
|
|
|
const TickType_t blink_on = pdMS_TO_TICKS(100); // 100ms on
|
|
const TickType_t blink_off = pdMS_TO_TICKS(100); // 100ms off
|
|
const TickType_t total_ticks = pdMS_TO_TICKS(duration_ms);
|
|
TickType_t elapsed = 0;
|
|
|
|
while (s_led_state.blinking && elapsed < total_ticks) {
|
|
// Turn LED on
|
|
gpio_set_level(LED_GPIO, 1);
|
|
vTaskDelay(blink_on);
|
|
elapsed += blink_on;
|
|
|
|
if (!s_led_state.blinking || elapsed >= total_ticks) break;
|
|
|
|
// Turn LED off
|
|
gpio_set_level(LED_GPIO, 0);
|
|
vTaskDelay(blink_off);
|
|
elapsed += blink_off;
|
|
}
|
|
|
|
// Ensure LED is off before exiting
|
|
gpio_set_level(LED_GPIO, 0);
|
|
|
|
ESP_LOGI(TAG, "LED blink task finished");
|
|
s_led_state.blinking = false;
|
|
s_led_state.task_handle = NULL;
|
|
vTaskDelete(NULL);
|
|
}
|
|
|
|
esp_err_t led_init(void) {
|
|
if (s_led_state.initialized) {
|
|
return ESP_OK;
|
|
}
|
|
|
|
// Configure LED GPIO as output
|
|
gpio_reset_pin(LED_GPIO);
|
|
gpio_set_direction(LED_GPIO, GPIO_MODE_OUTPUT);
|
|
gpio_set_level(LED_GPIO, 0); // Start with LED off
|
|
|
|
ESP_LOGI(TAG, "LED initialized on GPIO %d", LED_GPIO);
|
|
s_led_state.initialized = true;
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t led_blink_identify(uint32_t duration_ms) {
|
|
if (!s_led_state.initialized) {
|
|
ESP_LOGW(TAG, "LED not initialized");
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
|
|
// Limit duration to 60 seconds
|
|
if (duration_ms > 60000) {
|
|
duration_ms = 60000;
|
|
}
|
|
|
|
// Cancel any running blink
|
|
led_stop_blink();
|
|
|
|
// Start new blink task
|
|
s_led_state.blinking = true;
|
|
s_led_state.duration_ms = duration_ms;
|
|
|
|
BaseType_t ret = xTaskCreate(
|
|
led_blink_task,
|
|
"led_blink",
|
|
2048,
|
|
(void *)(uintptr_t)duration_ms,
|
|
5, // Priority
|
|
NULL,
|
|
&s_led_state.task_handle
|
|
);
|
|
|
|
if (ret != pdPASS) {
|
|
ESP_LOGE(TAG, "Failed to create LED blink task");
|
|
s_led_state.blinking = false;
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "LED identify blink started for %lu ms", duration_ms);
|
|
return ESP_OK;
|
|
}
|
|
|
|
void led_stop_blink(void) {
|
|
if (s_led_state.blinking && s_led_state.task_handle != NULL) {
|
|
ESP_LOGD(TAG, "Stopping LED blink");
|
|
s_led_state.blinking = false;
|
|
// Task will delete itself
|
|
vTaskDelay(pdMS_TO_TICKS(50)); // Give task time to exit
|
|
}
|
|
// Ensure LED is off
|
|
gpio_set_level(LED_GPIO, 0);
|
|
}
|
|
|
|
bool led_is_blinking(void) {
|
|
return s_led_state.blinking;
|
|
}
|