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