ESPHome  2024.9.0
led_strip.cpp
Go to the documentation of this file.
1 #include <cinttypes>
2 #include "led_strip.h"
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 static const uint32_t RMT_CLK_FREQ = 80000000;
17 
18 static const uint8_t RMT_CLK_DIV = 2;
19 
21  ESP_LOGCONFIG(TAG, "Setting up ESP32 LED Strip...");
22 
23  size_t buffer_size = this->get_buffer_size_();
24 
26  this->buf_ = allocator.allocate(buffer_size);
27  if (this->buf_ == nullptr) {
28  ESP_LOGE(TAG, "Cannot allocate LED buffer!");
29  this->mark_failed();
30  return;
31  }
32 
33  this->effect_data_ = allocator.allocate(this->num_leds_);
34  if (this->effect_data_ == nullptr) {
35  ESP_LOGE(TAG, "Cannot allocate effect data!");
36  this->mark_failed();
37  return;
38  }
39 
41  this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 +
42  1); // 8 bits per byte, 1 rmt_item32_t per bit + 1 rmt_item32_t for reset
43 
44  rmt_config_t config;
45  memset(&config, 0, sizeof(config));
46  config.channel = this->channel_;
47  config.rmt_mode = RMT_MODE_TX;
48  config.gpio_num = gpio_num_t(this->pin_);
49  config.mem_block_num = 1;
50  config.clk_div = RMT_CLK_DIV;
51  config.tx_config.loop_en = false;
52  config.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
53  config.tx_config.carrier_en = false;
54  config.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
55  config.tx_config.idle_output_en = true;
56 
57  if (rmt_config(&config) != ESP_OK) {
58  ESP_LOGE(TAG, "Cannot initialize RMT!");
59  this->mark_failed();
60  return;
61  }
62  if (rmt_driver_install(config.channel, 0, 0) != ESP_OK) {
63  ESP_LOGE(TAG, "Cannot install RMT driver!");
64  this->mark_failed();
65  return;
66  }
67 }
68 
69 void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high,
70  uint32_t bit1_low, uint32_t reset_time_high, uint32_t reset_time_low) {
71  float ratio = (float) RMT_CLK_FREQ / RMT_CLK_DIV / 1e09f;
72 
73  // 0-bit
74  this->bit0_.duration0 = (uint32_t) (ratio * bit0_high);
75  this->bit0_.level0 = 1;
76  this->bit0_.duration1 = (uint32_t) (ratio * bit0_low);
77  this->bit0_.level1 = 0;
78  // 1-bit
79  this->bit1_.duration0 = (uint32_t) (ratio * bit1_high);
80  this->bit1_.level0 = 1;
81  this->bit1_.duration1 = (uint32_t) (ratio * bit1_low);
82  this->bit1_.level1 = 0;
83  // reset
84  this->reset_.duration0 = (uint32_t) (ratio * reset_time_high);
85  this->reset_.level0 = 1;
86  this->reset_.duration1 = (uint32_t) (ratio * reset_time_low);
87  this->reset_.level1 = 0;
88 }
89 
91  // protect from refreshing too often
92  uint32_t now = micros();
93  if (*this->max_refresh_rate_ != 0 && (now - this->last_refresh_) < *this->max_refresh_rate_) {
94  // try again next loop iteration, so that this change won't get lost
95  this->schedule_show();
96  return;
97  }
98  this->last_refresh_ = now;
99  this->mark_shown_();
100 
101  ESP_LOGVV(TAG, "Writing RGB values to bus...");
102 
103  if (rmt_wait_tx_done(this->channel_, pdMS_TO_TICKS(1000)) != ESP_OK) {
104  ESP_LOGE(TAG, "RMT TX timeout");
105  this->status_set_warning();
106  return;
107  }
108  delayMicroseconds(50);
109 
110  size_t buffer_size = this->get_buffer_size_();
111 
112  size_t size = 0;
113  size_t len = 0;
114  uint8_t *psrc = this->buf_;
115  rmt_item32_t *pdest = this->rmt_buf_;
116  while (size < buffer_size) {
117  uint8_t b = *psrc;
118  for (int i = 0; i < 8; i++) {
119  pdest->val = b & (1 << (7 - i)) ? this->bit1_.val : this->bit0_.val;
120  pdest++;
121  len++;
122  }
123  size++;
124  psrc++;
125  }
126 
127  if (this->reset_.duration0 > 0 || this->reset_.duration1 > 0) {
128  pdest->val = this->reset_.val;
129  pdest++;
130  len++;
131  }
132 
133  if (rmt_write_items(this->channel_, this->rmt_buf_, len, false) != ESP_OK) {
134  ESP_LOGE(TAG, "RMT TX error");
135  this->status_set_warning();
136  return;
137  }
138  this->status_clear_warning();
139 }
140 
142  int32_t r = 0, g = 0, b = 0;
143  switch (this->rgb_order_) {
144  case ORDER_RGB:
145  r = 0;
146  g = 1;
147  b = 2;
148  break;
149  case ORDER_RBG:
150  r = 0;
151  g = 2;
152  b = 1;
153  break;
154  case ORDER_GRB:
155  r = 1;
156  g = 0;
157  b = 2;
158  break;
159  case ORDER_GBR:
160  r = 2;
161  g = 0;
162  b = 1;
163  break;
164  case ORDER_BGR:
165  r = 2;
166  g = 1;
167  b = 0;
168  break;
169  case ORDER_BRG:
170  r = 1;
171  g = 2;
172  b = 0;
173  break;
174  }
175  uint8_t multiplier = this->is_rgbw_ || this->is_wrgb_ ? 4 : 3;
176  uint8_t white = this->is_wrgb_ ? 0 : 3;
177 
178  return {this->buf_ + (index * multiplier) + r + this->is_wrgb_,
179  this->buf_ + (index * multiplier) + g + this->is_wrgb_,
180  this->buf_ + (index * multiplier) + b + this->is_wrgb_,
181  this->is_rgbw_ || this->is_wrgb_ ? this->buf_ + (index * multiplier) + white : nullptr,
182  &this->effect_data_[index],
183  &this->correction_};
184 }
185 
187  ESP_LOGCONFIG(TAG, "ESP32 RMT LED Strip:");
188  ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
189  ESP_LOGCONFIG(TAG, " Channel: %u", this->channel_);
190  const char *rgb_order;
191  switch (this->rgb_order_) {
192  case ORDER_RGB:
193  rgb_order = "RGB";
194  break;
195  case ORDER_RBG:
196  rgb_order = "RBG";
197  break;
198  case ORDER_GRB:
199  rgb_order = "GRB";
200  break;
201  case ORDER_GBR:
202  rgb_order = "GBR";
203  break;
204  case ORDER_BGR:
205  rgb_order = "BGR";
206  break;
207  case ORDER_BRG:
208  rgb_order = "BRG";
209  break;
210  default:
211  rgb_order = "UNKNOWN";
212  break;
213  }
214  ESP_LOGCONFIG(TAG, " RGB Order: %s", rgb_order);
215  ESP_LOGCONFIG(TAG, " Max refresh rate: %" PRIu32, *this->max_refresh_rate_);
216  ESP_LOGCONFIG(TAG, " Number of LEDs: %u", this->num_leds_);
217 }
218 
220 
221 } // namespace esp32_rmt_led_strip
222 } // namespace esphome
223 
224 #endif // USE_ESP32
This class represents the communication layer between the front-end MQTT layer and the hardware outpu...
Definition: light_state.h:34
light::ESPColorView get_view_internal(int32_t index) const override
Definition: led_strip.cpp:141
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
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:69
void status_clear_warning()
Definition: component.cpp:166
void write_state(light::LightState *state) override
Definition: led_strip.cpp:90
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:292
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