Viking Skull Lamp  V1.0.1
Loading...
Searching...
No Matches
lightControl.cpp
Go to the documentation of this file.
1/*
2 * Created on May 28 2022
3 *
4 * Copyright (c) 2022 - Daniel Hajnal
5 * hajnal.daniel96@gmail.com
6 * This file is part of the Viking Skull Lamp project.
7 * Modified 2022.06.27
8*/
9
10/*
11MIT License
12Copyright (c) 2022 Daniel Hajnal
13Permission is hereby granted, free of charge, to any person obtaining a copy
14of this software and associated documentation files (the "Software"), to deal
15in the Software without restriction, including without limitation the rights
16to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17copies of the Software, and to permit persons to whom the Software is
18furnished to do so, subject to the following conditions:
19The above copyright notice and this permission notice shall be included in all
20copies or substantial portions of the Software.
21THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27SOFTWARE.
28*/
29
30#include "lightControl.hpp"
31
34
37
39unsigned long lightTimer = 0;
40
42unsigned long sampleTimer = 0;
43
46
51
53bool lightStateOn = false;
54
56bool prevPeak = false;
57
59bool prevBeat = false;
60
65unsigned long peakTimes[ 4 ] = { 0 };
66
67void lightInit(){
68
69 FastLED.addLeds<NEOPIXEL, LIGHT_STRIP_PIN>( stripLightBuffer, LIGHT_STRIP_SIZE );
70 FastLED.addLeds<NEOPIXEL, LIGHT_SKULL_PIN>( skullLightBuffer, LIGHT_SKULL_SIZE );
71
72 FastLED.setBrightness( lightBrightness );
73
74 FastLED.clear();
75 FastLED.show();
76
77}
78
79void lightOn(){
80
82 lightStateOn = true;
83
84}
85void lightOff(){
86
88 lightStateOn = false;
89
90}
91
93
94 if( lightStateOn ){
95
96 lightOff();
97
98 }
99
100 else{
101
102 lightOn();
103
104 }
105
106}
107
109
110 // Check if we have to update the lighting.
111 if( ( (unsigned long)millis() - lightTimer ) > LIGHT_UPDATE_RATE ){
112
113 // Handle the fading effect.
115
116 // Handle the limits if the fadedBrightness variable
117 if( fadeDirection > 0 ){
118
119 // Check if saturated.
121
122 // If it reached the upper limit stop the fading.
124 fadeDirection = 0;
125
126 }
127
128 }
129
130 // Handle the limits if the fadedBrightness variable
131 else if( fadeDirection < 0 ){
132
133 // Check if saturated.
134 if( fadedBrightness <= 0 ){
135
136 // If it reached the lower limit stop the fading.
137 fadedBrightness = 0;
138 fadeDirection = 0;
139
140 }
141
142 }
143
144 // Every other case( basically that means fading is stopped ),
145 // when the lighting is on, we set the fadedBrightness to the
146 // lightBrightness, which is configured in the menu.
147 else if( lightStateOn ){
148
150
151 }
152
153 // Set the calculated brightness.
154 FastLED.setBrightness( fadedBrightness );
155
156 // We have to select the right update function
157 // depending on the lightMode, which is configured
158 // in the menu.
159 switch( lightMode ){
160
162
164
165 break;
166
168
170
171 break;
172
173 case LIGHT_MODE_PULSE:
174
176
177 break;
178
179 case LIGHT_MODE_MUSIC:
180
182
183 break;
184
186
188
189 break;
190
191 case LIGHT_MODE_COLOR:
192
194
195 break;
196
197 }
198
199 // Send the calculated data from the pixel buffers to the LEDs.
200 FastLED.show();
201
202 // Store the system time for the next update timing.
203 lightTimer = millis();
204
205 }
206
207 // Check if we have to update the clap detector.
208 if( ( (unsigned long)millis() - sampleTimer ) > SAMPLE_UPDATE_RATE ){
209
210 // Update the peak detector.
211 peak.update();
212
213 // Check if we have a peak.
214 if( peak.peak == true && prevPeak == false ){
215
216 // Shift down the elements in the curcular buffer.
217 peakTimes[ 0 ] = peakTimes[ 1 ];
218 peakTimes[ 1 ] = peakTimes[ 2 ];
219 peakTimes[ 2 ] = peakTimes[ 3 ];
220 peakTimes[ 3 ] = millis();
221
222 // Calculate the time between the two oldest peaks.
223 unsigned long delta = peakTimes[ 1 ] - peakTimes[ 0 ];
224
225 // At least 1000ms has to elapse after the prevoius clap.
226 if( delta > 1000 ){
227
228 // Try to detect the pattern from the time between the claps.
229 delta = peakTimes[ 2 ] - peakTimes[ 1 ];
230 if( ( delta > 400 ) && ( delta < 900 ) ){
231
232 delta = peakTimes[ 3 ] - peakTimes[ 2 ];
233 if( ( delta > 50 ) && ( delta < 450 ) ){
234
235 Serial.println( F( "Clap pattern match!" ) );
236
237 // The following has to acomplish to toggle the lights:
238 // * Clap switch function has to be enabled.
239 // * We are not in music mode. The clap detector become mad when the drop kicks in.
240 // * Front has to be closed.
242
243 // Play the melody and toggle the lights.
244 clapMelody();
245 lightToggle();
246
247 }
248
249 }
250
251 }
252
253 }
254
255 }
256
257 // Store the current peak state.
259
260 // Store the system time for the next update timing.
261 sampleTimer = millis();
262
263 }
264
265
266
267}
268
270
271 // Generic counter.
272 uint8_t i;
273
274 // Hue value will be calculated here.
275 uint8_t hue;
276
277 // Fold a rainbow pattern around the LED strip,
278 // and make it slowly rotate.
279 for( i = 0; i < LIGHT_STRIP_SIZE; i++ ){
280
281 // Calculate the rainbow color according to the
282 // position on the LED strep.
283 hue = (uint8_t)( (uint16_t)i * 255 / LIGHT_STRIP_SIZE );
284
285 // Shift it a slowly by a timer and limit it between 0 - 255.
286 hue = (uint8_t)( (unsigned long)( hue + millis() / 100 ) % 255 );
287
288 // Store the pixel value.
289 stripLightBuffer[ i ].setHSV( hue, 255, 255 );
290
291 }
292
293 // The LED disc will lit as one color. This color also changy by time.
294 hue = (uint8_t)( (unsigned long)( millis() / 100 ) % 255 );
295
296 for( i = 0; i < LIGHT_SKULL_SIZE; i++ ){
297
298 skullLightBuffer[ i ].setHSV( hue, 255, 255 );
299
300 }
301
302}
303
305
306 // Generic counter.
307 uint8_t i;
308
309 // Value will be calculated here for each pixel on the LED strip.
310 float value;
311
312 for( i = 0; i < LIGHT_STRIP_SIZE; i++ ){
313
314 // Now this is something. It basically wraps a sine wawe around the LED strip.
315 // * When the result of the sine function is -1.0, the brigntess will be 0 ( off ).
316 // * When the result of the sine function is 1.0, the brigntess will be 255 ( full brightness ).
317 // Also it is rotated by time.
318 // sine function ms rotation map it for the it tells how many sine convert from convert from
319 // timer speed LED strip size periods will be mapped -1.0 - 1.0 0.0 - 1.0
320 // to 0.0 - 1.0 to 0 - 255
321 // | | | | | | |
322 // V V V V V V V
323 value = ( sin( (float)millis() / 500.0 + (float)i / (float)LIGHT_STRIP_SIZE * TWO_PI * 2.0 ) / 2.0 + 0.5 ) * 255;
324
325 // Set the valie for each pixel with a mystic blue color.
326 stripLightBuffer[ i ].setHSV( 136, 163, (uint8_t)value );
327
328 }
329
330 // For the led disc we want to imitate that a candle is placed inside the skull.
331 // We split the LED disc to two color components a warmer orange-ish color and
332 // a colder yellow-ish one. The intensity if these pixels will be random. to
333 // mimic a flame in a candle. I think it is realistic enoug, but if you doesn't
334 // like it, you can tweak the parameters.
335 for( i = 0; i < LIGHT_SKULL_SIZE; i++ ){
336
337 // Select the even indexed pixels.
338 if( i % 2 == 0 ){
339
340 skullLightBuffer[ i ].setHSV( 31, 138, random( 50 ) + 205 );
341
342 }
343
344 // Select the odd indexed pixels.
345 else{
346
347 skullLightBuffer[ i ].setHSV( 20, 237, random( 50 ) + 305 );
348
349 }
350
351 }
352
353}
354
356
357 // Generic counter.
358 uint8_t i;
359
360 // The value parameter will change according a sine wave.
361 // The sine function is modulated by the millis() timer.
362 float value;
363
364 // The hue component will be stored to this variable.
365 // It is static to not lose value between function calls.
366 static uint8_t hue;
367
368 // Flag that shows, that random generation required.
369 // It is static to not lose value between function calls.
370 static bool generate = true;
371
372 // Calculate the value.
373 value = ( sin( (float)millis() / 1500.0 ) );
374
375 // If the value is less than zero it's time
376 // to generate a new hue component.
377 if( value < 0.0 ){
378
379 // Also a the new hue should be generated az tero cross event
380 // when the return value of the sine function goes positive
381 // to negative. The generate flag is responsible to detect
382 // the zero crossing event.
383 if( generate ){
384
385 // Generate a new hue value between 0 and 255.
386 hue = random( 0, 255 );
387
388 // Clear the generate flag to skip value generation
389 // for the rest of the negative period.
390 generate = false;
391
392 }
393
394 }
395
396 // If the value is greater or equal to zero we have to display
397 // the generated hue with the value.
398 else{
399
400 // This if statement detect the positive zero crossing event.
401 if( !generate ){
402
403 // In this case we set the generate flag. It is required to
404 // detect the next negative zero crossing.
405 generate = true;
406
407 }
408
409 // The sine function generates an output between -1.0 and 1.0.
410 // We skip for the negative part, but we have to mupltiply the
411 // positive part by 255 to make it's range between 0 and 255.0.
412 value *= 255;
413
414 // Set all LEDs in the strip with this value.
415 for( i = 0; i < LIGHT_STRIP_SIZE; i++ ){
416
417 stripLightBuffer[ i ].setHSV( hue, 255, (uint8_t)value );
418
419 }
420
421 // This multiplication creates a simple effect.
422 // The LEDs on the LED disc wil turn on faster,
423 // than the LEDs on the strip.
424 value *= 5;
425
426 // Becouse the previous multiplication we have to limit the
427 // value to 255.
428 if( value > 255 ){
429
430 value = 255;
431
432 }
433
434 // Set all LEDs in the strip with the value and hue + 128. Why?
435 // The hue parameter represents all the visible colors between 0 and 255.
436 // If we add 128 to it the resoulting color will be the complementary color
437 // of the original one. The complementary color pairs ar always looks nice
438 // next to each other.
439 for( i = 0; i < LIGHT_SKULL_SIZE; i++ ){
440
441 skullLightBuffer[ i ].setHSV( (uint8_t)( hue + 128 ), 255, (uint8_t)value );
442
443 }
444
445 }
446
447}
448
450
451 // Generic counter variable.
452 uint8_t i;
453
454 // Hue parameter will be stored here.
455 uint8_t hue;
456
457 // Value parameter will be stored here.
458 float value;
459
460 // The hue value will be filtered with a complementary
461 // filter, and the result will be stored here.
462 // It is static to not lose value between function calls.
463 static float hueFiltered = 0.0;
464
465 // The speed variable is used to modulete the rotation
466 // effect on the LED strip.
467 // It is static to not lose value between function calls.
468 static float speed = 0.0;
469
470 // This variable counts the number of peaks in a period.
471 // It is used to detect some crazy drops in the music.
472 // It is static to not lose value between function calls.
473 static uint16_t peakCounter = 0;
474
475 // The default value of the LED disc in the skull is 128.
476 static float skullValue = 128.0;
477
478 // This variable stores the hue component of the LED disc.
479 static uint8_t skullHue = 0;
480
481 // The hue parameter is just changing by time. It goes from
482 // 0 to 255 linearly and starts again.
483 hue = (uint8_t)( (unsigned long)( millis() / 100 ) % 255 );
484
485 // Apply 90% complementary filtering to hue.
486 hueFiltered = 0.1 * (float)hue + 0.9 * hueFiltered;
487
488 // Apply 90% complementary filtering to skullValue.
489 skullValue = 0.1 * (float)50.0 + 0.9 * skullValue;
490
491 // Detect if we have a peak.
492 if( peak.peak == true && prevBeat == false ){
493
494 // Instantly crank up the value of the LEDs in the skull
495 // to make it reactive to the beat.
496 skullValue = 200.0;
497
498 // Add some value to the skullHue to generate a new color for
499 // the beat.
500 skullHue += 15;
501
502 // Instantly generate the complementary color of the hue component.
503 hueFiltered = (float)( (uint8_t)( ( (uint8_t)hue ) + 128 ) );
504
505 // Add some value to the speed variable.
506 // It will modulete the rotation later.
507 speed += PI / 8.0;
508
509 // Add some value to the peak counter.
510 peakCounter += 10;
511
512 // If the peakCounter exceeds 300, that means
513 // we have some serious drop in the music.
514 if( peakCounter > 300 ){
515
516 // If the lights are turned on, we respond to the
517 // drop with some smoke.
518 if( lightStateOn ){
519
521
522 }
523
524 // Also we have to reset the peak counter.
525 peakCounter = 0;
526
527 }
528
529
530 }
531
532 // Save the beat state.
534
535 // Every iteration we have to decrement the peakCounter
536 // if it's above 0. It will act like a digital UV meter.
537 if( peakCounter > 0 ){
538
539 peakCounter--;
540
541 }
542
543 // limit the speed variable to 2 pi.
544 if( speed > TWO_PI ){
545
546 speed = 0.0;
547
548 }
549
550 // Calculate the value parameter for each LED in the strip.
551 for( i = 0; i < LIGHT_STRIP_SIZE; i++ ){
552
553
554 // Now this is something. It basically wraps a sine wawe around the LED strip.
555 // * When the result of the sine function is -1.0, the brigntess will be 0 ( off ).
556 // * When the result of the sine function is 1.0, the brigntess will be 255 ( full brightness ).
557 // Also it is rotated by time.
558 // sine function ms rotation map it for the it tells how many sine modul. convert from convert from
559 // timer speed LED strip size periods will be mapped fact. -1.0 - 1.0 0.0 - 1.0
560 // to 0.0 - 1.0 to 0 - 255
561 // | | | | | | | |
562 // V V V V V V V V
563 value = ( sin( (float)millis() / 2000.0 + (float)i / (float)LIGHT_STRIP_SIZE * TWO_PI * 2.0 + speed ) / 2.0 + 0.5 ) * 255;
564
565 // Fill the pixel buffer with the calculated colors.
566 stripLightBuffer[ i ].setHSV( (uint8_t)hueFiltered, 163, (uint8_t)value );
567
568 }
569
570 // Fill the pixel buffer with the calculated colors.
571 for( i = 0; i < LIGHT_SKULL_SIZE; i++ ){
572
573 skullLightBuffer[ i ].setHSV( skullHue, 128, (uint8_t)skullValue );
574
575 }
576
577
578}
579
581
582 // Generic counter.
583 uint8_t i;
584
585 // We set a bright light.
586 for( i = 0; i < LIGHT_STRIP_SIZE; i++ ){
587
588 stripLightBuffer[ i ].setHSV( 43, 61, 255 );
589
590 }
591
592 for( i = 0; i < LIGHT_SKULL_SIZE; i++ ){
593
594 skullLightBuffer[ i ].setHSV( 255, 0, 255 );
595
596 }
597
598}
599
601
602 // Generic counter.
603 uint8_t i;
604
605 // In this mode we set the user defined value to the strip.
606 for( i = 0; i < LIGHT_STRIP_SIZE; i++ ){
607
608 stripLightBuffer[ i ].setHSV( selectedColor, 255, 255 );
609
610 }
611
612 // And create the complementary color for the skull.
613 for( i = 0; i < LIGHT_SKULL_SIZE; i++ ){
614
615 skullLightBuffer[ i ].setHSV( (uint8_t)( selectedColor + 128 ), 255, 255 );
616
617 }
618
619}
620
void clapMelody()
Generate clap melody.
void update()
Update function.
bool peak
Flag, that indicates a peak event.
void fogMachineEnable()
Enable the humidifier.
Definition: fogMachine.cpp:167
void lightModeMusicUpdate()
Update function for the music mode.
int fadeDirection
Stores the direction and speed of the fading.
bool prevPeak
Flag for peak detection. It is used for the clap detection.
void lightInit()
Initialize the LEDs.
bool lightStateOn
This is a flag, that shows the actual state of the lighting.
unsigned long peakTimes[4]
This array is used for the clap detection.
void lightUpdate()
Update function for the lighting.
void lightOn()
Turn on the lighting.
unsigned long sampleTimer
Stores the last system time when the last sample update event happened.
void lightModeKomodoUpdate()
Update function for the komodo mode.
void lightToggle()
Toggle the lighting.
void lightModeCandleUpdate()
Update function for the candle mode.
void lightModePulseUpdate()
Update function for the pulse mode.
CRGB skullLightBuffer[LIGHT_SKULL_SIZE]
Pixel buffer for the LED disc.
int fadedBrightness
The actual brightness of the strips are calculated to this variable.
void lightModeColorUpdate()
Update function for the color mode.
void lightOff()
Turn off the lighting.
unsigned long lightTimer
Stores the last system time when the last light update event happened.
bool prevBeat
Flag for beat detection. It is used for the music visualizer.
void lightModeRainbowUpdate()
Update function for the rainbow mode.
CRGB stripLightBuffer[LIGHT_STRIP_SIZE]
Pixel buffer for the LED strip.
#define LIGHT_SKULL_SIZE
Number of LEDs on the LED disc.
#define LIGHT_STRIP_PIN
The LED strip is connected to pin 12 on the Arduino.
#define LIGHT_SKULL_PIN
The LED disc is connected to pin 11 on the Arduino.
#define LIGHT_FADE_RATE
The speed of dimming when turning on or off the lighting.
#define LIGHT_STRIP_SIZE
Number of LEDs on the LED strip.
#define LIGHT_UPDATE_RATE
Time between two LED updates in ms.
#define SAMPLE_UPDATE_RATE
It is used for the clap switch.
enum lightMode_t lightMode
It will store the user selected light mode.
Definition: menu.cpp:44
uint8_t lightBrightness
It will store the user selected brightness.
Definition: menu.cpp:48
uint8_t selectedColor
It will store the user selected color.
Definition: menu.cpp:46
@ LIGHT_MODE_MUSIC
Definition: menu.hpp:161
@ LIGHT_MODE_RAINBOW
Definition: menu.hpp:158
@ LIGHT_MODE_KOMODO
Definition: menu.hpp:162
@ LIGHT_MODE_COLOR
Definition: menu.hpp:163
@ LIGHT_MODE_PULSE
Definition: menu.hpp:160
@ LIGHT_MODE_CANDLE
Definition: menu.hpp:159
bool clapSwitchEnabled
It will store the user selected state of the clap switch.
Definition: menu.cpp:52
enum frontState_t frontState
Store the state of the front panel.
@ FRONT_CLOSE
Front is closed.
musicChannel peak
Peak detector object.