ESPHome  2024.9.0
addressable_light_effect.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <utility>
4 #include <vector>
5 
9 
10 namespace esphome {
11 namespace light {
12 
13 inline static int16_t sin16_c(uint16_t theta) {
14  static const uint16_t BASE[] = {0, 6393, 12539, 18204, 23170, 27245, 30273, 32137};
15  static const uint8_t SLOPE[] = {49, 48, 44, 38, 31, 23, 14, 4};
16  uint16_t offset = (theta & 0x3FFF) >> 3; // 0..2047
17  if (theta & 0x4000)
18  offset = 2047 - offset;
19  uint8_t section = offset / 256; // 0..7
20  uint16_t b = BASE[section];
21  uint8_t m = SLOPE[section];
22  uint8_t secoffset8 = uint8_t(offset) / 2;
23  uint16_t mx = m * secoffset8;
24  int16_t y = mx + b;
25  if (theta & 0x8000)
26  return -y;
27  return y;
28 }
29 inline static uint8_t half_sin8(uint8_t v) { return sin16_c(uint16_t(v) * 128u) >> 8; }
30 
32  public:
33  explicit AddressableLightEffect(const std::string &name) : LightEffect(name) {}
34  void start_internal() override {
35  this->get_addressable_()->set_effect_active(true);
37  this->start();
38  }
39  void stop() override { this->get_addressable_()->set_effect_active(false); }
40  virtual void apply(AddressableLight &it, const Color &current_color) = 0;
41  void apply() override {
42  // not using any color correction etc. that will be handled by the addressable layer through ESPColorCorrection
44  this->apply(*this->get_addressable_(), current_color);
45  }
46 
47  protected:
49 };
50 
52  public:
53  AddressableLambdaLightEffect(const std::string &name,
54  std::function<void(AddressableLight &, Color, bool initial_run)> f,
55  uint32_t update_interval)
56  : AddressableLightEffect(name), f_(std::move(f)), update_interval_(update_interval) {}
57  void start() override { this->initial_run_ = true; }
58  void apply(AddressableLight &it, const Color &current_color) override {
59  const uint32_t now = millis();
60  if (now - this->last_run_ >= this->update_interval_ || this->initial_run_) {
61  this->last_run_ = now;
62  this->f_(it, current_color, this->initial_run_);
63  this->initial_run_ = false;
64  it.schedule_show();
65  }
66  }
67 
68  protected:
69  std::function<void(AddressableLight &, Color, bool initial_run)> f_;
70  uint32_t update_interval_;
71  uint32_t last_run_{0};
73 };
74 
76  public:
77  explicit AddressableRainbowLightEffect(const std::string &name) : AddressableLightEffect(name) {}
78  void apply(AddressableLight &it, const Color &current_color) override {
79  ESPHSVColor hsv;
80  hsv.value = 255;
81  hsv.saturation = 240;
82  uint16_t hue = (millis() * this->speed_) % 0xFFFF;
83  const uint16_t add = 0xFFFF / this->width_;
84  for (auto var : it) {
85  hsv.hue = hue >> 8;
86  var = hsv;
87  hue += add;
88  }
89  it.schedule_show();
90  }
91  void set_speed(uint32_t speed) { this->speed_ = speed; }
92  void set_width(uint16_t width) { this->width_ = width; }
93 
94  protected:
95  uint32_t speed_{10};
96  uint16_t width_{50};
97 };
98 
100  uint8_t r, g, b, w;
101  bool random;
102  size_t num_leds;
103  bool gradient;
104 };
105 
107  public:
108  explicit AddressableColorWipeEffect(const std::string &name) : AddressableLightEffect(name) {}
109  void set_colors(const std::vector<AddressableColorWipeEffectColor> &colors) { this->colors_ = colors; }
110  void set_add_led_interval(uint32_t add_led_interval) { this->add_led_interval_ = add_led_interval; }
111  void set_reverse(bool reverse) { this->reverse_ = reverse; }
112  void apply(AddressableLight &it, const Color &current_color) override {
113  const uint32_t now = millis();
114  if (now - this->last_add_ < this->add_led_interval_)
115  return;
116  this->last_add_ = now;
117  if (this->reverse_) {
118  it.shift_left(1);
119  } else {
120  it.shift_right(1);
121  }
122  const AddressableColorWipeEffectColor &color = this->colors_[this->at_color_];
123  Color esp_color = Color(color.r, color.g, color.b, color.w);
124  if (color.gradient) {
125  size_t next_color_index = (this->at_color_ + 1) % this->colors_.size();
126  const AddressableColorWipeEffectColor &next_color = this->colors_[next_color_index];
127  const Color next_esp_color = Color(next_color.r, next_color.g, next_color.b, next_color.w);
128  uint8_t gradient = 255 * ((float) this->leds_added_ / color.num_leds);
129  esp_color = esp_color.gradient(next_esp_color, gradient);
130  }
131  if (this->reverse_) {
132  it[-1] = esp_color;
133  } else {
134  it[0] = esp_color;
135  }
136  if (++this->leds_added_ >= color.num_leds) {
137  this->leds_added_ = 0;
138  this->at_color_ = (this->at_color_ + 1) % this->colors_.size();
139  AddressableColorWipeEffectColor &new_color = this->colors_[this->at_color_];
140  if (new_color.random) {
142  new_color.r = c.r;
143  new_color.g = c.g;
144  new_color.b = c.b;
145  }
146  }
147  it.schedule_show();
148  }
149 
150  protected:
151  std::vector<AddressableColorWipeEffectColor> colors_;
152  size_t at_color_{0};
153  uint32_t last_add_{0};
154  uint32_t add_led_interval_{};
155  size_t leds_added_{0};
156  bool reverse_{};
157 };
158 
160  public:
161  explicit AddressableScanEffect(const std::string &name) : AddressableLightEffect(name) {}
162  void set_move_interval(uint32_t move_interval) { this->move_interval_ = move_interval; }
163  void set_scan_width(uint32_t scan_width) { this->scan_width_ = scan_width; }
164  void apply(AddressableLight &it, const Color &current_color) override {
165  const uint32_t now = millis();
166  if (now - this->last_move_ < this->move_interval_)
167  return;
168 
169  if (direction_) {
170  this->at_led_++;
171  if (this->at_led_ == it.size() - this->scan_width_)
172  this->direction_ = false;
173  } else {
174  this->at_led_--;
175  if (this->at_led_ == 0)
176  this->direction_ = true;
177  }
178  this->last_move_ = now;
179 
180  it.all() = Color::BLACK;
181  for (uint32_t i = 0; i < this->scan_width_; i++) {
182  it[this->at_led_ + i] = current_color;
183  }
184 
185  it.schedule_show();
186  }
187 
188  protected:
189  uint32_t move_interval_{};
190  uint32_t scan_width_{1};
191  uint32_t last_move_{0};
192  uint32_t at_led_{0};
193  bool direction_{true};
194 };
195 
197  public:
198  explicit AddressableTwinkleEffect(const std::string &name) : AddressableLightEffect(name) {}
199  void apply(AddressableLight &addressable, const Color &current_color) override {
200  const uint32_t now = millis();
201  uint8_t pos_add = 0;
202  if (now - this->last_progress_ > this->progress_interval_) {
203  const uint32_t pos_add32 = (now - this->last_progress_) / this->progress_interval_;
204  pos_add = pos_add32;
205  this->last_progress_ += pos_add32 * this->progress_interval_;
206  }
207  for (auto view : addressable) {
208  if (view.get_effect_data() != 0) {
209  const uint8_t sine = half_sin8(view.get_effect_data());
210  view = current_color * sine;
211  const uint8_t new_pos = view.get_effect_data() + pos_add;
212  if (new_pos < view.get_effect_data()) {
213  view.set_effect_data(0);
214  } else {
215  view.set_effect_data(new_pos);
216  }
217  } else {
218  view = Color::BLACK;
219  }
220  }
221  while (random_float() < this->twinkle_probability_) {
222  const size_t pos = random_uint32() % addressable.size();
223  if (addressable[pos].get_effect_data() != 0)
224  continue;
225  addressable[pos].set_effect_data(1);
226  }
227  addressable.schedule_show();
228  }
229  void set_twinkle_probability(float twinkle_probability) { this->twinkle_probability_ = twinkle_probability; }
230  void set_progress_interval(uint32_t progress_interval) { this->progress_interval_ = progress_interval; }
231 
232  protected:
233  float twinkle_probability_{0.05f};
234  uint32_t progress_interval_{4};
235  uint32_t last_progress_{0};
236 };
237 
239  public:
240  explicit AddressableRandomTwinkleEffect(const std::string &name) : AddressableLightEffect(name) {}
241  void apply(AddressableLight &it, const Color &current_color) override {
242  const uint32_t now = millis();
243  uint8_t pos_add = 0;
244  if (now - this->last_progress_ > this->progress_interval_) {
245  pos_add = (now - this->last_progress_) / this->progress_interval_;
246  this->last_progress_ = now;
247  }
248  uint8_t subsine = ((8 * (now - this->last_progress_)) / this->progress_interval_) & 0b111;
249  for (auto view : it) {
250  if (view.get_effect_data() != 0) {
251  const uint8_t x = (view.get_effect_data() >> 3) & 0b11111;
252  const uint8_t color = view.get_effect_data() & 0b111;
253  const uint16_t sine = half_sin8((x << 3) | subsine);
254  if (color == 0) {
255  view = current_color * sine;
256  } else {
257  view = Color(((color >> 2) & 1) * sine, ((color >> 1) & 1) * sine, ((color >> 0) & 1) * sine);
258  }
259  const uint8_t new_x = x + pos_add;
260  if (new_x > 0b11111) {
261  view.set_effect_data(0);
262  } else {
263  view.set_effect_data((new_x << 3) | color);
264  }
265  } else {
266  view = Color(0, 0, 0, 0);
267  }
268  }
269  while (random_float() < this->twinkle_probability_) {
270  const size_t pos = random_uint32() % it.size();
271  if (it[pos].get_effect_data() != 0)
272  continue;
273  const uint8_t color = random_uint32() & 0b111;
274  it[pos].set_effect_data(0b1000 | color);
275  }
276  it.schedule_show();
277  }
278  void set_twinkle_probability(float twinkle_probability) { this->twinkle_probability_ = twinkle_probability; }
279  void set_progress_interval(uint32_t progress_interval) { this->progress_interval_ = progress_interval; }
280 
281  protected:
282  float twinkle_probability_{};
283  uint32_t progress_interval_{};
284  uint32_t last_progress_{0};
285 };
286 
288  public:
289  explicit AddressableFireworksEffect(const std::string &name) : AddressableLightEffect(name) {}
290  void start() override {
291  auto &it = *this->get_addressable_();
292  it.all() = Color::BLACK;
293  }
294  void apply(AddressableLight &it, const Color &current_color) override {
295  const uint32_t now = millis();
296  if (now - this->last_update_ < this->update_interval_)
297  return;
298  this->last_update_ = now;
299  // "invert" the fade out parameter so that higher values make fade out faster
300  const uint8_t fade_out_mult = 255u - this->fade_out_rate_;
301  for (auto view : it) {
302  Color target = view.get() * fade_out_mult;
303  if (target.r < 64)
304  target *= 170;
305  view = target;
306  }
307  int last = it.size() - 1;
308  it[0].set(it[0].get() + (it[1].get() * 128));
309  for (int i = 1; i < last; i++) {
310  it[i] = (it[i - 1].get() * 64) + it[i].get() + (it[i + 1].get() * 64);
311  }
312  it[last] = it[last].get() + (it[last - 1].get() * 128);
313  if (random_float() < this->spark_probability_) {
314  const size_t pos = random_uint32() % it.size();
315  if (this->use_random_color_) {
316  it[pos] = Color::random_color();
317  } else {
318  it[pos] = current_color;
319  }
320  }
321  it.schedule_show();
322  }
323  void set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; }
324  void set_spark_probability(float spark_probability) { this->spark_probability_ = spark_probability; }
325  void set_use_random_color(bool random_color) { this->use_random_color_ = random_color; }
326  void set_fade_out_rate(uint8_t fade_out_rate) { this->fade_out_rate_ = fade_out_rate; }
327 
328  protected:
329  uint8_t fade_out_rate_{};
330  uint32_t update_interval_{};
331  uint32_t last_update_{0};
332  float spark_probability_{};
333  bool use_random_color_{};
334 };
335 
337  public:
338  explicit AddressableFlickerEffect(const std::string &name) : AddressableLightEffect(name) {}
339  void apply(AddressableLight &it, const Color &current_color) override {
340  const uint32_t now = millis();
341  const uint8_t intensity = this->intensity_;
342  const uint8_t inv_intensity = 255 - intensity;
343  if (now - this->last_update_ < this->update_interval_)
344  return;
345 
346  this->last_update_ = now;
347  uint32_t rng_state = random_uint32();
348  for (auto var : it) {
349  rng_state = (rng_state * 0x9E3779B9) + 0x9E37;
350  const uint8_t flicker = (rng_state & 0xFF) % intensity;
351  // scale down by random factor
352  var = var.get() * (255 - flicker);
353 
354  // slowly fade back to "real" value
355  var = (var.get() * inv_intensity) + (current_color * intensity);
356  }
357  it.schedule_show();
358  }
359  void set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; }
360  void set_intensity(float intensity) { this->intensity_ = to_uint8_scale(intensity); }
361 
362  protected:
363  uint32_t update_interval_{16};
364  uint32_t last_update_{0};
365  uint8_t intensity_{13};
366 };
367 
368 } // namespace light
369 } // namespace esphome
const char * name
Definition: stm32flash.h:78
void apply(AddressableLight &addressable, const Color &current_color) override
virtual void clear_effect_data()=0
void set_move_interval(uint32_t move_interval)
void set_effect_active(bool effect_active)
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
Definition: helpers.cpp:193
uint16_t x
Definition: tt21100.cpp:17
LightOutput * get_output() const
Get the light output associated with this object.
void set_progress_interval(uint32_t progress_interval)
void set_update_interval(uint32_t update_interval)
int speed
Definition: fan.h:35
STL namespace.
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
uint8_t g
Definition: color.h:18
uint16_t y
Definition: tt21100.cpp:18
static Color random_color()
Definition: color.h:142
void set_add_led_interval(uint32_t add_led_interval)
void apply(AddressableLight &it, const Color &current_color) override
void set_progress_interval(uint32_t progress_interval)
void apply(AddressableLight &it, const Color &current_color) override
virtual int32_t size() const =0
Color gradient(const Color &to_color, uint8_t amnt)
Definition: color.h:153
void apply(AddressableLight &it, const Color &current_color) override
AddressableLambdaLightEffect(const std::string &name, std::function< void(AddressableLight &, Color, bool initial_run)> f, uint32_t update_interval)
void set_twinkle_probability(float twinkle_probability)
std::vector< AddressableColorWipeEffectColor > colors_
Color color_from_light_color_values(LightColorValues val)
Convert the color information from a LightColorValues object to a Color object (does not apply bright...
void set_colors(const std::vector< AddressableColorWipeEffectColor > &colors)
uint8_t b
Definition: color.h:22
static const Color BLACK
Definition: color.h:168
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
LightColorValues remote_values
The remote color values reported to the frontend.
Definition: light_state.h:77
uint8_t r
Definition: color.h:14
virtual void start()
Initialize this LightEffect. Will be called once after creation.
Definition: light_effect.h:17
void set_update_interval(uint32_t update_interval)
void apply(AddressableLight &it, const Color &current_color) override
std::function< void(AddressableLight &, Color, bool initial_run)> f_
uint8_t m
Definition: bl0906.h:208
void apply(AddressableLight &it, const Color &current_color) override
void apply(AddressableLight &it, const Color &current_color) override
void apply(AddressableLight &it, const Color &current_color) override
float random_float()
Return a random float between 0 and 1.
Definition: helpers.cpp:216