ESPHome 2026.8.1
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 const light::ChannelColors &colors = this->channel_colors_;
225 uint8_t *led = this->buf_ + (index * colors.bytes_per_led());
226 return {led + colors.r,
227 led + colors.g,
228 led + colors.b,
229 colors.has_white() ? led + colors.w : nullptr,
230 &this->effect_data_[index],
231 &this->correction_};
232}
233
235 ESP_LOGCONFIG(TAG,
236 "ESP32 RMT LED Strip:\n"
237 " Pin: %u",
238 this->pin_);
239 ESP_LOGCONFIG(TAG, " RMT Symbols: %" PRIu32, this->rmt_symbols_);
240 char channel_colors[5];
241 ESP_LOGCONFIG(TAG,
242 " Channel colors: %s\n"
243 " Max refresh rate: %" PRIu32 "\n"
244 " Number of LEDs: %u",
245 this->channel_colors_.to_string(channel_colors), this->max_refresh_rate_.value_or(0), this->num_leds_);
246}
247
249
250} // namespace esphome::esp32_rmt_led_strip
251
252#endif // USE_ESP32
void mark_failed()
Mark this component as failed.
void status_clear_warning()
Definition component.h:289
An STL allocator that uses SPI or internal RAM.
Definition helpers.h:2099
T * allocate(size_t n)
Definition helpers.h:2126
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:43
const void size_t len
Definition hal.h:64
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
Which byte of an addressable LED's data carries each colour.
const char * to_string(char *buf) const
Write the order back out as text, e.g.