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