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