ESPHome  2025.2.0
led_strip.cpp
Go to the documentation of this file.
1 #include "led_strip.h"
2 #include <cinttypes>
3 
4 #ifdef USE_ESP32
5 
6 #include "esphome/core/helpers.h"
7 #include "esphome/core/log.h"
8 
9 #include <esp_attr.h>
10 
11 namespace esphome {
12 namespace esp32_rmt_led_strip {
13 
14 static const char *const TAG = "esp32_rmt_led_strip";
15 
16 #ifdef USE_ESP32_VARIANT_ESP32H2
17 static const uint32_t RMT_CLK_FREQ = 32000000;
18 static const uint8_t RMT_CLK_DIV = 1;
19 #else
20 static const uint32_t RMT_CLK_FREQ = 80000000;
21 static 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 = 0;
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 
114 void 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  }
158  delayMicroseconds(50);
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
This class represents the communication layer between the front-end MQTT layer and the hardware outpu...
Definition: light_state.h:63
light::ESPColorView get_view_internal(int32_t index) const override
Definition: led_strip.cpp:204
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
T * allocate(size_t n)
Definition: helpers.h:703
uint32_t IRAM_ATTR HOT micros()
Definition: core.cpp:27
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)
Definition: led_strip.cpp:114
void status_clear_warning()
Definition: component.cpp:166
void write_state(light::LightState *state) override
Definition: led_strip.cpp:135
const float HARDWARE
For components that deal with hardware and are very important like GPIO switch.
Definition: component.cpp:18
std::string size_t len
Definition: helpers.h:301
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition: core.cpp:28
bool state
Definition: fan.h:34