#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 #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; }