ESPHome 2025.12.0-dev
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
11namespace esphome {
12namespace esp32_rmt_led_strip {
13
14static const char *const TAG = "esp32_rmt_led_strip";
15
16#ifdef USE_ESP32_VARIANT_ESP32H2
17static const uint32_t RMT_CLK_FREQ = 32000000;
18static const uint8_t RMT_CLK_DIV = 1;
19#else
20static const uint32_t RMT_CLK_FREQ = 80000000;
21static const uint8_t RMT_CLK_DIV = 2;
22#endif
23
24static const size_t RMT_SYMBOLS_PER_BYTE = 8;
25
26#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
27static size_t IRAM_ATTR HOT encoder_callback(const void *data, size_t size, size_t symbols_written, size_t symbols_free,
28 rmt_symbol_word_t *symbols, bool *done, void *arg) {
29 auto *params = static_cast<LedParams *>(arg);
30 const auto *bytes = static_cast<const uint8_t *>(data);
31 size_t index = symbols_written / RMT_SYMBOLS_PER_BYTE;
32
33 // convert byte to symbols
34 if (index < size) {
35 if (symbols_free < RMT_SYMBOLS_PER_BYTE) {
36 return 0;
37 }
38 for (size_t i = 0; i < RMT_SYMBOLS_PER_BYTE; i++) {
39 if (bytes[index] & (1 << (7 - i))) {
40 symbols[i] = params->bit1;
41 } else {
42 symbols[i] = params->bit0;
43 }
44 }
45#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
46 if ((index + 1) >= size && params->reset.duration0 == 0 && params->reset.duration1 == 0) {
47 *done = true;
48 }
49#endif
50 return RMT_SYMBOLS_PER_BYTE;
51 }
52
53 // send reset
54 if (symbols_free < 1) {
55 return 0;
56 }
57 symbols[0] = params->reset;
58 *done = true;
59 return 1;
60}
61#endif
62
64 size_t buffer_size = this->get_buffer_size_();
65
67 this->buf_ = allocator.allocate(buffer_size);
68 if (this->buf_ == nullptr) {
69 ESP_LOGE(TAG, "Cannot allocate LED buffer!");
70 this->mark_failed();
71 return;
72 }
73 memset(this->buf_, 0, buffer_size);
74
75 this->effect_data_ = allocator.allocate(this->num_leds_);
76 if (this->effect_data_ == nullptr) {
77 ESP_LOGE(TAG, "Cannot allocate effect data!");
78 this->mark_failed();
79 return;
80 }
81
82#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
83 // copy of the led buffer
84 this->rmt_buf_ = allocator.allocate(buffer_size);
85#else
87
88 // 8 bits per byte, 1 rmt_symbol_word_t per bit + 1 rmt_symbol_word_t for reset
89 this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 + 1);
90#endif
91
92 rmt_tx_channel_config_t channel;
93 memset(&channel, 0, sizeof(channel));
94 channel.clk_src = RMT_CLK_SRC_DEFAULT;
95 channel.resolution_hz = RMT_CLK_FREQ / RMT_CLK_DIV;
96 channel.gpio_num = gpio_num_t(this->pin_);
97 channel.mem_block_symbols = this->rmt_symbols_;
98 channel.trans_queue_depth = 1;
99 channel.flags.io_loop_back = 0;
100 channel.flags.io_od_mode = 0;
101 channel.flags.invert_out = 0;
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
138void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high,
139 uint32_t bit1_low, uint32_t reset_time_high, uint32_t reset_time_low) {
140 float ratio = (float) RMT_CLK_FREQ / RMT_CLK_DIV / 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 if (*this->max_refresh_rate_ != 0 && (now - this->last_refresh_) < *this->max_refresh_rate_) {
163 // try again next loop iteration, so that this change won't get lost
164 this->schedule_show();
165 return;
166 }
167 this->last_refresh_ = now;
168 this->mark_shown_();
169
170 ESP_LOGVV(TAG, "Writing RGB values to bus");
171
172 esp_err_t error = rmt_tx_wait_all_done(this->channel_, 1000);
173 if (error != ESP_OK) {
174 ESP_LOGE(TAG, "RMT TX timeout");
175 this->status_set_warning();
176 return;
177 }
179
180#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
181 memcpy(this->rmt_buf_, this->buf_, this->get_buffer_size_());
182#else
183 size_t buffer_size = this->get_buffer_size_();
184
185 size_t size = 0;
186 size_t len = 0;
187 uint8_t *psrc = this->buf_;
188 rmt_symbol_word_t *pdest = this->rmt_buf_;
189 while (size < buffer_size) {
190 uint8_t b = *psrc;
191 for (int i = 0; i < 8; i++) {
192 pdest->val = b & (1 << (7 - i)) ? this->params_.bit1.val : this->params_.bit0.val;
193 pdest++;
194 len++;
195 }
196 size++;
197 psrc++;
198 }
199
200 if (this->params_.reset.duration0 > 0 || this->params_.reset.duration1 > 0) {
201 pdest->val = this->params_.reset.val;
202 pdest++;
203 len++;
204 }
205#endif
206
207 rmt_transmit_config_t config;
208 memset(&config, 0, sizeof(config));
209#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
210 error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, this->get_buffer_size_(), &config);
211#else
212 error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, len * sizeof(rmt_symbol_word_t), &config);
213#endif
214 if (error != ESP_OK) {
215 ESP_LOGE(TAG, "RMT TX error");
216 this->status_set_warning();
217 return;
218 }
219 this->status_clear_warning();
220}
221
223 int32_t r = 0, g = 0, b = 0;
224 switch (this->rgb_order_) {
225 case ORDER_RGB:
226 r = 0;
227 g = 1;
228 b = 2;
229 break;
230 case ORDER_RBG:
231 r = 0;
232 g = 2;
233 b = 1;
234 break;
235 case ORDER_GRB:
236 r = 1;
237 g = 0;
238 b = 2;
239 break;
240 case ORDER_GBR:
241 r = 2;
242 g = 0;
243 b = 1;
244 break;
245 case ORDER_BGR:
246 r = 2;
247 g = 1;
248 b = 0;
249 break;
250 case ORDER_BRG:
251 r = 1;
252 g = 2;
253 b = 0;
254 break;
255 }
256 uint8_t multiplier = this->is_rgbw_ || this->is_wrgb_ ? 4 : 3;
257 uint8_t white = this->is_wrgb_ ? 0 : 3;
258
259 return {this->buf_ + (index * multiplier) + r + this->is_wrgb_,
260 this->buf_ + (index * multiplier) + g + this->is_wrgb_,
261 this->buf_ + (index * multiplier) + b + this->is_wrgb_,
262 this->is_rgbw_ || this->is_wrgb_ ? this->buf_ + (index * multiplier) + white : nullptr,
263 &this->effect_data_[index],
264 &this->correction_};
265}
266
268 ESP_LOGCONFIG(TAG,
269 "ESP32 RMT LED Strip:\n"
270 " Pin: %u",
271 this->pin_);
272 ESP_LOGCONFIG(TAG, " RMT Symbols: %" PRIu32, this->rmt_symbols_);
273 const char *rgb_order;
274 switch (this->rgb_order_) {
275 case ORDER_RGB:
276 rgb_order = "RGB";
277 break;
278 case ORDER_RBG:
279 rgb_order = "RBG";
280 break;
281 case ORDER_GRB:
282 rgb_order = "GRB";
283 break;
284 case ORDER_GBR:
285 rgb_order = "GBR";
286 break;
287 case ORDER_BGR:
288 rgb_order = "BGR";
289 break;
290 case ORDER_BRG:
291 rgb_order = "BRG";
292 break;
293 default:
294 rgb_order = "UNKNOWN";
295 break;
296 }
297 ESP_LOGCONFIG(TAG,
298 " RGB Order: %s\n"
299 " Max refresh rate: %" PRIu32 "\n"
300 " Number of LEDs: %u",
301 rgb_order, *this->max_refresh_rate_, this->num_leds_);
302}
303
305
306} // namespace esp32_rmt_led_strip
307} // namespace esphome
308
309#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:1084
T * allocate(size_t n)
Definition helpers.h:1104
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:69
bool state
Definition fan.h:0
const float HARDWARE
For components that deal with hardware and are very important like GPIO switch.
Definition component.cpp:58
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:500
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition core.cpp:33
uint32_t IRAM_ATTR HOT micros()
Definition core.cpp:32