148 lines
4.5 KiB
C
148 lines
4.5 KiB
C
#include "led_driver.h"
|
|
#include "driver/rmt_tx.h"
|
|
#include "led_strip_encoder.h"
|
|
#include "esp_log.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "esp_random.h"
|
|
#include <string.h>
|
|
#include "esp_rom_sys.h"
|
|
|
|
static const char *TAG = "LED";
|
|
static bool led_ready = false; // 🚦 flag de inicialização
|
|
static rmt_channel_handle_t led_chan = NULL;
|
|
static rmt_encoder_handle_t led_enc = NULL;
|
|
static uint8_t led_pixels[LED_COUNT * 3];
|
|
|
|
// --- Conversão HSV → RGB ---
|
|
static void hsv2rgb(uint32_t h, uint32_t s, uint32_t v,
|
|
uint8_t *r, uint8_t *g, uint8_t *b)
|
|
{
|
|
h %= 360;
|
|
uint32_t rgb_max = v * 255 / 100;
|
|
uint32_t rgb_min = rgb_max * (100 - s) / 100;
|
|
uint32_t i = h / 60, diff = h % 60;
|
|
uint32_t adj = (rgb_max - rgb_min) * diff / 60;
|
|
|
|
switch (i) {
|
|
case 0: *r = rgb_max; *g = rgb_min + adj; *b = rgb_min; break;
|
|
case 1: *r = rgb_max - adj; *g = rgb_max; *b = rgb_min; break;
|
|
case 2: *r = rgb_min; *g = rgb_max; *b = rgb_min + adj; break;
|
|
case 3: *r = rgb_min; *g = rgb_max - adj; *b = rgb_max; break;
|
|
case 4: *r = rgb_min + adj; *g = rgb_min; *b = rgb_max; break;
|
|
default:*r = rgb_max; *g = rgb_min; *b = rgb_max - adj; break;
|
|
}
|
|
}
|
|
|
|
// --- Inicialização ---
|
|
esp_err_t led_init(void)
|
|
{
|
|
ESP_LOGI(TAG, "Inicializando LEDs no GPIO%d (%d LEDs)", LED_PIN, LED_COUNT);
|
|
|
|
rmt_tx_channel_config_t tx_cfg = {
|
|
.gpio_num = LED_PIN,
|
|
.clk_src = RMT_CLK_SRC_DEFAULT,
|
|
.mem_block_symbols = 64,
|
|
.resolution_hz = LED_RES_HZ,
|
|
.trans_queue_depth = 4,
|
|
};
|
|
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_cfg, &led_chan));
|
|
|
|
led_strip_encoder_config_t enc_cfg = {.resolution = LED_RES_HZ};
|
|
ESP_ERROR_CHECK(rmt_new_led_strip_encoder(&enc_cfg, &led_enc));
|
|
ESP_ERROR_CHECK(rmt_enable(led_chan));
|
|
|
|
memset(led_pixels, 0, sizeof(led_pixels));
|
|
|
|
led_ready = true; // ✅ marca como pronto
|
|
led_show(); // apaga tudo ao iniciar
|
|
return ESP_OK;
|
|
}
|
|
|
|
// --- Apagar todos os LEDs ---
|
|
esp_err_t led_clear(void)
|
|
{
|
|
if (!led_ready) return ESP_ERR_INVALID_STATE;
|
|
memset(led_pixels, 0, sizeof(led_pixels));
|
|
return led_show();
|
|
}
|
|
|
|
// --- Definir cor individual ---
|
|
esp_err_t led_set(uint16_t index, uint8_t r, uint8_t g, uint8_t b)
|
|
{
|
|
if (index >= LED_COUNT) return ESP_ERR_INVALID_ARG;
|
|
led_pixels[index * 3 + 0] = g; // WS2812 usa ordem GRB
|
|
led_pixels[index * 3 + 1] = r;
|
|
led_pixels[index * 3 + 2] = b;
|
|
return ESP_OK;
|
|
}
|
|
|
|
// --- Enviar buffer para o anel ---
|
|
esp_err_t led_show(void)
|
|
{
|
|
if (!led_ready || !led_chan || !led_enc) {
|
|
ESP_LOGW(TAG, "⚠️ led_show() chamado antes da inicialização — ignorado");
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
|
|
rmt_transmit_config_t tx_cfg = {.loop_count = 0};
|
|
esp_err_t err = rmt_transmit(led_chan, led_enc,
|
|
led_pixels, sizeof(led_pixels), &tx_cfg);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "❌ rmt_transmit falhou (%s)", esp_err_to_name(err));
|
|
return err;
|
|
}
|
|
|
|
err = rmt_tx_wait_all_done(led_chan, portMAX_DELAY);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "❌ rmt_tx_wait_all_done falhou (%s)", esp_err_to_name(err));
|
|
return err;
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
// --- Animação de rotação até posição alvo ---
|
|
void led_spin_to(uint16_t target, uint16_t rounds, uint16_t delay_start, uint16_t delay_end)
|
|
{
|
|
if (!led_ready) return;
|
|
|
|
uint32_t total_steps = rounds * LED_COUNT + target;
|
|
for (uint32_t i = 0; i < total_steps; i++) {
|
|
uint16_t pos = i % LED_COUNT;
|
|
|
|
// Limpa e acende o LED atual
|
|
led_clear();
|
|
led_set(pos, 255, 150, 0);
|
|
led_show();
|
|
|
|
// Calcula delay progressivo (aceleração -> desaceleração)
|
|
uint32_t delay = delay_start + ((delay_end - delay_start) * i) / total_steps;
|
|
vTaskDelay(pdMS_TO_TICKS(delay));
|
|
}
|
|
|
|
// Flash final
|
|
for (int j = 0; j < 3; j++) {
|
|
led_clear();
|
|
led_show();
|
|
vTaskDelay(pdMS_TO_TICKS(80));
|
|
for (int k = 0; k < LED_COUNT; k++)
|
|
led_set(k, 255, 200, 50);
|
|
led_show();
|
|
vTaskDelay(pdMS_TO_TICKS(80));
|
|
}
|
|
}
|
|
|
|
// --- Animação idle (arco-íris lento) ---
|
|
void led_idle_animation(void)
|
|
{
|
|
if (!led_ready) return;
|
|
|
|
static uint16_t hue = 0;
|
|
for (int i = 0; i < LED_COUNT; i++) {
|
|
uint8_t r, g, b;
|
|
hsv2rgb((hue + i * 6) % 360, 100, 10, &r, &g, &b);
|
|
led_set(i, r, g, b);
|
|
}
|
|
led_show();
|
|
hue = (hue + 2) % 360;
|
|
}
|