FastLED 3.9.15
Loading...
Searching...
No Matches
FxNoiseRing.h
Go to the documentation of this file.
1
10
11#include <Arduino.h>
12#include <FastLED.h>
13
14
15#include "fl/json.h"
16#include "fl/math_macros.h"
17#include "fl/warn.h"
18#include "noisegen.h"
19#include "fl/screenmap.h"
20#include "fl/slice.h"
21#include "fl/ui.h"
22
23#include "sensors/pir.h"
24#include "./simple_timer.h"
25#include "fl/sstream.h"
26#include "fl/assert.h"
27
28
29
30
31
32#define LED_PIN 2
33#define COLOR_ORDER GRB // Color order matters for a real device, web-compiler will ignore this.
34#define NUM_LEDS 250
35#define PIN_PIR 0
36
37#define PIR_LATCH_MS 60000 // how long to keep the PIR sensor active after a trigger
38#define PIR_RISING_TIME 1000 // how long to fade in the PIR sensor
39#define PIR_FALLING_TIME 1000 // how long to fade out the PIR sensor
40
41using namespace fl;
42
44
45// Enhanced coordinate system for ring-based effects
46struct RingCoord {
47 float angle; // Position on ring (0 to 2π)
48 float radius; // Distance from center (normalized 0-1)
49 float x, y; // Cartesian coordinates
50 int led_index; // LED position on strip
51};
52
53// Convert LED index to ring coordinates
54RingCoord calculateRingCoord(int led_index, int num_leds, float time_offset = 0.0f) {
55 RingCoord coord;
56 coord.led_index = led_index;
57 coord.angle = (led_index * 2.0f * M_PI / num_leds) + time_offset;
58 coord.radius = 1.0f; // Fixed radius for ring
59 coord.x = cos(coord.angle);
60 coord.y = sin(coord.angle);
61 return coord;
62}
63
64// Performance optimization with lookup tables
65class RingLUT {
66private:
69
70public:
71 void initialize() {
72 for(int i = 0; i < NUM_LEDS; i++) {
73 float angle = i * 2.0f * M_PI / NUM_LEDS;
74 cos_table[i] = cos(angle);
75 sin_table[i] = sin(angle);
76 }
77 }
78
79 RingCoord fastRingCoord(int led_index, float time_offset = 0.0f) {
80 RingCoord coord;
81 coord.led_index = led_index;
82 coord.angle = (led_index * 2.0f * M_PI / NUM_LEDS) + time_offset;
83 coord.x = cos_table[led_index];
84 coord.y = sin_table[led_index];
85 coord.radius = 1.0f;
86 return coord;
87 }
88};
89
90// Plasma wave parameters
92 float time_scale = 1.0f;
93 float noise_intensity = 0.5f;
94 float noise_amplitude = 0.8f;
95 uint8_t time_bitshift = 5;
96 uint8_t hue_offset = 0;
97 float brightness = 1.0f;
98};
99
100// Plasma wave generator - Featured Implementation
102private:
103 struct WaveSource {
104 float x, y; // Source position
105 float frequency; // Wave frequency
106 float amplitude; // Wave strength
107 float phase_speed; // Phase evolution rate
108 };
109
111 {0.5f, 0.5f, 1.0f, 1.0f, 0.8f}, // Center source
112 {0.0f, 0.0f, 1.5f, 0.8f, 1.2f}, // Corner source
113 {1.0f, 1.0f, 0.8f, 1.2f, 0.6f}, // Opposite corner
114 {0.5f, 0.0f, 1.2f, 0.9f, 1.0f} // Edge source
115 };
116
117public:
118 CRGB calculatePlasmaPixel(const RingCoord& coord, uint32_t time_ms, const PlasmaParams& params) {
119 float time_scaled = time_ms * params.time_scale * 0.001f;
120
121 // Calculate wave interference
122 float wave_sum = 0.0f;
123 for (int i = 0; i < 4; i++) {
124 float dx = coord.x - sources[i].x;
125 float dy = coord.y - sources[i].y;
126 float distance = sqrt(dx*dx + dy*dy);
127
128 float wave_phase = distance * sources[i].frequency + time_scaled * sources[i].phase_speed;
129 wave_sum += sin(wave_phase) * sources[i].amplitude;
130 }
131
132 // Add noise modulation for organic feel
133 float noise_scale = params.noise_intensity;
134 float noise_x = coord.x * 0xffff * noise_scale;
135 float noise_y = coord.y * 0xffff * noise_scale;
136 uint32_t noise_time = time_ms << params.time_bitshift;
137
138 float noise_mod = (inoise16(noise_x, noise_y, noise_time) - 32768) / 65536.0f;
139 wave_sum += noise_mod * params.noise_amplitude;
140
141 // Map to color space
142 return mapWaveToColor(wave_sum, params);
143 }
144
145private:
146 CRGB mapWaveToColor(float wave_value, const PlasmaParams& params) {
147 // Normalize wave to 0-1 range
148 float normalized = (wave_value + 4.0f) / 8.0f; // Assuming max amplitude ~4
149 normalized = constrain(normalized, 0.0f, 1.0f);
150
151 // Create flowing hue based on wave phase
152 uint8_t hue = (uint8_t)(normalized * 255.0f + params.hue_offset) % 256;
153
154 // Dynamic saturation based on wave intensity
155 float intensity = abs(wave_value);
156 uint8_t sat = (uint8_t)(192 + intensity * 63); // High saturation with variation
157
158 // Brightness modulation
159 uint8_t val = (uint8_t)(normalized * 255.0f * params.brightness);
160
161 return CHSV(hue, sat, val);
162 }
163};
164
165// Advanced Color Palette System
167private:
168 uint8_t current_palette = 0;
170 static const uint32_t PALETTE_CHANGE_INTERVAL = 5000; // 5 seconds
171
172public:
173 void update(uint32_t now, bool auto_cycle_enabled, uint8_t manual_palette) {
174 if (auto_cycle_enabled) {
178 }
179 } else {
180 current_palette = manual_palette;
181 }
182 }
183
184 CRGB mapColor(float hue_norm, float intensity, float special_param = 0.0f) {
185 switch(current_palette) {
186 case 0: return mapSunsetBoulevard(hue_norm, intensity, special_param);
187 case 1: return mapOceanBreeze(hue_norm, intensity, special_param);
188 case 2: return mapNeonNights(hue_norm, intensity, special_param);
189 case 3: return mapForestWhisper(hue_norm, intensity, special_param);
190 case 4: return mapGalaxyExpress(hue_norm, intensity, special_param);
191 default: return mapSunsetBoulevard(hue_norm, intensity, special_param);
192 }
193 }
194
195private:
196 CRGB mapSunsetBoulevard(float hue_norm, float intensity, float special_param) {
197 // Warm oranges, deep reds, golden yellows (Hue 0-45)
198 uint8_t hue = (uint8_t)(hue_norm * 45);
199 uint8_t sat = 200 + (uint8_t)(intensity * 55);
200 uint8_t val = 150 + (uint8_t)(intensity * 105);
201 return CHSV(hue, sat, val);
202 }
203
204 CRGB mapOceanBreeze(float hue_norm, float intensity, float special_param) {
205 // Deep blues, aqua, seafoam green (Hue 120-210)
206 uint8_t hue = 120 + (uint8_t)(hue_norm * 90);
207 uint8_t sat = 180 + (uint8_t)(intensity * 75);
208 uint8_t val = 120 + (uint8_t)(intensity * 135);
209 return CHSV(hue, sat, val);
210 }
211
212 CRGB mapNeonNights(float hue_norm, float intensity, float special_param) {
213 // Electric pink, cyan, purple, lime green - high contrast
214 uint8_t base_hues[] = {0, 85, 128, 192}; // Red, Cyan, Pink, Purple
215 uint8_t selected_hue = base_hues[(int)(hue_norm * 4) % 4];
216 uint8_t sat = 255; // Maximum saturation for neon effect
217 uint8_t val = 100 + (uint8_t)(intensity * 155);
218 return CHSV(selected_hue, sat, val);
219 }
220
221 CRGB mapForestWhisper(float hue_norm, float intensity, float special_param) {
222 // Deep greens, earth browns, golden highlights (Hue 60-150)
223 uint8_t hue = 60 + (uint8_t)(hue_norm * 90);
224 uint8_t sat = 150 + (uint8_t)(intensity * 105);
225 uint8_t val = 100 + (uint8_t)(intensity * 155);
226 return CHSV(hue, sat, val);
227 }
228
229 CRGB mapGalaxyExpress(float hue_norm, float intensity, float special_param) {
230 // Deep purples, cosmic blues, silver stars (Hue 200-300)
231 if (special_param > 0.8f) {
232 // Silver/white stars
233 uint8_t brightness = 200 + (uint8_t)(intensity * 55);
235 } else {
236 uint8_t hue = 200 + (uint8_t)(hue_norm * 100);
237 uint8_t sat = 180 + (uint8_t)(intensity * 75);
238 uint8_t val = 80 + (uint8_t)(intensity * 175);
239 return CHSV(hue, sat, val);
240 }
241 }
242};
243
244// ALL 10 ALGORITHM IMPLEMENTATIONS
245CRGB drawCosmicSwirl(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
246 float time_factor = time_ms * 0.0008f;
247
248 // Multi-octave noise for organic complexity
249 float noise1 = inoise16(coord.x * 2000, coord.y * 2000, time_factor * 1000) / 65536.0f;
250 float noise2 = inoise16(coord.x * 1000, coord.y * 1000, time_factor * 2000) / 65536.0f * 0.5f;
251 float noise3 = inoise16(coord.x * 4000, coord.y * 4000, time_factor * 500) / 65536.0f * 0.25f;
252
253 float combined_noise = noise1 + noise2 + noise3;
254 float hue_norm = (combined_noise + coord.angle / (2*M_PI) + 1.0f) * 0.5f;
255 float intensity = (combined_noise + 1.0f) * 0.5f;
256
257 return palette.mapColor(hue_norm, intensity);
258}
259
260CRGB drawElectricStorm(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
261 uint32_t fast_time = time_ms << 3; // 8x time acceleration
262
263 float x_noise = coord.x * 8000;
264 float y_noise = coord.y * 8000;
265
266 uint16_t noise1 = inoise16(x_noise, y_noise, fast_time);
267 uint16_t noise2 = inoise16(x_noise + 10000, y_noise + 10000, fast_time + 5000);
268
269 uint8_t threshold = 200;
270 bool lightning = (noise1 >> 8) > threshold || (noise2 >> 8) > threshold;
271
272 if (lightning) {
273 float lightning_intensity = max((noise1 >> 8) - threshold, (noise2 >> 8) - threshold) / 55.0f;
274 return palette.mapColor(0.7f, lightning_intensity, 1.0f); // Special lightning effect
275 } else {
276 float storm_intensity = (noise1 >> 8) / 1020.0f; // Very low intensity for background
277 return palette.mapColor(0.6f, storm_intensity);
278 }
279}
280
281CRGB drawLavaLamp(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
282 float slow_time = time_ms * 0.0002f;
283
284 float blob_scale = 800;
285 uint16_t primary_noise = inoise16(coord.x * blob_scale, coord.y * blob_scale, slow_time * 1000);
286 uint16_t secondary_noise = inoise16(coord.x * blob_scale * 0.5f, coord.y * blob_scale * 0.5f, slow_time * 1500);
287
288 float blob_value = (primary_noise + secondary_noise * 0.3f) / 65536.0f;
289
290 if (blob_value > 0.6f) {
291 // Hot blob center
292 float intensity = (blob_value - 0.6f) / 0.4f;
293 return palette.mapColor(0.1f, intensity); // Warm colors
294 } else if (blob_value > 0.3f) {
295 // Blob edge gradient
296 float edge_factor = (blob_value - 0.3f) / 0.3f;
297 return palette.mapColor(0.2f, edge_factor);
298 } else {
299 // Background
300 return palette.mapColor(0.8f, 0.2f); // Cool background
301 }
302}
303
304CRGB drawDigitalRain(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
305 float vertical_pos = sin(coord.angle) * 0.5f + 0.5f;
306 float cascade_speed = 0.002f;
307 float time_offset = time_ms * cascade_speed;
308
309 int stream_id = (int)(coord.angle * 10) % 8;
310 float stream_phase = fmod(vertical_pos + time_offset + stream_id * 0.125f, 1.0f);
311
312 uint16_t noise = inoise16(stream_id * 1000, stream_phase * 10000, time_ms / 4);
313 uint8_t digital_value = (noise >> 8) > 128 ? 255 : 0;
314
315 if (digital_value > 0) {
316 float intensity = 1.0f - stream_phase * 0.8f; // Fade trailing
317 return palette.mapColor(0.4f, intensity); // Matrix green area
318 } else {
319 return CRGB::Black;
320 }
321}
322
323CRGB drawGlitchCity(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
324 uint32_t glitch_time = (time_ms / 100) * 100; // Quantize time
325
326 uint16_t noise1 = inoise16(coord.x * 3000, coord.y * 3000, glitch_time);
327 uint16_t noise2 = inoise16(coord.x * 5000, coord.y * 5000, glitch_time + 1000);
328
329 uint16_t glitch_value = noise1 ^ noise2; // XOR for harsh digital effects
330
331 if ((glitch_value & 0xF000) == 0xF000) {
332 return CRGB(255, 255, 255); // Full-bright glitch flash
333 }
334
335 float intensity = (glitch_value & 0xFF) / 255.0f;
336 float hue_chaos = ((glitch_value >> 8) & 0xFF) / 255.0f;
337
338 return palette.mapColor(hue_chaos, intensity, 0.5f);
339}
340
341CRGB drawOceanDepths(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
342 float ocean_time = time_ms * 0.0005f;
343
344 float current1 = inoise16(coord.x * 1200, coord.y * 1200, ocean_time * 800) / 65536.0f;
345 float current2 = inoise16(coord.x * 2400, coord.y * 2400, ocean_time * 600) / 65536.0f * 0.5f;
346 float current3 = inoise16(coord.x * 600, coord.y * 600, ocean_time * 1000) / 65536.0f * 0.3f;
347
348 float depth_factor = (current1 + current2 + current3 + 1.5f) / 3.0f;
349 float hue_variation = (current2 + 0.5f);
350
351 return palette.mapColor(hue_variation, depth_factor);
352}
353
354CRGB drawFireDance(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
355 float vertical_component = sin(coord.angle) * 0.5f + 0.5f;
356
357 float flame_x = coord.x * 1500;
358 float flame_y = coord.y * 1500 + time_ms * 0.003f;
359
360 uint16_t turbulence = inoise16(flame_x, flame_y, time_ms);
361 float flame_intensity = (turbulence / 65536.0f) * (1.0f - vertical_component * 0.3f);
362
363 float fire_hue = flame_intensity * 0.15f; // Red to orange range
364 return palette.mapColor(fire_hue, flame_intensity);
365}
366
367CRGB drawNebulaDrift(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
368 float nebula_time = time_ms * 0.0003f;
369
370 float cloud1 = inoise16(coord.x * 800, coord.y * 800, nebula_time * 1000) / 65536.0f;
371 float cloud2 = inoise16(coord.x * 1600, coord.y * 1600, nebula_time * 700) / 65536.0f * 0.5f;
372 float cloud3 = inoise16(coord.x * 400, coord.y * 400, nebula_time * 1200) / 65536.0f * 0.25f;
373
374 float nebula_density = cloud1 + cloud2 + cloud3;
375
376 uint16_t star_noise = inoise16(coord.x * 4000, coord.y * 4000, nebula_time * 200);
377 bool is_star = (star_noise > 60000);
378
379 if (is_star) {
380 float star_intensity = (star_noise - 60000) / 5536.0f;
381 return palette.mapColor(0.0f, star_intensity, 1.0f); // Stars
382 } else {
383 float hue_drift = (nebula_density + 1.0f) * 0.5f;
384 float intensity = (nebula_density + 1.0f) * 0.4f;
385 return palette.mapColor(hue_drift, intensity);
386 }
387}
388
389CRGB drawBinaryPulse(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
390 float pulse_period = 2000.0f;
391 float pulse_phase = fmod(time_ms, pulse_period) / pulse_period;
392
393 float distance_from_center = sqrt(coord.x * coord.x + coord.y * coord.y);
394
395 float ring_frequency = 5.0f;
396 float pulse_offset = pulse_phase * 2.0f;
397 float ring_value = sin((distance_from_center * ring_frequency - pulse_offset) * 2 * M_PI);
398
399 uint16_t noise = inoise16(coord.x * 2000, coord.y * 2000, time_ms / 8);
400 float digital_mod = ((noise >> 8) > 128) ? 1.0f : -0.5f;
401
402 float final_value = ring_value * digital_mod;
403
404 if (final_value > 0.3f) {
405 return palette.mapColor(0.8f, final_value, 0.8f); // Active pulse
406 } else if (final_value > -0.2f) {
407 float transition_intensity = (final_value + 0.2f) * 2.0f;
408 return palette.mapColor(0.3f, transition_intensity); // Transition zones
409 } else {
410 return palette.mapColor(0.7f, 0.1f); // Background
411 }
412}
413
414// Enhanced variant manager with ALL 10 ALGORITHMS and smooth transitions
416private:
417 uint8_t current_variant = 0;
418 uint8_t target_variant = 0;
419 float transition_progress = 1.0f; // 0.0 = old, 1.0 = new
420 uint32_t transition_start = 0;
421 static const uint32_t TRANSITION_DURATION = 1500; // 1.5 second fade for smoother transitions
422
423 // Algorithm instances
427
428public:
430
431 void update(uint32_t now, bool auto_cycle_enabled, uint8_t manual_variant, const PlasmaParams& params) {
432 plasma_params = params;
433
434 // Handle automatic cycling vs manual override
435 if (auto_cycle_enabled) {
436 EVERY_N_MILLISECONDS(12000) { // Slightly longer for each variant
437 startTransition((current_variant + 1) % 10, now); // ALL 10 variants
438 }
439 } else if (manual_variant != target_variant && transition_progress >= 1.0f) {
440 // Manual override
441 startTransition(manual_variant, now);
442 }
443
444 // Update transition progress
445 if (transition_progress < 1.0f) {
446 uint32_t elapsed = now - transition_start;
447 transition_progress = min(1.0f, elapsed / (float)TRANSITION_DURATION);
448
449 if (transition_progress >= 1.0f) {
451 }
452 }
453 }
454
455 CRGB renderPixel(const RingCoord& coord, uint32_t time_ms) {
456 if (transition_progress >= 1.0f) {
457 // No transition, render current variant
458 return renderVariant(current_variant, coord, time_ms);
459 } else {
460 // Advanced cross-fade with brightness preservation
461 CRGB old_color = renderVariant(current_variant, coord, time_ms);
462 CRGB new_color = renderVariant(target_variant, coord, time_ms);
463 return smoothLerpCRGB(old_color, new_color, transition_progress);
464 }
465 }
466
467 uint8_t getCurrentVariant() const { return current_variant; }
468 const char* getCurrentVariantName() const {
469 const char* names[] = {
470 "Cosmic Swirl", "Electric Storm", "Lava Lamp", "Digital Rain", "Plasma Waves",
471 "Glitch City", "Ocean Depths", "Fire Dance", "Nebula Drift", "Binary Pulse"
472 };
473 return names[current_variant % 10];
474 }
475
476private:
477 void startTransition(uint8_t new_variant, uint32_t now) {
478 target_variant = new_variant % 10; // Ensure valid range
479 transition_start = now;
480 transition_progress = 0.0f;
481 }
482
483 CRGB renderVariant(uint8_t variant, const RingCoord& coord, uint32_t time_ms) {
484 switch(variant % 10) {
485 case 0: return drawCosmicSwirl(coord, time_ms, palette_manager);
486 case 1: return drawElectricStorm(coord, time_ms, palette_manager);
487 case 2: return drawLavaLamp(coord, time_ms, palette_manager);
488 case 3: return drawDigitalRain(coord, time_ms, palette_manager);
489 case 4: return drawPlasmaWithPalette(coord, time_ms, palette_manager);
490 case 5: return drawGlitchCity(coord, time_ms, palette_manager);
491 case 6: return drawOceanDepths(coord, time_ms, palette_manager);
492 case 7: return drawFireDance(coord, time_ms, palette_manager);
493 case 8: return drawNebulaDrift(coord, time_ms, palette_manager);
494 case 9: return drawBinaryPulse(coord, time_ms, palette_manager);
495 default: return drawCosmicSwirl(coord, time_ms, palette_manager);
496 }
497 }
498
499 // Enhanced Plasma Waves with palette integration
501 // Generate base plasma waves
502 CRGB plasma_color = plasma_gen.calculatePlasmaPixel(coord, time_ms, plasma_params);
503
504 // Extract intensity and hue information from plasma
505 float intensity = (plasma_color.r + plasma_color.g + plasma_color.b) / 765.0f;
506
507 // Calculate wave interference for hue mapping
508 float time_scaled = time_ms * plasma_params.time_scale * 0.001f;
509 float wave_sum = 0.0f;
510
511 // Simplified wave calculation for hue determination
512 float dx = coord.x - 0.5f;
513 float dy = coord.y - 0.5f;
514 float distance = sqrt(dx*dx + dy*dy);
515 float wave_phase = distance * 2.0f + time_scaled * 1.5f;
516 wave_sum = sin(wave_phase);
517
518 float hue_norm = (wave_sum + 1.0f) * 0.5f; // Normalize to 0-1
519
520 // Use palette system for consistent color theming
521 return palette.mapColor(hue_norm, intensity, intensity > 0.8f ? 1.0f : 0.0f);
522 }
523
524 // Enhanced interpolation with brightness preservation and smooth curves
525 CRGB smoothLerpCRGB(const CRGB& a, const CRGB& b, float t) {
526 // Apply smooth curve to transition
527 float smooth_t = t * t * (3.0f - 2.0f * t); // Smoothstep function
528
529 // Preserve brightness during transition to avoid flickering
530 float brightness_a = (a.r + a.g + a.b) / 765.0f;
531 float brightness_b = (b.r + b.g + b.b) / 765.0f;
532 float target_brightness = brightness_a + (brightness_b - brightness_a) * smooth_t;
533
534 CRGB result = CRGB(
535 a.r + (int)((b.r - a.r) * smooth_t),
536 a.g + (int)((b.g - a.g) * smooth_t),
537 a.b + (int)((b.b - a.b) * smooth_t)
538 );
539
540 // Brightness compensation
541 float current_brightness = (result.r + result.g + result.b) / 765.0f;
542 if (current_brightness > 0.01f) {
543 float compensation = target_brightness / current_brightness;
544 compensation = min(compensation, 2.0f); // Limit boost
545 result.r = min(255, (int)(result.r * compensation));
546 result.g = min(255, (int)(result.g * compensation));
547 result.b = min(255, (int)(result.b * compensation));
548 }
549
550 return result;
551 }
552};
553
554// ALL 10 Variant names for UI
556 "Cosmic Swirl", "Electric Storm", "Lava Lamp", "Digital Rain", "Plasma Waves",
557 "Glitch City", "Ocean Depths", "Fire Dance", "Nebula Drift", "Binary Pulse"
558};
559
560// 5 Color Palette names for UI
562 "Sunset Boulevard", "Ocean Breeze", "Neon Nights", "Forest Whisper", "Galaxy Express"
563};
564
565// Helper functions to get indices from names
566uint8_t getVariantIndex(const fl::string& name) {
567 for (int i = 0; i < 10; i++) {
568 if (variant_names[i] == name) {
569 return i;
570 }
571 }
572 return 0; // Default to first variant
573}
574
575uint8_t getPaletteIndex(const fl::string& name) {
576 for (int i = 0; i < 5; i++) {
577 if (palette_names[i] == name) {
578 return i;
579 }
580 }
581 return 0; // Default to first palette
582}
583
584// Global instances - order matters for initialization
588
589// KICKASS UI controls - comprehensive control suite
590UISlider brightness("Brightness", 1, 0, 1);
591UISlider scale("Scale", 4, .1, 4, .1);
592UISlider timeBitshift("Time Bitshift", 5, 0, 16, 1);
593UISlider timescale("Time Scale", 1, .1, 10, .1);
594
595// Advanced variant and palette controls
598UICheckbox autoCycle("Auto Cycle Effects", true);
599UICheckbox autoPalette("Auto Cycle Palettes", true);
600// This PIR type is special because it will bind to a pin for a real device,
601// but also provides a UIButton when run in the simulator.
603UICheckbox useDither("Use Binary Dither", true);
604
607
608// Save a pointer to the controller so that we can modify the dither in real time.
610
611void setup() {
612 Serial.begin(115200);
613 // ScreenMap is purely something that is needed for the sketch to correctly
614 // show on the web display. For deployements to real devices, this essentially
615 // becomes a no-op.
618 .setCorrection(TypicalLEDStrip)
619 .setDither(DISABLE_DITHER)
620 .setScreenMap(xyMap);
621 FastLED.setBrightness(brightness);
622 pir.activate(millis()); // Activate the PIR sensor on startup.
623
624 // Initialize performance optimizations
625 ring_lut.initialize();
626}
627
628void draw(uint32_t now) {
629 // Configure plasma parameters from UI controls with enhanced scaling
630 PlasmaParams plasma_params;
631 plasma_params.time_scale = timescale.as<float>();
632 plasma_params.noise_intensity = scale.as<float>() * 0.8f; // Slightly reduce for better visual balance
633 plasma_params.brightness = brightness.as<float>();
634 plasma_params.time_bitshift = timeBitshift.as<int>();
635 plasma_params.hue_offset = (now / 100) % 256; // Slow hue rotation for extra dynamism
636 plasma_params.noise_amplitude = 0.6f + 0.4f * sin(now * 0.001f); // Breathing noise effect
637
638 // Update palette manager with auto-cycling and manual control
639 palette_manager.update(now, autoPalette.value(), getPaletteIndex(palettes.value()));
640
641 // Update variant manager with enhanced parameters
642 variant_manager.update(now, autoCycle.value(), getVariantIndex(variants.value()), plasma_params);
643
644 // KICKASS rendering with performance optimizations
645 for (int i = 0; i < NUM_LEDS; i++) {
646 RingCoord coord = ring_lut.fastRingCoord(i);
647 CRGB pixel_color = variant_manager.renderPixel(coord, now);
648
649 // Apply global brightness and gamma correction for better visual quality
650 float global_brightness = brightness.as<float>();
651 pixel_color.r = (uint8_t)(pixel_color.r * global_brightness);
652 pixel_color.g = (uint8_t)(pixel_color.g * global_brightness);
653 pixel_color.b = (uint8_t)(pixel_color.b * global_brightness);
654
655 leds[i] = pixel_color;
656 }
657
658 // Optional: Add subtle sparkle overlay for extra visual interest
660 // Add random sparkles to 1% of LEDs
661 int sparkle_count = NUM_LEDS / 100 + 1;
662 for (int s = 0; s < sparkle_count; s++) {
663 int sparkle_pos = random16() % NUM_LEDS;
664 if (random8() > 250) { // Very rare sparkles
665 leds[sparkle_pos] = blend(leds[sparkle_pos], CRGB::White, 128);
666 }
667 }
668 }
669}
670
671void loop() {
672 // Allow the dither to be enabled and disabled.
674 uint32_t now = millis();
675 uint8_t bri = pir.transition(now);
676 FastLED.setBrightness(bri * brightness.as<float>());
677 // Apply leds generation to the leds.
678 draw(now);
679
680
681 FastLED.show();
682}
CRGB leds[NUM_LEDS]
#define NUM_LEDS
uint8_t hue
fl::XYMap xyMap
Definition ColorBoost.h:61
FL_DISABLE_WARNING_PUSH FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS CFastLED FastLED
Global LED strip management instance.
Definition FastLED.cpp:74
central include file for FastLED, defines the CFastLED class/object
uint8_t noise[NUM_LAYERS][WIDTH][HEIGHT]
Definition Fire2023.h:98
uint8_t noise2[NUM_LAYERS][WIDTH][HEIGHT]
Definition Fire2023.h:99
UINumberField palette("Palette", 0, 0, 2)
uint8_t getPaletteIndex(uint32_t millis32, int width, int max_width, int height, int max_height, uint32_t y_speed)
ColorPaletteManager palette_manager
RingCoord calculateRingCoord(int led_index, int num_leds, float time_offset=0.0f)
Definition FxNoiseRing.h:54
UICheckbox autoPalette("Auto Cycle Palettes", true)
uint8_t getPaletteIndex(const fl::string &name)
UICheckbox autoCycle("Auto Cycle Effects", true)
CRGB drawCosmicSwirl(const RingCoord &coord, uint32_t time_ms, ColorPaletteManager &palette)
#define PIR_FALLING_TIME
Definition FxNoiseRing.h:39
#define NUM_LEDS
Definition FxNoiseRing.h:34
CRGB drawDigitalRain(const RingCoord &coord, uint32_t time_ms, ColorPaletteManager &palette)
void setup()
CRGB drawOceanDepths(const RingCoord &coord, uint32_t time_ms, ColorPaletteManager &palette)
NoiseVariantManager variant_manager(palette_manager)
CRGB drawLavaLamp(const RingCoord &coord, uint32_t time_ms, ColorPaletteManager &palette)
#define PIN_PIR
Definition FxNoiseRing.h:35
CLEDController * controller
uint8_t getVariantIndex(const fl::string &name)
void draw(uint32_t now)
CRGB drawFireDance(const RingCoord &coord, uint32_t time_ms, ColorPaletteManager &palette)
UISlider timescale("Time Scale", 1,.1, 10,.1)
#define PIR_LATCH_MS
Definition FxNoiseRing.h:37
Pir pir(PIN_PIR, PIR_LATCH_MS, PIR_RISING_TIME, PIR_FALLING_TIME)
fl::string variant_names[10]
float current_brightness
Timer timer
UISlider timeBitshift("Time Bitshift", 5, 0, 16, 1)
fl::string palette_names[5]
CRGB drawElectricStorm(const RingCoord &coord, uint32_t time_ms, ColorPaletteManager &palette)
CRGB drawGlitchCity(const RingCoord &coord, uint32_t time_ms, ColorPaletteManager &palette)
RingLUT ring_lut
CRGB drawNebulaDrift(const RingCoord &coord, uint32_t time_ms, ColorPaletteManager &palette)
UIDropdown variants("Noise Variants", variant_names)
UISlider brightness("Brightness", 1, 0, 1)
UICheckbox useDither("Use Binary Dither", true)
UIDropdown palettes("Color Palettes", palette_names)
CRGB drawBinaryPulse(const RingCoord &coord, uint32_t time_ms, ColorPaletteManager &palette)
#define PIR_RISING_TIME
Definition FxNoiseRing.h:38
void loop()
int led_index
Definition FxNoiseRing.h:50
float noise_intensity
Definition FxNoiseRing.h:93
float radius
Definition FxNoiseRing.h:48
float brightness
Definition FxNoiseRing.h:97
uint8_t hue_offset
Definition FxNoiseRing.h:96
uint8_t time_bitshift
Definition FxNoiseRing.h:95
float noise_amplitude
Definition FxNoiseRing.h:94
float time_scale
Definition FxNoiseRing.h:92
float angle
Definition FxNoiseRing.h:47
uint16_t scale
Definition Noise.ino:74
#define COLOR_ORDER
Definition advanced.h:42
UISlider brightness("Brightness", 128, 0, 255, 1)
#define LED_PIN
Definition advanced.h:40
Base definition for an LED controller.
CRGB mapSunsetBoulevard(float hue_norm, float intensity, float special_param)
CRGB mapForestWhisper(float hue_norm, float intensity, float special_param)
static const uint32_t PALETTE_CHANGE_INTERVAL
void update(uint32_t now, bool auto_cycle_enabled, uint8_t manual_palette)
uint32_t last_palette_change
CRGB mapGalaxyExpress(float hue_norm, float intensity, float special_param)
CRGB mapNeonNights(float hue_norm, float intensity, float special_param)
CRGB mapColor(float hue_norm, float intensity, float special_param=0.0f)
CRGB mapOceanBreeze(float hue_norm, float intensity, float special_param)
NoiseVariantManager(ColorPaletteManager &palette_mgr)
const char * getCurrentVariantName() const
CRGB renderPixel(const RingCoord &coord, uint32_t time_ms)
PlasmaParams plasma_params
CRGB smoothLerpCRGB(const CRGB &a, const CRGB &b, float t)
void startTransition(uint8_t new_variant, uint32_t now)
static const uint32_t TRANSITION_DURATION
uint32_t transition_start
void update(uint32_t now, bool auto_cycle_enabled, uint8_t manual_variant, const PlasmaParams &params)
uint8_t getCurrentVariant() const
CRGB renderVariant(uint8_t variant, const RingCoord &coord, uint32_t time_ms)
PlasmaWaveGenerator plasma_gen
ColorPaletteManager & palette_manager
CRGB drawPlasmaWithPalette(const RingCoord &coord, uint32_t time_ms, ColorPaletteManager &palette)
CRGB calculatePlasmaPixel(const RingCoord &coord, uint32_t time_ms, const PlasmaParams &params)
CRGB mapWaveToColor(float wave_value, const PlasmaParams &params)
WaveSource sources[4]
float cos_table[NUM_LEDS]
Definition FxNoiseRing.h:67
void initialize()
Definition FxNoiseRing.h:71
RingCoord fastRingCoord(int led_index, float time_offset=0.0f)
Definition FxNoiseRing.h:79
float sin_table[NUM_LEDS]
Definition FxNoiseRing.h:68
A simple timer utility class for tracking timed events.
WS2811 controller class.
Definition FastLED.h:266
Definition pir.h:42
static ScreenMap Circle(int numLeds, float cm_between_leds=1.5f, float cm_led_diameter=0.5f, float completion=1.0f)
Definition screenmap.cpp:52
Result type for promise operations.
#define BINARY_DITHER
Enable dithering using binary dithering (only option)
Definition dither_mode.h:14
#define DISABLE_DITHER
Disable dithering.
Definition dither_mode.h:12
static uint32_t t
Definition Luminova.h:54
FastLED's Elegant JSON Library: fl::Json
@ TypicalLEDStrip
Typical values for SMD5050 LEDs.
Definition color.h:19
uint16_t inoise16(uint32_t x, uint32_t y, uint32_t z, uint32_t t)
16-bit, fixed point implementation of Perlin's noise.
Definition noise.cpp:420
LIB8STATIC uint16_t random16()
Generate a 16-bit random number.
Definition random8.h:56
LIB8STATIC uint8_t random8()
Generate an 8-bit random number.
Definition random8.h:46
#define EVERY_N_MILLISECONDS(N)
Alias for EVERY_N_MILLIS.
Definition lib8tion.h:1221
#define M_PI
Definition math_macros.h:93
CRGB blend(const CRGB &p1, const CRGB &p2, fract8 amountOfP2)
IMPORTANT!
Definition crgb.h:20
Noise generation classes.
@ White
<div style='background:#FFFFFF;width:4em;height:4em;'></div>
Definition crgb.h:703
@ Black
<div style='background:#000000;width:4em;height:4em;'></div>
Definition crgb.h:567
Representation of an RGB pixel (Red, Green, Blue)
Definition crgb.h:86
Representation of an HSV pixel (hue, saturation, value (aka brightness)).
Definition hsv.h:15