/* RMT example -- RGB LED Strip This example code is in the Public Domain (or CC0 licensed, at your option.) Unless required by applicable law or agreed to in writing, this software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ #pragma once #include "driver/rmt.h" #include "esp_log.h" #include "esp_system.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "led_strip.h" #include "nvs.h" #include "nvs_flash.h" #include "sdkconfig.h" static const char *LIGHT_TAG = "DisplayAmbientLight_Light"; #define RMT_TX_CHANNEL RMT_CHANNEL_0 #define RMT_TX_GPIO 1 #define STRIP_LED_NUMBER CONFIG_NUMBER_OF_LEDS #define EXAMPLE_CHASE_SPEED_MS (10) typedef enum light_mode_e { light_mode_init = 0, light_mode_connection_wifi = 1, light_mode_idle = 2, light_mode_mqtt_connected = 3, light_mode_desktop_online = 4, light_mode_desktop_sending_colors = 5, light_mode_off = 6, } light_mode_t; led_strip_t *light_led_strip; light_mode_t light_mode; float display_ambient_light_brightness = 1; uint8_t display_ambient_lighting_level = 255; float led_strip_red_calibration = 1.0; float led_strip_green_calibration = 1.0; float led_strip_blue_calibration = 1.0; void led_strip_fade_in_light_level(void *pvParameter) { float target = (float)display_ambient_lighting_level / 255.0; float step_length = (target - display_ambient_light_brightness) / 40.0; for (int t = 0; t < 40; t++) { display_ambient_light_brightness += step_length; vTaskDelay(pdMS_TO_TICKS(10)); } display_ambient_light_brightness = target; vTaskDelete(NULL); } void led_strip_set_brightness(uint8_t level) { if (display_ambient_lighting_level == level) { return; } display_ambient_lighting_level = level; xTaskCreate(led_strip_fade_in_light_level, "LED_STRIP_FADE_IN_LIGHT_LEVEL", 4096, NULL, 1, NULL); nvs_handle_t nvs_handle; esp_err_t err = nvs_open("storage", NVS_READWRITE, &nvs_handle); err = nvs_set_u8(nvs_handle, "brightness", level); if (err != ESP_OK) { ESP_LOGW(LIGHT_TAG, "Error (%s) saving light level!\n", esp_err_to_name(err)); nvs_close(nvs_handle); return; } err = nvs_commit(nvs_handle); if (err != ESP_OK) { ESP_LOGW(LIGHT_TAG, "Error (%s) saving light level!\n", esp_err_to_name(err)); } nvs_close(nvs_handle); } void led_strip_set_color_calibration(float red, float green, float blue) { led_strip_red_calibration = red; led_strip_green_calibration = green; led_strip_blue_calibration = blue; nvs_handle_t local_nvs_handle; esp_err_t err = nvs_open("storage", NVS_READWRITE, &local_nvs_handle); if (err != ESP_OK) { ESP_LOGW(LIGHT_TAG, "Error (%s) opening NVS handle!\n", esp_err_to_name(err)); return; } err = nvs_set_u8(local_nvs_handle, "calibration_r", (uint32_t)(red * 255)); if (err != ESP_OK) { nvs_close(local_nvs_handle); ESP_LOGW(LIGHT_TAG, "Error (%s) write calibration_r failed!", esp_err_to_name(err)); return; } err = nvs_set_u8(local_nvs_handle, "calibration_g", (uint8_t)(green * 255)); if (err != ESP_OK) { nvs_close(local_nvs_handle); ESP_LOGW(LIGHT_TAG, "Error (%s) calibration_g failed!", esp_err_to_name(err)); return; } err = nvs_set_u8(local_nvs_handle, "calibration_b", (uint8_t)(blue * 255)); if (err != ESP_OK) { nvs_close(local_nvs_handle); ESP_LOGW(LIGHT_TAG, "Error (%s) calibration_b failed!", esp_err_to_name(err)); return; } err = nvs_commit(local_nvs_handle); if (err != ESP_OK) { ESP_LOGW(LIGHT_TAG, "Error (%s) save led_strip_red_calibration failed!", esp_err_to_name(err)); } nvs_close(local_nvs_handle); } /** * @brief Simple helper function, converting HSV color space to RGB color * space * * Wiki: https://en.wikipedia.org/wiki/HSL_and_HSV * */ void led_strip_hsv2rgb(uint32_t h, uint32_t s, uint32_t v, uint32_t *r, uint32_t *g, uint32_t *b) { h %= 360; // h -> [0,360] uint32_t rgb_max = v * 2.55f; uint32_t rgb_min = rgb_max * (100 - s) / 100.0f; uint32_t i = h / 60; uint32_t diff = h % 60; // RGB adjustment amount by hue uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60; switch (i) { case 0: *r = rgb_max; *g = rgb_min + rgb_adj; *b = rgb_min; break; case 1: *r = rgb_max - rgb_adj; *g = rgb_max; *b = rgb_min; break; case 2: *r = rgb_min; *g = rgb_max; *b = rgb_min + rgb_adj; break; case 3: *r = rgb_min; *g = rgb_max - rgb_adj; *b = rgb_max; break; case 4: *r = rgb_min + rgb_adj; *g = rgb_min; *b = rgb_max; break; default: *r = rgb_max; *g = rgb_min; *b = rgb_max - rgb_adj; break; } } void update_desktop_connection_state() { static uint8_t tick = 0; bool beat = tick / 10 % 2 ? 1 : 0; switch (light_mode) { case light_mode_desktop_online: if (beat) { ESP_ERROR_CHECK( light_led_strip->set_pixel(light_led_strip, 0, 10, 10, 10)); } ESP_ERROR_CHECK( light_led_strip->set_pixel(light_led_strip, 1, 10, 10, 10)); ESP_ERROR_CHECK( light_led_strip->set_pixel(light_led_strip, 2, 10, 10, 10)); break; case light_mode_mqtt_connected: if (beat) { ESP_ERROR_CHECK( light_led_strip->set_pixel(light_led_strip, 0, 10, 10, 10)); ESP_ERROR_CHECK( light_led_strip->set_pixel(light_led_strip, 1, 10, 10, 10)); } ESP_ERROR_CHECK( light_led_strip->set_pixel(light_led_strip, 2, 22, 22, 22)); break; case light_mode_idle: if (beat) { ESP_ERROR_CHECK( light_led_strip->set_pixel(light_led_strip, 0, 77, 77, 77)); ESP_ERROR_CHECK( light_led_strip->set_pixel(light_led_strip, 1, 77, 77, 77)); ESP_ERROR_CHECK( light_led_strip->set_pixel(light_led_strip, 2, 77, 77, 77)); } break; default: break; } tick++; } void light_for_init() { ESP_LOGI(LIGHT_TAG, "light_for_init"); ESP_ERROR_CHECK(light_led_strip->clear(light_led_strip, 100)); nvs_handle local_nvs_handle; esp_err_t err = nvs_open("storage", NVS_READWRITE, &local_nvs_handle); if (err != ESP_OK) { ESP_LOGW(LIGHT_TAG, "Error (%s) opening NVS handle!", esp_err_to_name(err)); } uint8_t r = 255, g = 255, b = 255; uint8_t brightness = 200; err = nvs_get_u8(local_nvs_handle, "calibration_r", &r); if (err != ESP_OK) { ESP_LOGW(LIGHT_TAG, "Error (%s) reading calibration_r!", esp_err_to_name(err)); } err = nvs_get_u8(local_nvs_handle, "calibration_g", &g); if (err != ESP_OK) { ESP_LOGW(LIGHT_TAG, "Error (%s) reading calibration_g!", esp_err_to_name(err)); } err = nvs_get_u8(local_nvs_handle, "calibration_b", &b); if (err != ESP_OK) { ESP_LOGW(LIGHT_TAG, "Error (%s) reading calibration_b!", esp_err_to_name(err)); } err = nvs_get_u8(local_nvs_handle, "brightness", &brightness); if (err != ESP_OK) { ESP_LOGW(LIGHT_TAG, "Error (%s) reading brightness!", esp_err_to_name(err)); } nvs_close(local_nvs_handle); // set brightness led_strip_set_brightness(brightness); // play init light float r_f = (float)r / 255.0; float g_f = (float)g / 255.0; float b_f = (float)b / 255.0; uint8_t init_r, init_g, init_b; do { for (uint8_t i = 0; i < 50; i++) { init_r = (uint8_t)(r_f * (float)i); init_g = (uint8_t)(g_f * (float)i); init_b = (uint8_t)(b_f * (float)i); for (int j = 0; j < STRIP_LED_NUMBER; j++) { ESP_ERROR_CHECK(light_led_strip->set_pixel(light_led_strip, j, init_r, init_g, init_b)); } ESP_ERROR_CHECK(light_led_strip->refresh(light_led_strip, 100)); vTaskDelay(pdMS_TO_TICKS(20)); } vTaskDelay(pdMS_TO_TICKS(100)); } while (light_mode == light_mode_init); led_strip_set_color_calibration((float)r / 255.0, (float)g / 255.0, (float)b / 255.0); } void light_for_connecting_wifi() { ESP_LOGI(LIGHT_TAG, "light_for_connecting_wifi"); int8_t tick_tock = 0; do { ESP_ERROR_CHECK( light_led_strip->set_pixel(light_led_strip, tick_tock, 150, 150, 0)); ESP_ERROR_CHECK(light_led_strip->set_pixel(light_led_strip, (tick_tock + 1) % 2, 0, 200, 0)); ESP_ERROR_CHECK(light_led_strip->refresh(light_led_strip, 100)); tick_tock = !tick_tock; vTaskDelay(pdMS_TO_TICKS(200)); } while (light_mode == light_mode_connection_wifi); } void light_for_idle() { ESP_LOGI(LIGHT_TAG, "light_for_idle"); ESP_ERROR_CHECK(light_led_strip->clear(light_led_strip, 100)); uint32_t red = 0, green = 0, blue = 0; uint16_t step_length = 360 / STRIP_LED_NUMBER; for (uint16_t offset = 0; light_mode == light_mode_idle || light_mode == light_mode_mqtt_connected || light_mode == light_mode_desktop_online; offset = (offset + 1) % 360) { for (uint16_t j = 0, hue = offset; j < STRIP_LED_NUMBER; j++, hue += step_length) { // Build RGB values led_strip_hsv2rgb(hue, 50, 30, &red, &green, &blue); red = red * display_ambient_light_brightness * led_strip_red_calibration; green = green * display_ambient_light_brightness * led_strip_green_calibration; blue = blue * display_ambient_light_brightness * led_strip_blue_calibration; // Write RGB values to strip driver ESP_ERROR_CHECK( light_led_strip->set_pixel(light_led_strip, j, red, green, blue)); } update_desktop_connection_state(); ESP_ERROR_CHECK(light_led_strip->refresh(light_led_strip, 100)); vTaskDelay(pdMS_TO_TICKS(10)); } } void light_strip_running_task(void *pv_parameters) { while (true) { if (!light_led_strip) { ESP_LOGE(LIGHT_TAG, "install WS2812 driver failed 2"); } switch (light_mode) { case light_mode_init: light_for_init(); break; case light_mode_connection_wifi: light_for_connecting_wifi(); break; case light_mode_idle: case light_mode_mqtt_connected: case light_mode_desktop_online: light_for_idle(); break; default: vTaskDelay(pdMS_TO_TICKS(100)); break; } } vTaskDelete(NULL); } void light_init_strip() { rmt_config_t config = RMT_DEFAULT_CONFIG_TX(RMT_TX_GPIO, RMT_TX_CHANNEL); // set counter clock to 40MHz config.clk_div = 2; ESP_ERROR_CHECK(rmt_config(&config)); ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0)); // install ws2812 driver led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG( STRIP_LED_NUMBER, (led_strip_dev_t)config.channel); light_led_strip = led_strip_new_rmt_ws2812(&strip_config); if (!light_led_strip) { ESP_LOGE(LIGHT_TAG, "install WS2812 driver failed"); } // Clear LED strip (turn off all LEDs) ESP_ERROR_CHECK(light_led_strip->clear(light_led_strip, 100)); // Show simple rainbow chasing pattern ESP_LOGI(LIGHT_TAG, "LED Rainbow Chase Start"); light_mode = light_mode_init; xTaskCreate(light_strip_running_task, "LIGHT_STRIP_RUNNING_TASK", 4096, NULL, 1, NULL); } void light_play_colors(uint16_t len, uint8_t *buffer) { light_mode = light_mode_desktop_sending_colors; uint16_t black_count = 0; // count of black pixels. r/g/b <= 10 for (uint16_t led_index = 0, buffer_cursor = 0; led_index < STRIP_LED_NUMBER && buffer_cursor < len; led_index++, buffer_cursor += 3) { uint8_t r = (uint8_t)((float)buffer[buffer_cursor] * display_ambient_light_brightness * led_strip_red_calibration), g = (uint8_t)((float)buffer[buffer_cursor + 1] * display_ambient_light_brightness * led_strip_green_calibration), b = (uint8_t)((float)buffer[buffer_cursor + 2] * display_ambient_light_brightness * led_strip_blue_calibration); if (r <= 7 && g <= 7 && b <= 7) { black_count++; } ESP_ERROR_CHECK( light_led_strip->set_pixel(light_led_strip, led_index, r, g, b)); } if (black_count > STRIP_LED_NUMBER / 5 * 4) { uint8_t r = (uint8_t)((float)50 * display_ambient_light_brightness * led_strip_red_calibration), g = (uint8_t)((float)40 * display_ambient_light_brightness * led_strip_green_calibration), b = (uint8_t)((float)20 * display_ambient_light_brightness * led_strip_blue_calibration); for (uint16_t led_index = 0; led_index < STRIP_LED_NUMBER; led_index++) { ESP_ERROR_CHECK( light_led_strip->set_pixel(light_led_strip, led_index, r, g, b)); } } else { } ESP_ERROR_CHECK(light_led_strip->refresh(light_led_strip, 100)); vTaskDelay(pdMS_TO_TICKS(10)); } void light_play(light_mode_t mode) { ESP_LOGI(LIGHT_TAG, "light_play: %d", mode); light_mode = mode; }