ESPHome 2025.9.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 (int32_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 return RMT_SYMBOLS_PER_BYTE;
46 }
47
48 // send reset
49 if (symbols_free < 1) {
50 return 0;
51 }
52 symbols[0] = params->reset;
53 *done = true;
54 return 1;
55}
56#endif
57
59 size_t buffer_size = this->get_buffer_size_();
60
62 this->buf_ = allocator.allocate(buffer_size);
63 if (this->buf_ == nullptr) {
64 ESP_LOGE(TAG, "Cannot allocate LED buffer!");
65 this->mark_failed();
66 return;
67 }
68 memset(this->buf_, 0, buffer_size);
69
70 this->effect_data_ = allocator.allocate(this->num_leds_);
71 if (this->effect_data_ == nullptr) {
72 ESP_LOGE(TAG, "Cannot allocate effect data!");
73 this->mark_failed();
74 return;
75 }
76
77#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
78 // copy of the led buffer
79 this->rmt_buf_ = allocator.allocate(buffer_size);
80#else
82
83 // 8 bits per byte, 1 rmt_symbol_word_t per bit + 1 rmt_symbol_word_t for reset
84 this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 + 1);
85#endif
86
87 rmt_tx_channel_config_t channel;
88 memset(&channel, 0, sizeof(channel));
89 channel.clk_src = RMT_CLK_SRC_DEFAULT;
90 channel.resolution_hz = RMT_CLK_FREQ / RMT_CLK_DIV;
91 channel.gpio_num = gpio_num_t(this->pin_);
92 channel.mem_block_symbols = this->rmt_symbols_;
93 channel.trans_queue_depth = 1;
94 channel.flags.io_loop_back = 0;
95 channel.flags.io_od_mode = 0;
96 channel.flags.invert_out = 0;
97 channel.flags.with_dma = this->use_dma_;
98 channel.intr_priority = 0;
99 if (rmt_new_tx_channel(&channel, &this->channel_) != ESP_OK) {
100 ESP_LOGE(TAG, "Channel creation failed");
101 this->mark_failed();
102 return;
103 }
104
105#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
106 rmt_simple_encoder_config_t encoder;
107 memset(&encoder, 0, sizeof(encoder));
108 encoder.callback = encoder_callback;
109 encoder.arg = &this->params_;
110 encoder.min_chunk_size = RMT_SYMBOLS_PER_BYTE;
111 if (rmt_new_simple_encoder(&encoder, &this->encoder_) != ESP_OK) {
112 ESP_LOGE(TAG, "Encoder creation failed");
113 this->mark_failed();
114 return;
115 }
116#else
117 rmt_copy_encoder_config_t encoder;
118 memset(&encoder, 0, sizeof(encoder));
119 if (rmt_new_copy_encoder(&encoder, &this->encoder_) != ESP_OK) {
120 ESP_LOGE(TAG, "Encoder creation failed");
121 this->mark_failed();
122 return;
123 }
124#endif
125
126 if (rmt_enable(this->channel_) != ESP_OK) {
127 ESP_LOGE(TAG, "Enabling channel failed");
128 this->mark_failed();
129 return;
130 }
131}
132
133void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high,
134 uint32_t bit1_low, uint32_t reset_time_high, uint32_t reset_time_low) {
135 float ratio = (float) RMT_CLK_FREQ / RMT_CLK_DIV / 1e09f;
136
137 // 0-bit
138 this->params_.bit0.duration0 = (uint32_t) (ratio * bit0_high);
139 this->params_.bit0.level0 = 1;
140 this->params_.bit0.duration1 = (uint32_t) (ratio * bit0_low);
141 this->params_.bit0.level1 = 0;
142 // 1-bit
143 this->params_.bit1.duration0 = (uint32_t) (ratio * bit1_high);
144 this->params_.bit1.level0 = 1;
145 this->params_.bit1.duration1 = (uint32_t) (ratio * bit1_low);
146 this->params_.bit1.level1 = 0;
147 // reset
148 this->params_.reset.duration0 = (uint32_t) (ratio * reset_time_high);
149 this->params_.reset.level0 = 1;
150 this->params_.reset.duration1 = (uint32_t) (ratio * reset_time_low);
151 this->params_.reset.level1 = 0;
152}
153
155 // protect from refreshing too often
156 uint32_t now = micros();
157 if (*this->max_refresh_rate_ != 0 && (now - this->last_refresh_) < *this->max_refresh_rate_) {
158 // try again next loop iteration, so that this change won't get lost
159 this->schedule_show();
160 return;
161 }
162 this->last_refresh_ = now;
163 this->mark_shown_();
164
165 ESP_LOGVV(TAG, "Writing RGB values to bus");
166
167 esp_err_t error = rmt_tx_wait_all_done(this->channel_, 1000);
168 if (error != ESP_OK) {
169 ESP_LOGE(TAG, "RMT TX timeout");
170 this->status_set_warning();
171 return;
172 }
174
175#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
176 memcpy(this->rmt_buf_, this->buf_, this->get_buffer_size_());
177#else
178 size_t buffer_size = this->get_buffer_size_();
179
180 size_t size = 0;
181 size_t len = 0;
182 uint8_t *psrc = this->buf_;
183 rmt_symbol_word_t *pdest = this->rmt_buf_;
184 while (size < buffer_size) {
185 uint8_t b = *psrc;
186 for (int i = 0; i < 8; i++) {
187 pdest->val = b & (1 << (7 - i)) ? this->params_.bit1.val : this->params_.bit0.val;
188 pdest++;
189 len++;
190 }
191 size++;
192 psrc++;
193 }
194
195 if (this->params_.reset.duration0 > 0 || this->params_.reset.duration1 > 0) {
196 pdest->val = this->params_.reset.val;
197 pdest++;
198 len++;
199 }
200#endif
201
202 rmt_transmit_config_t config;
203 memset(&config, 0, sizeof(config));
204#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
205 error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, this->get_buffer_size_(), &config);
206#else
207 error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, len * sizeof(rmt_symbol_word_t), &config);
208#endif
209 if (error != ESP_OK) {
210 ESP_LOGE(TAG, "RMT TX error");
211 this->status_set_warning();
212 return;
213 }
214 this->status_clear_warning();
215}
216
218 int32_t r = 0, g = 0, b = 0;
219 switch (this->rgb_order_) {
220 case ORDER_RGB:
221 r = 0;
222 g = 1;
223 b = 2;
224 break;
225 case ORDER_RBG:
226 r = 0;
227 g = 2;
228 b = 1;
229 break;
230 case ORDER_GRB:
231 r = 1;
232 g = 0;
233 b = 2;
234 break;
235 case ORDER_GBR:
236 r = 2;
237 g = 0;
238 b = 1;
239 break;
240 case ORDER_BGR:
241 r = 2;
242 g = 1;
243 b = 0;
244 break;
245 case ORDER_BRG:
246 r = 1;
247 g = 2;
248 b = 0;
249 break;
250 }
251 uint8_t multiplier = this->is_rgbw_ || this->is_wrgb_ ? 4 : 3;
252 uint8_t white = this->is_wrgb_ ? 0 : 3;
253
254 return {this->buf_ + (index * multiplier) + r + this->is_wrgb_,
255 this->buf_ + (index * multiplier) + g + this->is_wrgb_,
256 this->buf_ + (index * multiplier) + b + this->is_wrgb_,
257 this->is_rgbw_ || this->is_wrgb_ ? this->buf_ + (index * multiplier) + white : nullptr,
258 &this->effect_data_[index],
259 &this->correction_};
260}
261
263 ESP_LOGCONFIG(TAG,
264 "ESP32 RMT LED Strip:\n"
265 " Pin: %u",
266 this->pin_);
267 ESP_LOGCONFIG(TAG, " RMT Symbols: %" PRIu32, this->rmt_symbols_);
268 const char *rgb_order;
269 switch (this->rgb_order_) {
270 case ORDER_RGB:
271 rgb_order = "RGB";
272 break;
273 case ORDER_RBG:
274 rgb_order = "RBG";
275 break;
276 case ORDER_GRB:
277 rgb_order = "GRB";
278 break;
279 case ORDER_GBR:
280 rgb_order = "GBR";
281 break;
282 case ORDER_BGR:
283 rgb_order = "BGR";
284 break;
285 case ORDER_BRG:
286 rgb_order = "BRG";
287 break;
288 default:
289 rgb_order = "UNKNOWN";
290 break;
291 }
292 ESP_LOGCONFIG(TAG,
293 " RGB Order: %s\n"
294 " Max refresh rate: %" PRIu32 "\n"
295 " Number of LEDs: %u",
296 rgb_order, *this->max_refresh_rate_, this->num_leds_);
297}
298
300
301} // namespace esp32_rmt_led_strip
302} // namespace esphome
303
304#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:818
T * allocate(size_t n)
Definition helpers.h:838
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:66
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:49
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:279
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition core.cpp:31
uint32_t IRAM_ATTR HOT micros()
Definition core.cpp:30