ESPHome 2025.5.0
Loading...
Searching...
No Matches
addressable_light_effect.h
Go to the documentation of this file.
1#pragma once
2
3#include <utility>
4#include <vector>
5
9
10namespace esphome {
11namespace light {
12
13inline 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}
29inline 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 {
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_;
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;
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();
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};
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:
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:
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();
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_{};
331 uint32_t last_update_{0};
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;
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
uint8_t m
Definition bl0906.h:1
void set_colors(const std::vector< AddressableColorWipeEffectColor > &colors)
void apply(AddressableLight &it, const Color &current_color) override
void set_add_led_interval(uint32_t add_led_interval)
std::vector< AddressableColorWipeEffectColor > colors_
void apply(AddressableLight &it, const Color &current_color) override
void apply(AddressableLight &it, const Color &current_color) override
void set_update_interval(uint32_t update_interval)
AddressableLambdaLightEffect(const std::string &name, std::function< void(AddressableLight &, Color, bool initial_run)> f, uint32_t update_interval)
void apply(AddressableLight &it, const Color &current_color) override
std::function< void(AddressableLight &, Color, bool initial_run)> f_
virtual void apply(AddressableLight &it, const Color &current_color)=0
virtual void clear_effect_data()=0
ESPColorView get(int32_t index)
void set_effect_active(bool effect_active)
virtual int32_t size() const =0
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
void set_progress_interval(uint32_t progress_interval)
void set_twinkle_probability(float twinkle_probability)
void apply(AddressableLight &addressable, const Color &current_color) override
virtual void start()
Initialize this LightEffect. Will be called once after creation.
LightColorValues remote_values
The remote color values reported to the frontend.
LightOutput * get_output() const
Get the light output associated with this object.
Color color_from_light_color_values(LightColorValues val)
Convert the color information from a LightColorValues object to a Color object (does not apply bright...
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
float random_float()
Return a random float between 0 and 1.
Definition helpers.cpp:218
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
Definition helpers.cpp:196
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:27
static Color random_color()
Definition color.h:142
static const Color BLACK
Definition color.h:168
uint8_t g
Definition color.h:18
Color gradient(const Color &to_color, uint8_t amnt)
Definition color.h:153
uint8_t b
Definition color.h:22
uint8_t r
Definition color.h:14
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6