ESPHome 2026.5.3
Loading...
Searching...
No Matches
led_strip.cpp
Go to the documentation of this file.
1#include "led_strip.h"
2#include <cinttypes>
3
4#ifdef USE_ESP32
5
7#include "esphome/core/log.h"
8
9#include <esp_attr.h>
10#include <esp_clk_tree.h>
11
13
14static const char *const TAG = "esp32_rmt_led_strip";
15
16static const size_t RMT_SYMBOLS_PER_BYTE = 8;
17
18// Query the RMT default clock source frequency. This varies by variant:
19// APB (80MHz) on ESP32/S2/S3/C3, PLL_F80M (80MHz) on C6/P4, XTAL (32MHz) on H2.
20// Worst-case reset time is WS2811 at 300µs = 24000 ticks at 80MHz, well within
21// the 15-bit rmt_symbol_word_t duration field max of 32767.
22static uint32_t rmt_resolution_hz() {
23 uint32_t freq;
24 esp_clk_tree_src_get_freq_hz((soc_module_clk_t) RMT_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &freq);
25 return freq;
26}
27
28#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
29static size_t IRAM_ATTR HOT encoder_callback(const void *data, size_t size, size_t symbols_written, size_t symbols_free,
30 rmt_symbol_word_t *symbols, bool *done, void *arg) {
31 auto *params = static_cast<LedParams *>(arg);
32 const auto *bytes = static_cast<const uint8_t *>(data);
33 size_t index = symbols_written / RMT_SYMBOLS_PER_BYTE;
34
35 // convert byte to symbols
36 if (index < size) {
37 if (symbols_free < RMT_SYMBOLS_PER_BYTE) {
38 return 0;
39 }
40 for (size_t i = 0; i < RMT_SYMBOLS_PER_BYTE; i++) {
41 if (bytes[index] & (1 << (7 - i))) {
42 symbols[i] = params->bit1;
43 } else {
44 symbols[i] = params->bit0;
45 }
46 }
47#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
48 if ((index + 1) >= size && params->reset.duration0 == 0 && params->reset.duration1 == 0) {
49 *done = true;
50 }
51#endif
52 return RMT_SYMBOLS_PER_BYTE;
53 }
54
55 // send reset
56 if (symbols_free < 1) {
57 return 0;
58 }
59 symbols[0] = params->reset;
60 *done = true;
61 return 1;
62}
63#endif
64
66 size_t buffer_size = this->get_buffer_size_();
67
69 this->buf_ = allocator.allocate(buffer_size);
70 if (this->buf_ == nullptr) {
71 ESP_LOGE(TAG, "Cannot allocate LED buffer!");
72 this->mark_failed();
73 return;
74 }
75 memset(this->buf_, 0, buffer_size);
76
77 this->effect_data_ = allocator.allocate(this->num_leds_);
78 if (this->effect_data_ == nullptr) {
79 ESP_LOGE(TAG, "Cannot allocate effect data!");
80 this->mark_failed();
81 return;
82 }
83
84#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
85 // copy of the led buffer
86 this->rmt_buf_ = allocator.allocate(buffer_size);
87#else
89
90 // 8 bits per byte, 1 rmt_symbol_word_t per bit + 1 rmt_symbol_word_t for reset
91 this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 + 1);
92#endif
93
94 rmt_tx_channel_config_t channel;
95 memset(&channel, 0, sizeof(channel));
96 channel.clk_src = RMT_CLK_SRC_DEFAULT;
97 channel.resolution_hz = rmt_resolution_hz();
98 channel.gpio_num = gpio_num_t(this->pin_);
99 channel.mem_block_symbols = this->rmt_symbols_;
100 channel.trans_queue_depth = 1;
101 channel.flags.invert_out = this->invert_out_;
102 channel.flags.with_dma = this->use_dma_;
103 channel.intr_priority = 0;
104 if (rmt_new_tx_channel(&channel, &this->channel_) != ESP_OK) {
105 ESP_LOGE(TAG, "Channel creation failed");
106 this->mark_failed();
107 return;
108 }
109
110#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
111 rmt_simple_encoder_config_t encoder;
112 memset(&encoder, 0, sizeof(encoder));
113 encoder.callback = encoder_callback;
114 encoder.arg = &this->params_;
115 encoder.min_chunk_size = RMT_SYMBOLS_PER_BYTE;
116 if (rmt_new_simple_encoder(&encoder, &this->encoder_) != ESP_OK) {
117 ESP_LOGE(TAG, "Encoder creation failed");
118 this->mark_failed();
119 return;
120 }
121#else
122 rmt_copy_encoder_config_t encoder;
123 memset(&encoder, 0, sizeof(encoder));
124 if (rmt_new_copy_encoder(&encoder, &this->encoder_) != ESP_OK) {
125 ESP_LOGE(TAG, "Encoder creation failed");
126 this->mark_failed();
127 return;
128 }
129#endif
130
131 if (rmt_enable(this->channel_) != ESP_OK) {
132 ESP_LOGE(TAG, "Enabling channel failed");
133 this->mark_failed();
134 return;
135 }
136}
137
139 uint32_t bit1_low, uint32_t reset_time_high, uint32_t reset_time_low) {
140 float ratio = (float) rmt_resolution_hz() / 1e09f;
141
142 // 0-bit
143 this->params_.bit0.duration0 = (uint32_t) (ratio * bit0_high);
144 this->params_.bit0.level0 = 1;
145 this->params_.bit0.duration1 = (uint32_t) (ratio * bit0_low);
146 this->params_.bit0.level1 = 0;
147 // 1-bit
148 this->params_.bit1.duration0 = (uint32_t) (ratio * bit1_high);
149 this->params_.bit1.level0 = 1;
150 this->params_.bit1.duration1 = (uint32_t) (ratio * bit1_low);
151 this->params_.bit1.level1 = 0;
152 // reset
153 this->params_.reset.duration0 = (uint32_t) (ratio * reset_time_high);
154 this->params_.reset.level0 = 1;
155 this->params_.reset.duration1 = (uint32_t) (ratio * reset_time_low);
156 this->params_.reset.level1 = 0;
157}
158
160 // protect from refreshing too often
161 uint32_t now = micros();
162 auto rate = this->max_refresh_rate_.value_or(0);
163 if (rate != 0 && (now - this->last_refresh_) < rate) {
164 // try again next loop iteration, so that this change won't get lost
165 this->schedule_show();
166 return;
167 }
168 this->last_refresh_ = now;
169 this->mark_shown_();
170
171 ESP_LOGVV(TAG, "Writing RGB values to bus");
172
173 esp_err_t error = rmt_tx_wait_all_done(this->channel_, 1000);
174 if (error != ESP_OK) {
175 ESP_LOGE(TAG, "RMT TX timeout");
176 this->status_set_warning();
177 return;
178 }
180
181#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
182 memcpy(this->rmt_buf_, this->buf_, this->get_buffer_size_());
183#else
184 size_t buffer_size = this->get_buffer_size_();
185
186 size_t size = 0;
187 size_t len = 0;
188 uint8_t *psrc = this->buf_;
189 rmt_symbol_word_t *pdest = this->rmt_buf_;
190 while (size < buffer_size) {
191 uint8_t b = *psrc;
192 for (int i = 0; i < 8; i++) {
193 pdest->val = b & (1 << (7 - i)) ? this->params_.bit1.val : this->params_.bit0.val;
194 pdest++;
195 len++;
196 }
197 size++;
198 psrc++;
199 }
200
201 if (this->params_.reset.duration0 > 0 || this->params_.reset.duration1 > 0) {
202 pdest->val = this->params_.reset.val;
203 pdest++;
204 len++;
205 }
206#endif
207
208 rmt_transmit_config_t config;
209 memset(&config, 0, sizeof(config));
210#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
211 error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, this->get_buffer_size_(), &config);
212#else
213 error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, len * sizeof(rmt_symbol_word_t), &config);
214#endif
215 if (error != ESP_OK) {
216 ESP_LOGE(TAG, "RMT TX error");
217 this->status_set_warning();
218 return;
219 }
220 this->status_clear_warning();
221}
222
224 int32_t r = 0, g = 0, b = 0;
225 switch (this->rgb_order_) {
226 case ORDER_RGB:
227 r = 0;
228 g = 1;
229 b = 2;
230 break;
231 case ORDER_RBG:
232 r = 0;
233 g = 2;
234 b = 1;
235 break;
236 case ORDER_GRB:
237 r = 1;
238 g = 0;
239 b = 2;
240 break;
241 case ORDER_GBR:
242 r = 2;
243 g = 0;
244 b = 1;
245 break;
246 case ORDER_BGR:
247 r = 2;
248 g = 1;
249 b = 0;
250 break;
251 case ORDER_BRG:
252 r = 1;
253 g = 2;
254 b = 0;
255 break;
256 }
257 uint8_t multiplier = this->is_rgbw_ || this->is_wrgb_ ? 4 : 3;
258 uint8_t white = this->is_wrgb_ ? 0 : 3;
259
260 return {this->buf_ + (index * multiplier) + r + this->is_wrgb_,
261 this->buf_ + (index * multiplier) + g + this->is_wrgb_,
262 this->buf_ + (index * multiplier) + b + this->is_wrgb_,
263 this->is_rgbw_ || this->is_wrgb_ ? this->buf_ + (index * multiplier) + white : nullptr,
264 &this->effect_data_[index],
265 &this->correction_};
266}
267
269 ESP_LOGCONFIG(TAG,
270 "ESP32 RMT LED Strip:\n"
271 " Pin: %u",
272 this->pin_);
273 ESP_LOGCONFIG(TAG, " RMT Symbols: %" PRIu32, this->rmt_symbols_);
274 const char *rgb_order;
275 switch (this->rgb_order_) {
276 case ORDER_RGB:
277 rgb_order = "RGB";
278 break;
279 case ORDER_RBG:
280 rgb_order = "RBG";
281 break;
282 case ORDER_GRB:
283 rgb_order = "GRB";
284 break;
285 case ORDER_GBR:
286 rgb_order = "GBR";
287 break;
288 case ORDER_BGR:
289 rgb_order = "BGR";
290 break;
291 case ORDER_BRG:
292 rgb_order = "BRG";
293 break;
294 default:
295 rgb_order = "UNKNOWN";
296 break;
297 }
298 ESP_LOGCONFIG(TAG,
299 " RGB Order: %s\n"
300 " Max refresh rate: %" PRIu32 "\n"
301 " Number of LEDs: %u",
302 rgb_order, this->max_refresh_rate_.value_or(0), this->num_leds_);
303}
304
306
307} // namespace esphome::esp32_rmt_led_strip
308
309#endif // USE_ESP32
void mark_failed()
Mark this component as failed.
void status_clear_warning()
Definition component.h:306
An STL allocator that uses SPI or internal RAM.
Definition helpers.h:2053
T * allocate(size_t n)
Definition helpers.h:2080
void write_state(light::LightState *state) override
light::ESPColorView get_view_internal(int32_t index) const override
void set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high, uint32_t bit1_low, uint32_t reset_time_high, uint32_t reset_time_low)
This class represents the communication layer between the front-end MQTT layer and the hardware outpu...
Definition light_state.h:93
bool state
Definition fan.h:2
constexpr float HARDWARE
For components that deal with hardware and are very important like GPIO switch.
Definition component.h:41
std::string size_t len
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition hal.cpp:48
uint16_t size
Definition helpers.cpp:25
uint32_t IRAM_ATTR HOT micros()
Definition hal.cpp:43
static void uint32_t