├── .gitignore ├── image.jpg ├── 59333df79fe9b88a5f6df7c52084de0.jpg ├── idf_component.yml ├── CMakeLists.txt ├── LICENSE ├── Kconfig.projbuild ├── include ├── led_strip.h └── ws2812_control.h ├── README.md ├── README_EN.md ├── led_strip_rmt_ws2812.c └── ws2812_control.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ -------------------------------------------------------------------------------- /image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NingZiXi/ws2812_control/HEAD/image.jpg -------------------------------------------------------------------------------- /59333df79fe9b88a5f6df7c52084de0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NingZiXi/ws2812_control/HEAD/59333df79fe9b88a5f6df7c52084de0.jpg -------------------------------------------------------------------------------- /idf_component.yml: -------------------------------------------------------------------------------- 1 | version: 1.4.1 2 | license: "MIT" 3 | description: ws2812_control 4 | url: https://github.com/NingZiXi/ws2812_control 5 | files: 6 | exclude: 7 | - .git 8 | dependencies: 9 | idf: 10 | version: ">=4.4.7" 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "ws2812_control.cpp" 2 | "led_strip_rmt_ws2812.c" 3 | INCLUDE_DIRS "include" 4 | REQUIRES driver esp_timer 5 | ) 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 宁子希 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "ws2812 config" 2 | 3 | choice WS2812_MODE 4 | prompt "ws2812 mode (Strip mode, Matrix mode)" 5 | default WS2812_MODE_STRIP 6 | help 7 | Select the mode of the matrix. 8 | 9 | config WS2812_MODE_STRIP 10 | bool "Strip mode" 11 | help 12 | Select strip mode (single WS2812 strip). 13 | 14 | config WS2812_MODE_MATRIX 15 | bool "Matrix mode" 16 | help 17 | Select WS2812 mode (matrix of WS2812 LEDs). 18 | endchoice 19 | 20 | config WS2812_TX_GPIO 21 | int "GPIO number for ws2812 TX" 22 | default 33 23 | help 24 | GPIO number for the number TX signal. 25 | 26 | if WS2812_MODE_STRIP 27 | config WS2812_STRIP_LED_NUMBER 28 | int "Number of LEDs in the strip" 29 | default 12 30 | help 31 | Number of LEDs in the WS2812 LED strip. 32 | endif 33 | 34 | if WS2812_MODE_MATRIX 35 | config WS2812_MATRIX_WIDTH 36 | int "Matrix width (number of LEDs per row)" 37 | default 8 38 | help 39 | Number of LEDs in each row of the matrix. 40 | 41 | config WS2812_MATRIX_HEIGHT 42 | int "Matrix height (number of LEDs per column)" 43 | default 8 44 | help 45 | Number of LEDs in each column of the matrix. 46 | 47 | choice WS2812_MATRIX_START_CORNER 48 | prompt "Matrix start corner" 49 | default WS2812_MATRIX_START_TOP_RIGHT 50 | help 51 | Select the starting corner of the LED matrix. 52 | 53 | config WS2812_MATRIX_START_TOP_LEFT 54 | bool "Top left" 55 | 56 | config WS2812_MATRIX_START_TOP_RIGHT 57 | bool "Top right" 58 | 59 | config WS2812_MATRIX_START_BOTTOM_LEFT 60 | bool "Bottom left" 61 | 62 | config WS2812_MATRIX_START_BOTTOM_RIGHT 63 | bool "Bottom right" 64 | endchoice 65 | 66 | choice "Matrix Layout Orientation" 67 | prompt "Matrix Layout Orientation" 68 | default WS2812_MATRIX_LAYOUT_HORIZONTAL 69 | help 70 | Choose whether the matrix is row-major or column-major. 71 | 72 | config WS2812_MATRIX_LAYOUT_HORIZONTAL 73 | bool "Row-Major (Horizontal)" 74 | 75 | config WS2812_MATRIX_LAYOUT_VERTICAL 76 | bool "Column-Major (Vertical)" 77 | endchoice 78 | 79 | choice "Matrix Scan Order" 80 | prompt "Matrix Scan Order" 81 | default WS2812_MATRIX_SCAN_ZIGZAG 82 | help 83 | Choose the scanning order of the matrix lines. 84 | 85 | config WS2812_MATRIX_SCAN_PROGRESSIVE 86 | bool "Progressive" 87 | 88 | config WS2812_MATRIX_SCAN_ZIGZAG 89 | bool "Zigzag" 90 | endchoice 91 | endif 92 | 93 | endmenu 94 | -------------------------------------------------------------------------------- /include/led_strip.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #pragma once 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | #include "esp_err.h" 21 | 22 | /** 23 | * @brief LED Strip Type 24 | * 25 | */ 26 | typedef struct led_strip_s led_strip_t; 27 | 28 | /** 29 | * @brief LED Strip Device Type 30 | * 31 | */ 32 | typedef void *led_strip_dev_t; 33 | 34 | /** 35 | * @brief Declare of LED Strip Type 36 | * 37 | */ 38 | struct led_strip_s { 39 | /** 40 | * @brief Set RGB for a specific pixel 41 | * 42 | * @param strip: LED strip 43 | * @param index: index of pixel to set 44 | * @param red: red part of color 45 | * @param green: green part of color 46 | * @param blue: blue part of color 47 | * 48 | * @return 49 | * - ESP_OK: Set RGB for a specific pixel successfully 50 | * - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters 51 | * - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred 52 | */ 53 | esp_err_t (*set_pixel)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue); 54 | 55 | /** 56 | * @brief Refresh memory colors to LEDs 57 | * 58 | * @param strip: LED strip 59 | * @param timeout_ms: timeout value for refreshing task 60 | * 61 | * @return 62 | * - ESP_OK: Refresh successfully 63 | * - ESP_ERR_TIMEOUT: Refresh failed because of timeout 64 | * - ESP_FAIL: Refresh failed because some other error occurred 65 | * 66 | * @note: 67 | * After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip. 68 | */ 69 | esp_err_t (*refresh)(led_strip_t *strip, uint32_t timeout_ms); 70 | 71 | /** 72 | * @brief Clear LED strip (turn off all LEDs) 73 | * 74 | * @param strip: LED strip 75 | * @param timeout_ms: timeout value for clearing task 76 | * 77 | * @return 78 | * - ESP_OK: Clear LEDs successfully 79 | * - ESP_ERR_TIMEOUT: Clear LEDs failed because of timeout 80 | * - ESP_FAIL: Clear LEDs failed because some other error occurred 81 | */ 82 | esp_err_t (*clear)(led_strip_t *strip, uint32_t timeout_ms); 83 | 84 | /** 85 | * @brief Free LED strip resources 86 | * 87 | * @param strip: LED strip 88 | * 89 | * @return 90 | * - ESP_OK: Free resources successfully 91 | * - ESP_FAIL: Free resources failed because error occurred 92 | */ 93 | esp_err_t (*del)(led_strip_t *strip); 94 | }; 95 | 96 | /** 97 | * @brief LED Strip Configuration Type 98 | * 99 | */ 100 | typedef struct { 101 | uint32_t max_leds; /*!< Maximum LEDs in a single strip */ 102 | led_strip_dev_t dev; /*!< LED strip device (e.g. RMT channel, PWM channel, etc) */ 103 | } led_strip_config_t; 104 | 105 | /** 106 | * @brief Default configuration for LED strip 107 | * 108 | */ 109 | #define LED_STRIP_DEFAULT_CONFIG(number, dev_hdl) \ 110 | { \ 111 | .max_leds = number, \ 112 | .dev = dev_hdl, \ 113 | } 114 | 115 | /** 116 | * @brief Install a new ws2812 driver (based on RMT peripheral) 117 | * 118 | * @param config: LED strip configuration 119 | * @return 120 | * LED strip instance or NULL 121 | */ 122 | led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config); 123 | 124 | /** 125 | * @brief Init the RMT peripheral and LED strip configuration. 126 | * 127 | * @param[in] channel: RMT peripheral channel number. 128 | * @param[in] gpio: GPIO number for the RMT data output. 129 | * @param[in] led_num: number of addressable LEDs. 130 | * @return 131 | * LED strip instance or NULL 132 | */ 133 | led_strip_t * led_strip_init(uint8_t channel, uint8_t gpio, uint16_t led_num); 134 | 135 | /** 136 | * @brief Denit the RMT peripheral. 137 | * 138 | * @param[in] strip: LED strip 139 | * @return 140 | * - ESP_OK 141 | * - ESP_FAIL 142 | */ 143 | esp_err_t led_strip_denit(led_strip_t *strip); 144 | 145 | #ifdef __cplusplus 146 | } 147 | #endif 148 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![alt text](image.jpg) 2 |

🏳️‍🌈 ESP32 WS2812 Control

3 | 4 |

5 | ws2812_control是一个专为ESP32设计的WS2812 LED控制组件
6 | 支持多种LED效果和矩阵/条形两种工作模式
7 | 提供丰富的API接口和可配置参数 8 |

9 | 10 |

11 | English 12 | · 简体中文 13 | · 更新日志 14 | · 反馈问题 15 |

16 | 17 |

18 | 19 | License 20 | 21 | 22 | ESP32 23 | 24 | 25 | ESP-IDF 26 | 27 | 28 | Platform 29 | 30 | 31 | Version 32 | 33 | 34 | GitHub Stars 35 | 36 |

37 | 38 | --- 39 | 40 | ## 概述 41 | 42 | 本项目提供了一个用于控制 WS2812 LED 灯条的库,适用于 ESP32系列微控制器。该库包含了多种效果,如呼吸、渐变、闪烁和彩虹效果,以及基本的开关功能。设计时考虑了模块化和易集成性,方便在其他项目中使用。 43 | 44 | ## 功能特性 45 | - **支持WS2812矩阵屏**:除了普通WS2812驱动外还适用于创建多种排列和大小的 LED 矩阵。 46 | - **HSV 到 RGB 转换**:提供了一个辅助函数,用于将 HSV 颜色空间转换为 RGB 颜色空间。 47 | - **多种 LED 效果**:支持常亮、关闭、呼吸、渐入、慢闪、快闪和彩虹效果。 48 | - **单个 LED 控制**:可以单独设置某个 LED 的颜色。 49 | - **颜色宏定义**:预定义了多种常用颜色,如白色、绿色、红色、蓝色等。 50 | - **模块化设计**:代码结构清晰,易于扩展和维护。 51 | 52 | ## 配置WS2812参数 53 | 54 | 在menuconfig中配置WS2812参数,进入menuconfig找到ws2812 config菜单,此配置菜单用于设置 WS2812 LED 的信号引脚和工作模式,工作模式包括条形模式(Strip mode)和矩阵模式(Matrix mode)。根据所选的模式,您还可以配置相关参数,在条形模式(Strip mode)下只需要配置 LED 的数量即可,而在矩阵模式(Matrix mode)下,您还需要配置以下参数: 55 | 56 | 1. **矩阵宽高**:设置矩阵中每行每列的 LED 数量。 57 | 2. **矩阵布局类型**:指定矩阵的布局类型和方向,以便灵活支持不同的安装方式。 58 | 59 | 配置完成后,保存并退出 `menuconfig`,即可按照设定的模式和参数控制 WS2812 LED,下面先以条形模式举例介绍的使用方法。 60 | 61 | ## 条形模式(Strip mode)使用方法 62 | ### 1. 创建 WS2812 控制句柄 63 | 64 | 首先,需要创建一个 WS2812 控制句柄,用于后续的 LED 控制操作。 65 | 66 | ```c 67 | ws2812_strip_t* WS2812=ws2812_create(); 68 | ``` 69 | 70 | ### 2. 设置 LED 效果 71 | 72 | 可以通过调用 `ws2812_set` 函数来设置 LED 的效果。例如,设置所有 LED 为常亮状态,颜色为红色: 73 | 74 | ```c 75 | //方式1 76 | ws2812_set(WS2812, COLOR_RED, LED_EFFECT_ON); 77 | //方式2 78 | led_set_on(WS2812,COLOR_RED); 79 | //也可使用COLOR_RGB(255,0,0)来设置红色或自定义颜色 80 | led_set_on(WS2812,COLOR_RGB(255,0,0)); 81 | ``` 82 | 83 | ### 3. 常用功能 84 | 85 | - **设置单个 LED 颜色**: 86 | 87 | ```c 88 | led_set_pixel(WS2812, 0, COLOR_GREEN); // 设置第0个LED为绿色 89 | ``` 90 | 91 | - **关闭所有 LED**: 92 | 93 | ```c 94 | led_set_off(WS2812); 95 | ``` 96 | 97 | - **呼吸效果**: 98 | 99 | ```c 100 | //方式1 101 | ws2812_set(WS2812, COLOR_BLUE, LED_EFFECT_BREATH); 102 | //方式2 103 | led_set_breath(WS2812, COLOR_RGB(255, 0, 0), 10); // 红色呼吸 104 | ``` 105 | 106 | - **彩虹效果**: 107 | 108 | ```c 109 | //方式1 110 | ws2812_set(WS2812, COLOR_BLUE, LED_EFFECT_RAINBOW); 111 | //方式2 112 | led_set_rainbow(WS2812, 20); // 20ms间隔的彩虹效果 113 | ``` 114 | 115 | - **颜色渐变效果**: 116 | 117 | ```c 118 | led_set_color_gradient(WS2812, COLOR_RED, COLOR_BLUE, 1000); // 从红色渐变到蓝色,持续1000ms 119 | ``` 120 | 121 | 122 | #### `ws2812_set()`第三个参数 模式可设置以下效果 123 | > 124 | LED_EFFECT_ON // 使LED灯常亮效果 125 | LED_EFFECT_BREATH, // 使LED灯呼吸效果 126 | LED_EFFECT_FADE_IN, // 使LED灯淡入效果 127 | LED_EFFECT_BLINK_SLOW, // 使LED灯慢闪烁效果 128 | LED_EFFECT_BLINK_FAST, // 使LED灯快闪烁效果 129 | LED_EFFECT_RAINBOW // 使LED灯彩虹效果 130 | 131 | 需注意`ws2812_set()`的配置应用于所有灯珠 132 | 133 | 更多API请参考 [ws2812_control.h](include\ws2812_control.h) 文件 134 | 135 | ### 示例代码 136 | 137 | 以下是此库的示例代码, 138 | 139 | ```c 140 | #include 141 | #include 142 | 143 | void app_main(void){ 144 | 145 | // 创建一个WS2812灯带 146 | ws2812_strip_t* WS2812=ws2812_create(); 147 | 148 | //点亮灯带 颜色(255,0,0) 149 | led_set_on(WS2812,COLOR_RGB(255,0,0)); 150 | } 151 | ``` 152 | 153 | ## 矩阵模式(Matrix mode)使用方法 154 | 155 | 矩阵模式(Matrix mode)的使用方法与条形模式(Strip mode)类似 156 | 157 | ```c 158 | //创建ws2812矩阵控制句柄 159 | ws2812_matrix_t* WS2812 = ws2812_matrix_create(); 160 | 161 | //设置第2行第2列的LED为红色 162 | led_matrix_set_pixel(WS2812,2,2,COLOR_RGB(255,0,0)); 163 | 164 | //更新矩阵显示 165 | led_matrix_show(WS2812); 166 | ``` 167 | 168 | ### 示例代码 169 | 170 | 171 | 172 | ```c 173 | #include 174 | #include 175 | 176 | void app_main(void){ 177 | 178 | //创建ws2812矩阵控制句柄 179 | ws2812_matrix_t* WS2812 = ws2812_matrix_create(); 180 | 181 | //设置LED为红色 182 | led_matrix_set_pixel(WS2812,0,0,COLOR_RGB(255,0,0)); 183 | led_matrix_set_pixel(WS2812,1,1,COLOR_RGB(255,0,0)); 184 | led_matrix_set_pixel(WS2812,2,2,COLOR_RGB(255,0,0)); 185 | led_matrix_set_pixel(WS2812,3,3,COLOR_RGB(255,0,0)); 186 | 187 | //更新矩阵显示 188 | led_matrix_show(WS2812); 189 | } 190 | ``` 191 | ![alt text](59333df79fe9b88a5f6df7c52084de0.jpg) 192 | ## 依赖项 193 | 194 | - ESP-IDF >= 4.4.7 (最新IDF 5.3.1 经过测试可用) 195 | - led_strip 灯条驱动库 196 | 197 | ## 许可证 198 | 199 | 本项目采用 MIT 许可证。更多信息请参阅 [LICENSE](LICENSE) 文件。 200 | 201 | ## 贡献 202 | 203 | 欢迎任何形式的贡献,包括但不限于代码改进、文档更新、问题反馈等。请通过 GitHub 提交 Pull Request 或 Issue。 204 | 205 | ## 作者 206 | 207 | - 宁子希 (1589326497@qq.com) 208 | 209 | ## 版本历史 210 | 211 | - 1.0.0 (2024-08-31) 212 | - 初始版本发布 213 | - 1.1.0 (2024-10-31) 214 | - 增加跑马灯效果(led_set_marquee) 215 | - 1.2.0 (2024-11-26) 216 | - 增加矩阵模式 217 | - 1.3.0 (2025-4-27) 218 | - 改进彩虹效果实现 219 | - 动画led效果改由定时器实现 220 | - 1.4.0 (2025-9-5) 221 | - 增加颜色渐变效果(led_set_color_gradient) 222 | ## 🤝 贡献 223 | 本项目采用 MIT 许可证,详情请参阅 [LICENSE](LICENSE) 文件。 224 | 225 |

226 | 感谢您使用 ESP32 WS2812 Control!🌈
227 | 如果觉得项目对您有帮助,请给个 ⭐ Star 支持一下! 228 |

229 | -------------------------------------------------------------------------------- /include/ws2812_control.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ws2812_control.h 3 | * @author 宁子希 (1589326497@qq.com) 4 | * @brief WS2812灯条和矩阵屏幕控制 依赖led_strip库 5 | * @version 1.3.0 6 | * @date 2024-08-31 7 | * 8 | * @copyright Copyright (c) 2024 9 | * 10 | */ 11 | #ifndef WS2812_CONTROL_H 12 | #define WS2812_CONTROL_H 13 | 14 | #include 15 | #include "led_strip.h" 16 | #include "freertos/FreeRTOS.h" 17 | #include "freertos/task.h" 18 | #include "freertos/queue.h" 19 | #include "sdkconfig.h" 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | typedef led_strip_t ws2812_strip_t; 26 | 27 | // 定义颜色宏 28 | #define COLOR_RGB(r, g, b) ((led_color_t){(r), (g), (b)}) //自定义RGB颜色 29 | #define COLOR_WHITE (led_color_t){255, 255, 255} // 白色 30 | #define COLOR_GREEN (led_color_t){0, 255, 0} // 绿色 31 | #define COLOR_RED (led_color_t){255, 0, 0} // 红色 32 | #define COLOR_BLUE (led_color_t){0, 0, 255} // 蓝色 33 | #define COLOR_YELLOW (led_color_t){255, 255, 0} // 黄色 34 | #define COLOR_CYAN (led_color_t){0, 255, 255} // 青色 35 | #define COLOR_MAGENTA (led_color_t){255, 0, 255} // 洋红色 36 | #define COLOR_BLACK (led_color_t){0, 0, 0} // 黑色 37 | #define COLOR_ORANGE (led_color_t){255, 165, 0} // 橙色 38 | #define COLOR_PURPLE (led_color_t){128, 0, 128} // 紫色 39 | #define COLOR_PINK (led_color_t){255, 192, 203} // 粉色 40 | #define COLOR_GRAY (led_color_t){128, 128, 128} // 灰色 41 | #define COLOR_BROWN (led_color_t){165, 42, 42} // 棕色 42 | #define COLOR_LIME (led_color_t){0, 255, 0} // 酸橙绿 43 | #define COLOR_TEAL (led_color_t){0, 128, 128} // 蓝绿色 44 | #define COLOR_NAVY (led_color_t){0, 0, 128} // 海军蓝 45 | #define COLOR_FUCHSIA (led_color_t){255, 0, 255} // 紫红色 46 | #define COLOR_MAROON (led_color_t){128, 0, 0} // 栗色 47 | #define COLOR_OLIVE (led_color_t){128, 128, 0} // 橄榄绿 48 | 49 | // led_color_t 结构体表示 LED 灯的颜色 50 | typedef struct { 51 | uint32_t red; 52 | uint32_t green; 53 | uint32_t blue; 54 | } led_color_t; 55 | 56 | //ws2812 灯带模式结构体定义 57 | #ifdef CONFIG_WS2812_MODE_STRIP 58 | 59 | // led_effect_t 枚举类型表示 LED 灯的效果类型 60 | typedef enum { 61 | // 关闭LED灯效果 62 | LED_EFFECT_OFF, 63 | // 使LED灯常亮效果 64 | LED_EFFECT_ON, 65 | // 使LED灯呼吸效果 66 | LED_EFFECT_BREATH, 67 | // 使LED灯淡入效果 68 | LED_EFFECT_FADE_IN, 69 | // 使LED灯慢闪烁效果 70 | LED_EFFECT_BLINK_SLOW, 71 | // 使LED灯快闪烁效果 72 | LED_EFFECT_BLINK_FAST, 73 | // 使LED灯彩虹效果 74 | LED_EFFECT_RAINBOW 75 | } led_effect_t; 76 | 77 | //ws2812 矩阵屏幕模式结构体定义 78 | #elif defined(CONFIG_WS2812_MODE_MATRIX) 79 | 80 | /** 81 | * @brief 矩阵屏幕类型,用于定义矩阵屏幕的布局和扫描方式 82 | * 83 | */ 84 | #define MATRIX_START_TOP_LEFT 0x00 // 左上角 85 | #define MATRIX_START_TOP_RIGHT 0x01 // 右上角 86 | #define MATRIX_START_BOTTOM_LEFT 0x02 // 左下角 87 | #define MATRIX_START_BOTTOM_RIGHT 0x03 // 右下角 88 | #define MATRIX_START_CORNER 0x03 // 起始角落掩码 89 | 90 | #define MATRIX_LAYOUT_HORIZONTAL 0x00 // 行优先布局 91 | #define MATRIX_LAYOUT_VERTICAL 0x04 // 列优先布局 92 | #define MATRIX_LAYOUT_AXIS 0x04 // 布局轴掩码 93 | 94 | #define MATRIX_SCAN_PROGRESSIVE 0x00 // 连续扫描(每行的像素顺序相同) 95 | #define MATRIX_SCAN_ZIGZAG 0x08 // 之字形扫描 (每行的像素顺序在行之间反转) 96 | #define MATRIX_SCAN_SEQUENCE 0x08 // 扫描顺序掩码 97 | 98 | 99 | 100 | //WS2812Matrix结构体子类 101 | typedef struct { 102 | uint8_t matrixWidth; 103 | uint8_t matrixHeight; 104 | uint8_t pin; 105 | uint8_t matrixType; 106 | } ws2812_matrix_config; 107 | 108 | //ws2812_matrix_t 结构体表示 WS2812 灯条矩阵 109 | typedef struct { 110 | ws2812_strip_t *strip; 111 | ws2812_matrix_config config; 112 | } ws2812_matrix_t; 113 | 114 | 115 | #endif 116 | 117 | 118 | //ws2812 灯带模式函数声明 119 | #ifdef CONFIG_WS2812_MODE_STRIP 120 | 121 | /** 122 | * @brief 创建WS2812控制句柄 123 | * 124 | * @return ws2812_strip_t* - WS2812控制句柄 125 | */ 126 | ws2812_strip_t* ws2812_create(); 127 | 128 | /** 129 | * @brief 设置单个LED的颜色 130 | * 131 | * @param strip - WS2812控制句柄 132 | * @param index - LED的索引 133 | * @param color - LED的颜色 134 | */ 135 | void led_set_pixel(ws2812_strip_t *strip, int index, led_color_t color); 136 | 137 | /** 138 | * @brief 使所有LED常亮 139 | * 140 | * @param strip - WS2812控制句柄 141 | * @param color - LED的颜色 142 | */ 143 | void led_set_on(ws2812_strip_t *strip, led_color_t color); 144 | 145 | /** 146 | * @brief 关闭所有LED 147 | * 148 | * @param strip - WS2812控制句柄 149 | */ 150 | void led_set_off(ws2812_strip_t *strip); 151 | 152 | /** 153 | * @brief 使所有LED呼吸效果 154 | * 155 | * @param strip - WS2812控制句柄 156 | * @param color - LED的颜色 157 | * @param interval_ms - 呼吸时间间隔 158 | */ 159 | void led_set_breath(ws2812_strip_t *strip, led_color_t color, uint32_t interval_ms); 160 | 161 | /** 162 | * @brief 使所有LED缓缓亮起 163 | * 164 | * @param strip - WS2812控制句柄 165 | * @param color - LED的颜色 166 | */ 167 | void led_set_fade_in(ws2812_strip_t *strip, led_color_t color); 168 | 169 | /** 170 | * @brief 使所有LED闪烁 171 | * 172 | * @param strip - WS2812控制句柄 173 | * @param color - LED的颜色 174 | * @param interval_ms - 闪烁时间间隔 175 | */ 176 | void led_set_blink(ws2812_strip_t *strip, led_color_t color, uint32_t interval_ms); 177 | 178 | /** 179 | * @brief 使所有LED彩虹效果 180 | * 181 | * @param strip - WS2812控制句柄 182 | * @param interval_ms - 彩虹时间间隔 183 | */ 184 | void led_set_rainbow(ws2812_strip_t *strip, uint32_t interval_ms); 185 | 186 | /** 187 | * @brief 自选LED效果 188 | * 189 | * @param strip - WS2812控制句柄 190 | * @param color - LED的颜色 191 | * @param effect - LED的效果类型 192 | */ 193 | void ws2812_set(ws2812_strip_t *strip, led_color_t color, led_effect_t effect); 194 | 195 | /** 196 | * @brief LED跑马灯效果 197 | * 198 | * @param strip - WS2812控制句柄 199 | * @param index_start - 开始的LED索引 200 | * @param index_end - 结束的LED索引 201 | * @param color - LED的颜色 202 | * @param delay_ms - 延迟时间 203 | */ 204 | void led_set_marquee(ws2812_strip_t *strip, int index_start, int index_end, led_color_t color, int delay_ms); 205 | 206 | /** 207 | * @brief 更新LED显示 208 | * 209 | * @param strip - WS2812控制句柄 210 | */ 211 | void update_led_display(ws2812_strip_t *strip); 212 | 213 | /** 214 | * @brief 使所有LED从一个颜色渐变到另一个颜色 215 | * 216 | * @param strip - WS2812控制句柄 217 | * @param start_color - 起始颜色 218 | * @param end_color - 结束颜色 219 | * @param duration_ms - 渐变持续时间(毫秒) 220 | */ 221 | void led_set_color_gradient(ws2812_strip_t *strip, led_color_t start_color, led_color_t end_color, uint32_t duration_ms); 222 | 223 | 224 | //ws2812 矩阵屏幕模式函数声明 225 | #elif defined(CONFIG_WS2812_MODE_MATRIX) 226 | /** 227 | * @brief 创建WS2812矩阵控制句柄 228 | * 229 | * @return ws2812_matrix_t* 230 | */ 231 | ws2812_matrix_t* ws2812_matrix_create(); 232 | 233 | /** 234 | * @brief 设置单个LED的颜色 235 | * 236 | * @param matrix - WS2812控制句柄 237 | * @param x - led灯珠x坐标 238 | * @param y - led灯珠y坐标 239 | * @param color - LED的颜色 240 | */ 241 | void led_matrix_set_pixel(ws2812_matrix_t *matrix, int x, int y, led_color_t color); 242 | 243 | /** 244 | * @brief 更新LED显示 245 | * 246 | * @param matrix 247 | */ 248 | void led_matrix_show(ws2812_matrix_t *matrix); 249 | 250 | /** 251 | * @brief 清空LED显示 252 | * 253 | * @param matrix 254 | */ 255 | void led_matrix_clear(ws2812_matrix_t *matrix); 256 | 257 | #endif 258 | 259 | #ifdef __cplusplus 260 | } 261 | #endif 262 | 263 | #endif // WS2812_CONTROL_H 264 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | ![alt text](image.jpg) 2 |

🏳️‍🌈 ESP32 WS2812 Control

3 | 4 |

5 | ws2812_control is a WS2812 LED control component designed for ESP32
6 | Supports multiple LED effects and two working modes: matrix and strip
7 | Provides rich API interfaces and configurable parameters 8 |

9 | 10 |

11 | English 12 | · 简体中文 13 | · Changelog 14 | · Report Issues 15 |

16 | 17 |

18 | 19 | License 20 | 21 | 22 | ESP32 23 | 24 | 25 | ESP-IDF 26 | 27 | 28 | Platform 29 | 30 | 31 | Version 32 | 33 | 34 | GitHub Stars 35 | 36 |

37 | 38 | --- 39 | 40 | ## Overview 41 | 42 | This project provides a library for controlling WS2812 LED strips, compatible with ESP32 series microcontrollers. The library includes various effects such as breathing, fading, blinking, and rainbow effects, as well as basic on/off functionality. Designed with modularity and easy integration in mind, it can be conveniently used in other projects. 43 | 44 | ## Features 45 | - **Supports WS2812 Matrix**: In addition to regular WS2812 drivers, it also supports creating LED matrices of various arrangements and sizes. 46 | - **HSV to RGB Conversion**: Provides a helper function for converting HSV color space to RGB color space. 47 | - **Multiple LED Effects**: Supports always-on, off, breathing, fade-in, slow blink, fast blink, and rainbow effects. 48 | - **Individual LED Control**: Can set the color of individual LEDs. 49 | - **Color Macros**: Predefined common colors such as white, green, red, blue, etc. 50 | - **Modular Design**: Clear code structure, easy to extend and maintain. 51 | 52 | ## Configuration 53 | 54 | Configure WS2812 parameters in menuconfig. Navigate to the ws2812 config menu to set the signal pin and working mode (Strip mode or Matrix mode). Depending on the selected mode, you may need to configure additional parameters: 55 | 56 | 1. **Matrix Dimensions**: Set the number of LEDs per row and column in the matrix. 57 | 2. **Matrix Layout Type**: Specify the matrix layout type and direction to support different installation methods. 58 | 59 | After configuration, save and exit `menuconfig` to control WS2812 LEDs according to the set mode and parameters. Below is an example using Strip mode. 60 | 61 | ## Strip Mode Usage 62 | ### 1. Create WS2812 Handle 63 | 64 | First, create a WS2812 control handle for subsequent LED operations. 65 | 66 | ```c 67 | ws2812_strip_t* WS2812=ws2812_create(); 68 | ``` 69 | 70 | ### 2. Set LED Effects 71 | 72 | Use the `ws2812_set` function to set LED effects. For example, set all LEDs to always-on with red color: 73 | 74 | ```c 75 | // Method 1 76 | ws2812_set(WS2812, COLOR_RED, LED_EFFECT_ON); 77 | // Method 2 78 | led_set_on(WS2812,COLOR_RED); 79 | // Or use COLOR_RGB(255,0,0) to set red or custom colors 80 | led_set_on(WS2812,COLOR_RGB(255,0,0)); 81 | ``` 82 | 83 | ### 3. Common Functions 84 | 85 | - **Set Individual LED Color**: 86 | 87 | ```c 88 | led_set_pixel(WS2812, 0, COLOR_GREEN); // Set LED 0 to green 89 | ``` 90 | 91 | - **Turn Off All LEDs**: 92 | 93 | ```c 94 | led_set_off(WS2812); 95 | ``` 96 | 97 | - **Breathing Effect**: 98 | 99 | ```c 100 | // Method 1 101 | ws2812_set(WS2812, COLOR_BLUE, LED_EFFECT_BREATH); 102 | // Method 2 103 | led_set_breath(WS2812, COLOR_RGB(255, 0, 0), 10); // Red breathing 104 | ``` 105 | 106 | - **Rainbow Effect**: 107 | 108 | ```c 109 | // Method 1 110 | ws2812_set(WS2812, COLOR_BLUE, LED_EFFECT_RAINBOW); 111 | // Method 2 112 | led_set_rainbow(WS2812, 20); // Rainbow effect with 20ms interval 113 | ``` 114 | 115 | #### Available Effects for `ws2812_set()` 116 | > 117 | LED_EFFECT_ON // Always-on effect 118 | LED_EFFECT_BREATH, // Breathing effect 119 | LED_EFFECT_FADE_IN, // Fade-in effect 120 | LED_EFFECT_BLINK_SLOW, // Slow blink effect 121 | LED_EFFECT_BLINK_FAST, // Fast blink effect 122 | LED_EFFECT_RAINBOW // Rainbow effect 123 | 124 | Note: `ws2812_set()` applies to all LEDs. 125 | 126 | - **Color Gradient Effect**: 127 | 128 | ```c 129 | led_set_color_gradient(WS2812, COLOR_RED, COLOR_BLUE, 1000); // Gradient from red to blue over 1000ms 130 | ``` 131 | 132 | For more APIs, please refer to [ws2812_control.h](include\ws2812_control.h). 133 | 134 | ### Example Code 135 | 136 | ```c 137 | #include 138 | #include 139 | 140 | void app_main(void){ 141 | // Create a WS2812 strip 142 | ws2812_strip_t* WS2812=ws2812_create(); 143 | 144 | // Turn on strip with color (255,0,0) 145 | led_set_on(WS2812,COLOR_RGB(255,0,0)); 146 | } 147 | ``` 148 | 149 | ## Matrix Mode Usage 150 | 151 | Matrix mode usage is similar to Strip mode. 152 | 153 | ```c 154 | // Create WS2812 matrix handle 155 | ws2812_matrix_t* WS2812 = ws2812_matrix_create(); 156 | 157 | // Set LED at row 2, column 2 to red 158 | led_matrix_set_pixel(WS2812,2,2,COLOR_RGB(255,0,0)); 159 | 160 | // Update matrix display 161 | led_matrix_show(WS2812); 162 | ``` 163 | 164 | ### Example Code 165 | 166 | ```c 167 | #include 168 | #include 169 | 170 | void app_main(void){ 171 | // Create WS2812 matrix handle 172 | ws2812_matrix_t* WS2812 = ws2812_matrix_create(); 173 | 174 | // Set LEDs to red 175 | led_matrix_set_pixel(WS2812,0,0,COLOR_RGB(255,0,0)); 176 | led_matrix_set_pixel(WS2812,1,1,COLOR_RGB(255,0,0)); 177 | led_matrix_set_pixel(WS2812,2,2,COLOR_RGB(255,0,0)); 178 | led_matrix_set_pixel(WS2812,3,3,COLOR_RGB(255,0,0)); 179 | 180 | // Update matrix display 181 | led_matrix_show(WS2812); 182 | } 183 | ``` 184 | ![alt text](59333df79fe9b88a5f6df7c52084de0.jpg) 185 | 186 | ## Dependencies 187 | - ESP-IDF >= 4.4.7 (Tested with latest IDF 5.3.1) 188 | - led_strip driver library 189 | 190 | ## License 191 | This project is licensed under the MIT License. See [LICENSE](LICENSE) for details. 192 | 193 | ## Contributing 194 | Contributions of any kind are welcome, including but not limited to code improvements, documentation updates, and issue reports. Please submit Pull Requests or Issues via GitHub. 195 | 196 | ## Author 197 | - Ning ZiXi (1589326497@qq.com) 198 | 199 | ## Version History 200 | - 1.0.0 (2024-08-31) 201 | - Initial release 202 | - 1.1.0 (2024-10-31) 203 | - Added marquee effect (led_set_marquee) 204 | - 1.2.0 (2024-11-26) 205 | - Added matrix mode 206 | - 1.3.0 (2025-4-27) 207 | - Improved rainbow effect implementation 208 | - Animation effects now use timer 209 | - 1.4.0 (2025-4-28) 210 | - Added color gradient effect (led_set_color_gradient) 211 | 212 | ## 🤝 Contributing 213 | This project is licensed under the MIT License. See [LICENSE](LICENSE) for details. 214 | 215 |

216 | Thank you for using ESP32 WS2812 Control! 🌈
217 | If you find this project helpful, please give it a ⭐ Star! 218 |

219 | -------------------------------------------------------------------------------- /led_strip_rmt_ws2812.c: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include 16 | #include 17 | #include "esp_log.h" 18 | #include "esp_attr.h" 19 | #include "led_strip.h" 20 | #include "driver/rmt.h" 21 | 22 | #define RMT_TX_CHANNEL RMT_CHANNEL_0 23 | 24 | static const char *TAG = "ws2812"; 25 | #define STRIP_CHECK(a, str, goto_tag, ret_value, ...) \ 26 | do \ 27 | { \ 28 | if (!(a)) \ 29 | { \ 30 | ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ 31 | ret = ret_value; \ 32 | goto goto_tag; \ 33 | } \ 34 | } while (0) 35 | 36 | #define WS2812_T0H_NS (350) 37 | #define WS2812_T0L_NS (1000) 38 | #define WS2812_T1H_NS (1000) 39 | #define WS2812_T1L_NS (350) 40 | #define WS2812_RESET_US (280) 41 | 42 | static uint32_t ws2812_t0h_ticks = 0; 43 | static uint32_t ws2812_t1h_ticks = 0; 44 | static uint32_t ws2812_t0l_ticks = 0; 45 | static uint32_t ws2812_t1l_ticks = 0; 46 | 47 | typedef struct { 48 | led_strip_t parent; 49 | rmt_channel_t rmt_channel; 50 | uint32_t strip_len; 51 | uint8_t buffer[0]; 52 | } ws2812_t; 53 | 54 | /** 55 | * @brief Conver RGB data to RMT format. 56 | * 57 | * @note For WS2812, R,G,B each contains 256 different choices (i.e. uint8_t) 58 | * 59 | * @param[in] src: source data, to converted to RMT format 60 | * @param[in] dest: place where to store the convert result 61 | * @param[in] src_size: size of source data 62 | * @param[in] wanted_num: number of RMT items that want to get 63 | * @param[out] translated_size: number of source data that got converted 64 | * @param[out] item_num: number of RMT items which are converted from source data 65 | */ 66 | static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size, 67 | size_t wanted_num, size_t *translated_size, size_t *item_num) 68 | { 69 | if (src == NULL || dest == NULL) { 70 | *translated_size = 0; 71 | *item_num = 0; 72 | return; 73 | } 74 | const rmt_item32_t bit0 = {{{ ws2812_t0h_ticks, 1, ws2812_t0l_ticks, 0 }}}; //Logical 0 75 | const rmt_item32_t bit1 = {{{ ws2812_t1h_ticks, 1, ws2812_t1l_ticks, 0 }}}; //Logical 1 76 | size_t size = 0; 77 | size_t num = 0; 78 | uint8_t *psrc = (uint8_t *)src; 79 | rmt_item32_t *pdest = dest; 80 | while (size < src_size && num < wanted_num) { 81 | for (int i = 0; i < 8; i++) { 82 | // MSB first 83 | if (*psrc & (1 << (7 - i))) { 84 | pdest->val = bit1.val; 85 | } else { 86 | pdest->val = bit0.val; 87 | } 88 | num++; 89 | pdest++; 90 | } 91 | size++; 92 | psrc++; 93 | } 94 | *translated_size = size; 95 | *item_num = num; 96 | } 97 | 98 | static esp_err_t ws2812_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue) 99 | { 100 | esp_err_t ret = ESP_OK; 101 | ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent); 102 | STRIP_CHECK(index < ws2812->strip_len, "index out of the maximum number of leds", err, ESP_ERR_INVALID_ARG); 103 | uint32_t start = index * 3; 104 | // In thr order of GRB 105 | ws2812->buffer[start + 0] = green & 0xFF; 106 | ws2812->buffer[start + 1] = red & 0xFF; 107 | ws2812->buffer[start + 2] = blue & 0xFF; 108 | return ESP_OK; 109 | err: 110 | return ret; 111 | } 112 | 113 | static esp_err_t ws2812_refresh(led_strip_t *strip, uint32_t timeout_ms) 114 | { 115 | esp_err_t ret = ESP_OK; 116 | ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent); 117 | STRIP_CHECK(rmt_write_sample(ws2812->rmt_channel, ws2812->buffer, ws2812->strip_len * 3, true) == ESP_OK, 118 | "transmit RMT samples failed", err, ESP_FAIL); 119 | return rmt_wait_tx_done(ws2812->rmt_channel, pdMS_TO_TICKS(timeout_ms)); 120 | err: 121 | return ret; 122 | } 123 | 124 | static esp_err_t ws2812_clear(led_strip_t *strip, uint32_t timeout_ms) 125 | { 126 | ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent); 127 | // Write zero to turn off all leds 128 | memset(ws2812->buffer, 0, ws2812->strip_len * 3); 129 | return ws2812_refresh(strip, timeout_ms); 130 | } 131 | 132 | static esp_err_t ws2812_del(led_strip_t *strip) 133 | { 134 | ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent); 135 | free(ws2812); 136 | return ESP_OK; 137 | } 138 | 139 | led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config) 140 | { 141 | led_strip_t *ret = NULL; 142 | STRIP_CHECK(config, "configuration can't be null", err, NULL); 143 | 144 | // 24 bits per led 145 | uint32_t ws2812_size = sizeof(ws2812_t) + config->max_leds * 3; 146 | ws2812_t *ws2812 = calloc(1, ws2812_size); 147 | STRIP_CHECK(ws2812, "request memory for ws2812 failed", err, NULL); 148 | 149 | uint32_t counter_clk_hz = 0; 150 | STRIP_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev, &counter_clk_hz) == ESP_OK, 151 | "get rmt counter clock failed", err, NULL); 152 | // ns -> ticks 153 | float ratio = (float)counter_clk_hz / 1e9; 154 | ws2812_t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS); 155 | ws2812_t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS); 156 | ws2812_t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS); 157 | ws2812_t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS); 158 | 159 | // set ws2812 to rmt adapter 160 | rmt_translator_init((rmt_channel_t)config->dev, ws2812_rmt_adapter); 161 | 162 | ws2812->rmt_channel = (rmt_channel_t)config->dev; 163 | ws2812->strip_len = config->max_leds; 164 | 165 | ws2812->parent.set_pixel = ws2812_set_pixel; 166 | ws2812->parent.refresh = ws2812_refresh; 167 | ws2812->parent.clear = ws2812_clear; 168 | ws2812->parent.del = ws2812_del; 169 | 170 | return &ws2812->parent; 171 | err: 172 | return ret; 173 | } 174 | 175 | led_strip_t * led_strip_init(uint8_t channel, uint8_t gpio, uint16_t led_num) 176 | { 177 | static led_strip_t *pStrip; 178 | 179 | rmt_config_t config = RMT_DEFAULT_CONFIG_TX(gpio, channel); 180 | // set counter clock to 40MHz 181 | config.clk_div = 2; 182 | 183 | ESP_ERROR_CHECK(rmt_config(&config)); 184 | ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0)); 185 | 186 | // install ws2812 driver 187 | led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG(led_num, (led_strip_dev_t)config.channel); 188 | 189 | pStrip = led_strip_new_rmt_ws2812(&strip_config); 190 | 191 | if ( !pStrip ) { 192 | ESP_LOGE(TAG, "install WS2812 driver failed"); 193 | return NULL; 194 | } 195 | 196 | // Clear LED strip (turn off all LEDs) 197 | ESP_ERROR_CHECK(pStrip->clear(pStrip, 100)); 198 | 199 | return pStrip; 200 | } 201 | 202 | esp_err_t led_strip_denit(led_strip_t *strip) 203 | { 204 | ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent); 205 | ESP_ERROR_CHECK(rmt_driver_uninstall(ws2812->rmt_channel)); 206 | return strip->del(strip); 207 | } 208 | -------------------------------------------------------------------------------- /ws2812_control.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ws2812_control.c 3 | * @author 宁子希 (1589326497@qq.com) 4 | * @brief WS2812灯条和矩阵屏幕控制 依赖led_strip库 5 | * @version 1.3.0 6 | * @date 2024-08-31 7 | * 8 | * @copyright Copyright (c) 2024 9 | * 10 | */ 11 | #include "sdkconfig.h" 12 | #include "freertos/FreeRTOS.h" 13 | #include "freertos/task.h" 14 | #include "esp_log.h" 15 | #include "driver/rmt.h" 16 | #include "led_strip.h" 17 | #include "ws2812_control.h" 18 | #include "esp_timer.h" 19 | 20 | static const char *TAG = "WS2812_control"; 21 | 22 | #define RMT_TX_CHANNEL RMT_CHANNEL_0 23 | #define EXAMPLE_CHASE_SPEED_MS (240) 24 | 25 | // 定时器回调函数指针类型 26 | typedef void (*led_effect_func_t)(ws2812_strip_t*, led_color_t); 27 | 28 | // 定时器状态结构体 29 | typedef struct { 30 | ws2812_strip_t* strip; 31 | led_color_t color; 32 | led_effect_func_t effect_func; 33 | int counter; 34 | bool active; 35 | } timer_effect_state_t; 36 | 37 | // 全局定时器状态和句柄 38 | static timer_effect_state_t timer_state; 39 | static esp_timer_handle_t led_timer; 40 | 41 | /** 42 | *@brief 简单的辅助函数,将 HSV 颜色空间转换为 RGB 颜色空间 43 | * 44 | *@param h 色相值,范围为[0,360] 45 | *@param s 饱和度值,范围为[0,100] 46 | *@param v 亮度值,范围为[0,100] 47 | *@param r 指向存储红色值的指针 48 | *@param g 指向存储绿色值的指针 49 | *@param b 指向存储蓝色值的指针 50 | * 51 | */ 52 | void led_strip_hsv2rgb(uint32_t h, uint32_t s, uint32_t v, uint32_t *r, uint32_t *g, uint32_t *b) 53 | { 54 | h %= 360; // h -> [0,360] 55 | uint32_t rgb_max = v * 2.55f; 56 | uint32_t rgb_min = rgb_max * (100 - s) / 100.0f; 57 | 58 | uint32_t i = h / 60; 59 | uint32_t diff = h % 60; 60 | 61 | // RGB adjustment amount by hue 62 | uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60; 63 | 64 | switch (i) { 65 | case 0: 66 | *r = rgb_max; 67 | *g = rgb_min + rgb_adj; 68 | *b = rgb_min; 69 | break; 70 | case 1: 71 | *r = rgb_max - rgb_adj; 72 | *g = rgb_max; 73 | *b = rgb_min; 74 | break; 75 | case 2: 76 | *r = rgb_min; 77 | *g = rgb_max; 78 | *b = rgb_min + rgb_adj; 79 | break; 80 | case 3: 81 | *r = rgb_min; 82 | *g = rgb_max - rgb_adj; 83 | *b = rgb_max; 84 | break; 85 | case 4: 86 | *r = rgb_min + rgb_adj; 87 | *g = rgb_min; 88 | *b = rgb_max; 89 | break; 90 | default: 91 | *r = rgb_max; 92 | *g = rgb_min; 93 | *b = rgb_max - rgb_adj; 94 | break; 95 | } 96 | } 97 | 98 | /** 99 | *@brief 简单的辅助函数,将 RGB 颜色空间转换为 HSV 颜色空间 100 | * 101 | *@param r 红色值,范围为[0,255] 102 | *@param g 绿色值,范围为[0,255] 103 | *@param b 蓝色值,范围为[0,255] 104 | *@param h 指向存储色相值的指针 105 | *@param s 指向存储饱和度值的指针 106 | *@param v 指向存储亮度值的指针 107 | * 108 | */ 109 | void led_strip_rgb2hsv(uint32_t r, uint32_t g, uint32_t b, uint32_t *h, uint32_t *s, uint32_t *v) 110 | { 111 | uint32_t rgb_max = (r > g && r > b) ? r : (g > b ? g : b); 112 | uint32_t rgb_min = (r < g && r < b) ? r : (g < b ? g : b); 113 | uint32_t delta = rgb_max - rgb_min; 114 | 115 | // 计算亮度值 116 | *v = rgb_max / 2.55f; 117 | 118 | // 如果最大值为0,则饱和度和色相都为0 119 | if (rgb_max == 0) { 120 | *s = 0; 121 | *h = 0; 122 | return; 123 | } 124 | 125 | // 计算饱和度 126 | *s = (delta * 100) / rgb_max; 127 | 128 | // 如果delta为0,则色相为0 129 | if (delta == 0) { 130 | *h = 0; 131 | return; 132 | } 133 | 134 | // 计算色相 135 | if (rgb_max == r) { 136 | *h = (60 * ((int32_t)(g - b) / (int32_t)delta) + 360) % 360; 137 | } else if (rgb_max == g) { 138 | *h = (60 * ((int32_t)(b - r) / (int32_t)delta) + 120) % 360; 139 | } else { 140 | *h = (60 * ((int32_t)(r - g) / (int32_t)delta) + 240) % 360; 141 | } 142 | } 143 | 144 | // 定时器回调函数 145 | static void led_timer_callback(void* arg){ 146 | if (timer_state.active && timer_state.effect_func) { 147 | timer_state.effect_func(timer_state.strip, timer_state.color); 148 | timer_state.counter++; 149 | } 150 | } 151 | 152 | //ws2812 灯带模式 153 | #ifdef CONFIG_WS2812_MODE_STRIP 154 | 155 | // 创建WS2812控制句柄 156 | ws2812_strip_t* ws2812_create() { 157 | // 初始化WS2812控制任务 158 | rmt_config_t config = RMT_DEFAULT_CONFIG_TX((gpio_num_t)CONFIG_WS2812_TX_GPIO, RMT_TX_CHANNEL); 159 | config.clk_div = 2; 160 | 161 | ESP_ERROR_CHECK(rmt_config(&config)); 162 | ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0)); 163 | 164 | led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG(CONFIG_WS2812_STRIP_LED_NUMBER, (led_strip_dev_t)config.channel); 165 | ws2812_strip_t *strip = led_strip_new_rmt_ws2812(&strip_config); 166 | 167 | if (!strip) { 168 | ESP_LOGE(TAG, "install WS2812 driver failed"); 169 | } 170 | 171 | ESP_ERROR_CHECK(strip->clear(strip, 100)); 172 | 173 | // 初始化定时器 174 | esp_timer_create_args_t timer_args = { 175 | .callback = &led_timer_callback, 176 | .arg = NULL, 177 | .dispatch_method = ESP_TIMER_TASK, 178 | .name = "led_timer" 179 | }; 180 | ESP_ERROR_CHECK(esp_timer_create(&timer_args, &led_timer)); 181 | 182 | return strip; 183 | } 184 | 185 | // 设置 LED 颜色(单个) 186 | void led_set_pixel(ws2812_strip_t *strip,int index, led_color_t color) { 187 | if (timer_state.active) { 188 | esp_timer_stop(led_timer); 189 | } 190 | if (index >= 0 && index < CONFIG_WS2812_STRIP_LED_NUMBER) { 191 | ESP_ERROR_CHECK(strip->set_pixel(strip, index,color.red, color.green, color.blue)); 192 | } 193 | ESP_ERROR_CHECK(strip->refresh(strip, 100)); 194 | } 195 | 196 | // LED 全部常亮 197 | void led_set_on(ws2812_strip_t *strip, led_color_t color) { 198 | if (timer_state.active) { 199 | esp_timer_stop(led_timer); 200 | } 201 | for (int i = 0; i < CONFIG_WS2812_STRIP_LED_NUMBER; i++) { 202 | ESP_ERROR_CHECK(strip->set_pixel(strip, i, color.red, color.green, color.blue)); 203 | } 204 | ESP_ERROR_CHECK(strip->refresh(strip, 100)); 205 | } 206 | 207 | // LED 关闭 208 | void led_set_off(ws2812_strip_t *strip) { 209 | if (timer_state.active) { 210 | esp_timer_stop(led_timer); 211 | } 212 | for (int i = 0; i < CONFIG_WS2812_STRIP_LED_NUMBER; i++) { 213 | ESP_ERROR_CHECK(strip->set_pixel(strip, i, 0, 0, 0)); // 设置为黑色 214 | } 215 | ESP_ERROR_CHECK(strip->refresh(strip, 100)); 216 | } 217 | 218 | // LED 呼吸效果 219 | void led_set_breath(ws2812_strip_t *strip, led_color_t color, uint32_t interval_ms) { 220 | if (timer_state.active) { 221 | esp_timer_stop(led_timer); 222 | } 223 | 224 | // 设置定时器状态 225 | timer_state.strip = strip; 226 | timer_state.color = color; 227 | timer_state.active = true; 228 | timer_state.counter = 0; 229 | 230 | timer_state.effect_func = [](ws2812_strip_t* strip, led_color_t color) { 231 | static int brightness = 0; 232 | static bool increasing = true; 233 | 234 | // 更新亮度 235 | if (increasing) { 236 | brightness++; 237 | if (brightness >= 255) increasing = false; 238 | } else { 239 | brightness--; 240 | if (brightness <= 0) increasing = true; 241 | } 242 | 243 | // 设置LED亮度 244 | for (int j = 0; j < CONFIG_WS2812_STRIP_LED_NUMBER; j++) { 245 | ESP_ERROR_CHECK(strip->set_pixel( 246 | strip, j, 247 | (color.red * brightness) / 255, 248 | (color.green * brightness) / 255, 249 | (color.blue * brightness) / 255 250 | )); 251 | } 252 | strip->refresh(strip, 100); 253 | }; 254 | 255 | // 启动定时器,使用传入的时间间隔 256 | ESP_ERROR_CHECK(esp_timer_start_periodic(led_timer, interval_ms * 1000)); // 转换为微秒 257 | } 258 | 259 | // LED 缓缓亮起 260 | void led_set_fade_in(ws2812_strip_t *strip, led_color_t color) { 261 | if (timer_state.active) { 262 | esp_timer_stop(led_timer); 263 | } 264 | for (int i = 0; i < 256; i++) { 265 | for (int j = 0; j < CONFIG_WS2812_STRIP_LED_NUMBER; j++) { 266 | ESP_ERROR_CHECK(strip->set_pixel(strip, j, (color.red * i) / 255, (color.green * i) / 255, (color.blue * i) / 255)); 267 | } 268 | ESP_ERROR_CHECK(strip->refresh(strip, 100)); 269 | vTaskDelay(pdMS_TO_TICKS(20)); // 调整渐变速度 270 | } 271 | } 272 | 273 | // 闪烁效果 274 | void led_set_blink(ws2812_strip_t *strip, led_color_t color, uint32_t interval_ms) { 275 | if (timer_state.active) { 276 | esp_timer_stop(led_timer); 277 | } 278 | 279 | // 设置定时器状态 280 | timer_state.strip = strip; 281 | timer_state.color = color; 282 | timer_state.active = true; 283 | timer_state.counter = 0; 284 | 285 | timer_state.effect_func = [](ws2812_strip_t* strip, led_color_t color) { 286 | static bool is_on = false; 287 | 288 | if (is_on) { 289 | for (int i = 0; i < CONFIG_WS2812_STRIP_LED_NUMBER; i++) { 290 | ESP_ERROR_CHECK(strip->set_pixel(strip, i, 0, 0, 0)); // 设置为黑色 291 | } 292 | ESP_ERROR_CHECK(strip->refresh(strip, 100)); 293 | } else { 294 | for (int i = 0; i < CONFIG_WS2812_STRIP_LED_NUMBER; i++) { 295 | ESP_ERROR_CHECK(strip->set_pixel(strip, i, color.red, color.green, color.blue)); 296 | } 297 | ESP_ERROR_CHECK(strip->refresh(strip, 100)); 298 | } 299 | is_on = !is_on; 300 | }; 301 | 302 | // 启动定时器,使用传入的时间间隔 303 | ESP_ERROR_CHECK(esp_timer_start_periodic(led_timer, interval_ms * 1000)); // 转换为微秒 304 | } 305 | 306 | // 彩虹效果 307 | void led_set_rainbow(ws2812_strip_t *strip, uint32_t interval_ms) { 308 | if (timer_state.active) { 309 | esp_timer_stop(led_timer); 310 | } 311 | 312 | // 设置定时器状态 313 | timer_state.strip = strip; 314 | timer_state.active = true; 315 | timer_state.counter = 0; 316 | 317 | timer_state.effect_func = [](ws2812_strip_t* strip, led_color_t color) { 318 | static uint16_t hue = 0; 319 | uint32_t red, green, blue; 320 | 321 | for (int i = 0; i < CONFIG_WS2812_STRIP_LED_NUMBER; i++) { 322 | uint16_t led_hue = hue + (i * 360 / CONFIG_WS2812_STRIP_LED_NUMBER); 323 | led_hue %= 360; // 确保色相值在0-359范围内 324 | led_strip_hsv2rgb(led_hue, 100, 100, &red, &green, &blue); 325 | ESP_ERROR_CHECK(strip->set_pixel(strip, i, red, green, blue)); 326 | } 327 | ESP_ERROR_CHECK(strip->refresh(strip, 100)); 328 | hue = (hue + 1) % 360; 329 | }; 330 | 331 | // 启动定时器 332 | ESP_ERROR_CHECK(esp_timer_start_periodic(led_timer, interval_ms * 1000)); 333 | } 334 | 335 | void ws2812_set(ws2812_strip_t *strip, led_color_t color, led_effect_t effect){ 336 | // 根据不同的效果值执行相应的 LED 控制操作 337 | switch (effect) { 338 | case LED_EFFECT_ON: 339 | //设置灯条状态为开启,并设置颜色 340 | led_set_on(strip, color); 341 | break; 342 | case LED_EFFECT_OFF: 343 | //关闭灯条 344 | led_set_off(strip); 345 | break; 346 | case LED_EFFECT_BREATH: 347 | // 设置呼吸效果 348 | led_set_breath(strip, color ,10); 349 | break; 350 | case LED_EFFECT_FADE_IN: 351 | //设置渐入效果 352 | led_set_fade_in(strip, color); 353 | break; 354 | case LED_EFFECT_BLINK_SLOW: 355 | //设置缓慢闪烁效果 356 | led_set_blink(strip, color,500); 357 | break; 358 | case LED_EFFECT_BLINK_FAST: 359 | // 快速闪烁效果 360 | led_set_blink(strip, color,100); 361 | break; 362 | case LED_EFFECT_RAINBOW: 363 | // 彩虹效果 364 | led_set_rainbow(strip, 20); 365 | break; 366 | } 367 | } 368 | 369 | // 设置 LED 跑马灯(从 index_start 到 index_end) 370 | void led_set_marquee(ws2812_strip_t *strip, int index_start, int index_end, led_color_t color, int delay_ms) { 371 | if (timer_state.active) { 372 | esp_timer_stop(led_timer); 373 | } 374 | if (index_start < 0 || index_end < 0 || index_start >= CONFIG_WS2812_STRIP_LED_NUMBER || index_end >= CONFIG_WS2812_STRIP_LED_NUMBER ) { 375 | ESP_LOGE(TAG, "无效的 LED 索引"); 376 | return; // 参数检查 377 | } 378 | 379 | // 逐个亮起 LED 380 | if (index_start < index_end) { 381 | // 从 index_start 到 index_end 382 | for (int i = index_start; i <= index_end; i++) { 383 | ESP_ERROR_CHECK(strip->set_pixel(strip, i, color.red, color.green, color.blue)); 384 | ESP_ERROR_CHECK(strip->refresh(strip, delay_ms)); // 刷新显示 385 | 386 | // 延迟以控制亮起速度 387 | vTaskDelay(pdMS_TO_TICKS(delay_ms)); 388 | } 389 | } else { 390 | // 从 index_start 到 index_end 391 | for (int i = index_start; i >= index_end; i--) { 392 | ESP_ERROR_CHECK(strip->set_pixel(strip, i, color.red, color.green, color.blue)); 393 | ESP_ERROR_CHECK(strip->refresh(strip, delay_ms)); // 刷新显示 394 | 395 | // 延迟以控制亮起速度 396 | vTaskDelay(pdMS_TO_TICKS(delay_ms)); 397 | } 398 | } 399 | 400 | // 确保最终的 LED 关闭 401 | led_set_off(strip); 402 | } 403 | 404 | // 全局变量用于存储渐变效果的状态 405 | static uint32_t gradient_start_h, gradient_start_s, gradient_start_v; 406 | static uint32_t gradient_end_h, gradient_end_s, gradient_end_v; 407 | static uint32_t gradient_steps; 408 | static uint32_t gradient_current_step = 0; 409 | 410 | // 渐变效果的定时器回调函数 411 | void gradient_effect_func(ws2812_strip_t* strip, led_color_t color) { 412 | if (gradient_current_step <= gradient_steps) { 413 | // 计算当前步骤的HSV值 414 | uint32_t current_h, current_s, current_v; 415 | 416 | // 处理色相的最短路径插值 417 | int32_t hue_diff = (int32_t)gradient_end_h - (int32_t)gradient_start_h; 418 | if (hue_diff > 180) { 419 | hue_diff -= 360; 420 | } else if (hue_diff < -180) { 421 | hue_diff += 360; 422 | } 423 | 424 | current_h = (gradient_start_h + (hue_diff * (int32_t)gradient_current_step) / (int32_t)gradient_steps) % 360; 425 | current_s = gradient_start_s + ((int32_t)((gradient_end_s - gradient_start_s) * gradient_current_step) / (int32_t)gradient_steps); 426 | current_v = gradient_start_v + ((int32_t)((gradient_end_v - gradient_start_v) * gradient_current_step) / (int32_t)gradient_steps); 427 | 428 | // 将HSV转换为RGB 429 | uint32_t red, green, blue; 430 | led_strip_hsv2rgb(current_h, current_s, current_v, &red, &green, &blue); 431 | 432 | // 设置所有LED的颜色 433 | for (int i = 0; i < CONFIG_WS2812_STRIP_LED_NUMBER; i++) { 434 | ESP_ERROR_CHECK(strip->set_pixel(strip, i, red, green, blue)); 435 | } 436 | ESP_ERROR_CHECK(strip->refresh(strip, 100)); 437 | 438 | gradient_current_step++; 439 | 440 | // 如果达到最后一步,停止定时器 441 | if (gradient_current_step > gradient_steps) { 442 | gradient_current_step = 0; // 重置步骤计数器 443 | esp_timer_stop(led_timer); 444 | } 445 | } 446 | } 447 | 448 | // 从一个颜色渐变到另一个颜色 449 | void led_set_color_gradient(ws2812_strip_t *strip, led_color_t start_color, led_color_t end_color, uint32_t duration_ms) { 450 | if (timer_state.active) { 451 | esp_timer_stop(led_timer); 452 | } 453 | 454 | // 将RGB颜色转换为HSV 455 | led_strip_rgb2hsv(start_color.red, start_color.green, start_color.blue, &gradient_start_h, &gradient_start_s, &gradient_start_v); 456 | led_strip_rgb2hsv(end_color.red, end_color.green, end_color.blue, &gradient_end_h, &gradient_end_s, &gradient_end_v); 457 | 458 | const uint32_t step_interval_ms = 20; // 50Hz刷新率 459 | 460 | gradient_steps = duration_ms / step_interval_ms; 461 | 462 | if (gradient_steps == 0) { 463 | gradient_steps = 1; 464 | } 465 | 466 | // 重置当前步骤计数器 467 | gradient_current_step = 0; 468 | 469 | // 设置定时器状态 470 | timer_state.strip = strip; 471 | timer_state.color = start_color; 472 | timer_state.effect_func = gradient_effect_func; 473 | timer_state.active = true; 474 | 475 | uint32_t interval_us = step_interval_ms * 1000; 476 | ESP_ERROR_CHECK(esp_timer_start_periodic(led_timer, interval_us)); 477 | } 478 | 479 | // 更新LED显示 480 | void update_led_display(ws2812_strip_t *strip) { 481 | strip->refresh(strip, 100); 482 | } 483 | 484 | 485 | 486 | #elif defined(CONFIG_WS2812_MODE_MATRIX) 487 | 488 | ws2812_matrix_t* ws2812_matrix_create() { 489 | ws2812_matrix_t *matrix = (ws2812_matrix_t *)malloc(sizeof(ws2812_matrix_t)); 490 | if (!matrix) { 491 | ESP_LOGE(TAG, "Failed to allocate memory for ws2812_matrix_t"); 492 | return NULL; 493 | } 494 | 495 | matrix->config.matrixWidth = CONFIG_WS2812_MATRIX_WIDTH; 496 | matrix->config.matrixHeight = CONFIG_WS2812_MATRIX_HEIGHT; 497 | matrix->config.pin = CONFIG_WS2812_TX_GPIO; 498 | 499 | // 设置起始角落 500 | #if defined(CONFIG_WS2812_MATRIX_START_TOP_LEFT) 501 | matrix->config.matrixType = MATRIX_START_TOP_LEFT; 502 | #elif defined(CONFIG_WS2812_MATRIX_START_TOP_RIGHT) 503 | matrix->config.matrixType = MATRIX_START_TOP_RIGHT; 504 | #elif defined(CONFIG_WS2812_MATRIX_START_BOTTOM_LEFT) 505 | matrix->config.matrixType = MATRIX_START_BOTTOM_LEFT; 506 | #elif defined(CONFIG_WS2812_MATRIX_START_BOTTOM_RIGHT) 507 | matrix->config.matrixType = MATRIX_START_BOTTOM_RIGHT; 508 | #endif 509 | 510 | // 设置布局方向 511 | #if defined(CONFIG_WS2812_MATRIX_LAYOUT_HORIZONTAL) 512 | matrix->config.matrixType |= MATRIX_LAYOUT_HORIZONTAL; 513 | #elif defined(CONFIG_WS2812_MATRIX_LAYOUT_VERTICAL) 514 | matrix->config.matrixType |= MATRIX_LAYOUT_VERTICAL; 515 | #endif 516 | 517 | // 设置扫描顺序 518 | #if defined(CONFIG_WS2812_MATRIX_SCAN_PROGRESSIVE) 519 | matrix->config.matrixType |= MATRIX_SCAN_PROGRESSIVE; 520 | #elif defined(CONFIG_WS2812_MATRIX_SCAN_ZIGZAG) 521 | matrix->config.matrixType |= MATRIX_SCAN_ZIGZAG; 522 | #endif 523 | 524 | // 初始化RMT配置 525 | rmt_config_t config = RMT_DEFAULT_CONFIG_TX((gpio_num_t)CONFIG_WS2812_TX_GPIO, RMT_TX_CHANNEL); 526 | config.clk_div = 2; 527 | 528 | ESP_ERROR_CHECK(rmt_config(&config)); 529 | ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0)); 530 | 531 | // 初始化LED条配置 532 | led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG( 533 | CONFIG_WS2812_MATRIX_WIDTH * CONFIG_WS2812_MATRIX_HEIGHT, 534 | (led_strip_dev_t)config.channel 535 | ); 536 | matrix->strip = led_strip_new_rmt_ws2812(&strip_config); 537 | 538 | if (!matrix->strip) { 539 | ESP_LOGE(TAG, "Failed to create WS2812 strip"); 540 | free(matrix); 541 | return NULL; 542 | } 543 | 544 | ESP_ERROR_CHECK(matrix->strip->clear(matrix->strip, 100)); 545 | return matrix; 546 | } 547 | 548 | 549 | void led_matrix_set_pixel(ws2812_matrix_t *matrix, int x, int y, led_color_t color) { 550 | if (!matrix || !matrix->strip) { 551 | ESP_LOGE(TAG, "Matrix or strip handle is NULL"); 552 | return; 553 | } 554 | 555 | // 检查坐标是否在矩阵范围内 556 | if (x < 0 || x >= matrix->config.matrixWidth || y < 0 || y >= matrix->config.matrixHeight) { 557 | ESP_LOGE(TAG, "Coordinates out of bounds"); 558 | return; 559 | } 560 | 561 | int index; 562 | 563 | // 判断布局是行优先还是列优先 564 | if (matrix->config.matrixType & MATRIX_LAYOUT_VERTICAL) { 565 | // 列优先布局 566 | if (matrix->config.matrixType & MATRIX_SCAN_ZIGZAG) { 567 | // 之字形扫描 568 | index = (y % 2 == 0) ? (y * matrix->config.matrixWidth + x) 569 | : (y * matrix->config.matrixWidth + (matrix->config.matrixWidth - 1 - x)); 570 | } else { 571 | // 连续扫描 572 | index = y * matrix->config.matrixWidth + x; 573 | } 574 | } else { 575 | // 行优先布局 576 | if (matrix->config.matrixType & MATRIX_SCAN_ZIGZAG) { 577 | // 之字形扫描 578 | index = (x % 2 == 0) ? (x * matrix->config.matrixHeight + y) 579 | : (x * matrix->config.matrixHeight + (matrix->config.matrixHeight - 1 - y)); 580 | } else { 581 | // 连续扫描 582 | index = x * matrix->config.matrixHeight + y; 583 | } 584 | } 585 | 586 | // 调整索引以处理起始角 587 | switch (matrix->config.matrixType & MATRIX_START_CORNER) { 588 | case MATRIX_START_TOP_RIGHT: 589 | index = (matrix->config.matrixWidth * matrix->config.matrixHeight - 1) - index; 590 | break; 591 | case MATRIX_START_BOTTOM_LEFT: 592 | index = (matrix->config.matrixHeight - 1 - y) * matrix->config.matrixWidth + x; 593 | break; 594 | case MATRIX_START_BOTTOM_RIGHT: 595 | index = (matrix->config.matrixWidth * matrix->config.matrixHeight - 1) - index; 596 | break; 597 | case MATRIX_START_TOP_LEFT: 598 | default: 599 | // 无需调整 600 | break; 601 | } 602 | 603 | // 设置指定 LED 的颜色 604 | matrix->strip->set_pixel(matrix->strip, index, color.red, color.green, color.blue); 605 | } 606 | 607 | // 刷新显示以更新 LED 608 | void led_matrix_show(ws2812_matrix_t *matrix) { 609 | if (matrix == NULL || matrix->strip == NULL) { 610 | ESP_LOGE(TAG, "Matrix or strip handle is NULL"); 611 | return; 612 | } 613 | matrix->strip->refresh(matrix->strip, 100); 614 | } 615 | 616 | void led_matrix_clear(ws2812_matrix_t *matrix){ 617 | 618 | if (matrix == NULL || matrix->strip == NULL) { 619 | ESP_LOGE(TAG, "Matrix or strip handle is NULL"); 620 | return; 621 | } 622 | matrix->strip->clear(matrix->strip,100); // 清空所有 LED 623 | } 624 | 625 | #endif 626 | --------------------------------------------------------------------------------