-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathLighting.cpp
1328 lines (1254 loc) · 61.5 KB
/
Lighting.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include <FastLED.h>
#include <EEPROM.h> //For persistent settings
#include "Lighting.h"
#include "Config.h"
#include "NTPTime.h"
#include "TimedEffects.h"
CRGB off_color = CRGB::Black;
CRGB *leds = new CRGB[NUM_LEDS+1]; //array that gets rendered, +1 for sacrifice LED in case its needed
CRGB *spotlightLed = new CRGB[WIDTH*HEIGHT+1]; //dedicated spotlight array if on seperate pin, +1 for sacrifice LED in case its needed
CRGB *spotlights = new CRGB[WIDTH * HEIGHT]; //array to keep track of spotlight colors that we set on the web server
//default background colors
CRGB h_ten_color;
CRGB h_one_color;
CRGB m_ten_color;
CRGB m_one_color;
CRGB bg;
CRGB bg2;
//default patterns/colors
byte foregroundPattern = 1;
byte backgroundPattern = 1;
byte spotlightPattern = 1;
const char offPattern[] = "off"; //effect 0
const char solidPattern[] = "solid"; //effect 1
const char rainbowPattern[] = "rainbow"; //effect 2
const char gradientPattern[] = "gradient"; //effect 3
const char rainPattern[] = "rain"; //effect 4
const char sparklePattern[] = "sparkle"; //effect 5
const char firePattern[] = "fire"; //effect 6
const char *const effectNames[] PROGMEM = {offPattern, solidPattern, rainbowPattern, gradientPattern, rainPattern, sparklePattern, firePattern};
//The rest is stored in the spotlights[] array
//Needed for effects requiring some 2d-esque grid
struct grid{
int verticalSegments[HEIGHT * LEDS_PER_LINE][WIDTH + 1];
/* verticalSegment at these 2d indexes refer to the following LED's (with LEDS_PER_LINE = 2):
* - - - - - - - - - -
* [0,0] [0,1] [0,2] [0,3] [0,4] [0,5]
* [1,0] [1,1] [1,2] [1,3] [1,4] [1,5]
* - - - - - - - - - -
* [2,0] [2,1] [2,2] [2,3] [2,4] [2,5]
* [3,0] [3,1] [3,2] [3,3] [3,4] [3,5]
* - - - - - - - - - -
*/
int horizontalSegments[HEIGHT+1][WIDTH * LEDS_PER_LINE];
/* horizontalSegment at these 2d indexes refer to the following LED's (with LEDS_PER_LINE = 2):
* [0,0][0,1] [0,2][0,3] [0,4][0,5] [0,6][0,7] [0,8][0,9]
* - - - - - -
* - - - - - -
* [1,0][1,1] [1,2][1,3] [1,4][1,5] [1,6][1,7] [1,8][1,9]
* - - - - - -
* - - - - - -
* [2,0][2,1] [2,2][2,3] [2,4][2,5] [2,6][2,7] [2,8][2,9]
*/
//Age of raindrop (Time to live) for rain effect
int verticalLedTTL[HEIGHT*LEDS_PER_LINE][WIDTH+1];
int horizontalLedTTL[HEIGHT][WIDTH * LEDS_PER_LINE];
//Height for fire effect
int fireHeight[WIDTH*LEDS_PER_LINE+1];
} grid2d;
int power = 1;
int hueOffset = 0;
byte segmentBrightness = 77; //0-255
byte spotlightBrightness = 255; //0-255
byte backgroundBrightness = 25;
byte foregroundTransparency = 255; //255 = solid
boolean autobrightness = false;
int rainbowRate = 5;
uint32_t clockRefreshTimer = 0;
uint32_t lastUpdate = 0;
bool updateSettings = false;
bool autoEffect = false;
uint32_t loadingCursorPosition = 0;
byte hyphenLength = 0;
CRGB hyphenColor = CRGB::Black;
strip stripSegment, convert;
changelist lightingChanges;
uint16_t debugPrintCounter = 0;
/* Clock Segment Index
# 0 #
1 2
# 3 #
4 5
# 6 #
*/
// x0123456 which segment needs to be up for that number
const int PROGMEM zero = 0b01110111;
const int PROGMEM one = 0b00010010;
const int PROGMEM two = 0b01011101;
const int PROGMEM three = 0b01011011;
const int PROGMEM four = 0b00111010;
const int PROGMEM five = 0b01101011;
const int PROGMEM six = 0b01101111;
const int PROGMEM seven = 0b01010010;
const int PROGMEM eight = 0b01111111;
const int PROGMEM nine = 0b01111011;
short lightSensorValues[AUTOBRIGHTNESS_SAMPLES];
long lastBrightnessUpdate = 0;
double goalLightValue = 0;
//************************************************//
// Show Selected Lighting //
//************************************************//
//Pushes the leds[] colour array to the physical LED's with the applied settings
/* Renders the spotlights first, then the background, then the clock, then the hyphen if enabled
*/
void showLightingEffects() {
if(power == 0){
fill_solid(leds, NUM_LEDS, CRGB::Black);
#ifdef SPOTLIGHTPIN
fill_solid(spotlightLed, WIDTH*HEIGHT, CRGB::Black);
#endif
return;
}
if(autobrightness){
if(millis() >= lastBrightnessUpdate + AUTOBRIGHTNESS_DELAY*1000){
lastBrightnessUpdate = millis();
int avgLight = 0;
for(int i=1 ; i<AUTOBRIGHTNESS_SAMPLES ; i++){
avgLight += lightSensorValues[i];
lightSensorValues[i-1] = lightSensorValues[i];
}
lightSensorValues[AUTOBRIGHTNESS_SAMPLES-1] = analogRead(LIGHT_SENSOR); //set new sample
avgLight += lightSensorValues[AUTOBRIGHTNESS_SAMPLES-1]; //average the past N readings
goalLightValue = (avgLight/AUTOBRIGHTNESS_SAMPLES /1024.0)*2; //Between 0-2
}
//Adjust brightness to transition to new brightness each frame if autobrightness decides different values
byte goalSegmentBrightness = min(max((int)( 40.0 * goalLightValue*goalLightValue ), 5),150); //Clamp between 5 and 150, median 40
byte goalBackgroundBrightness = min(max((int)( 20.0 * goalLightValue*goalLightValue ), 2),100); //Clamp between 2 and 100, median 20
byte goalSpotlightBrightness = min(max((int)(160.0 * goalLightValue*goalLightValue ),30),255); //Clamp between 30 and 255, median 160
if (goalSegmentBrightness > segmentBrightness) segmentBrightness++;
else if(goalSegmentBrightness < segmentBrightness) segmentBrightness--;
if (goalBackgroundBrightness > backgroundBrightness) backgroundBrightness++;
else if(goalBackgroundBrightness < backgroundBrightness) backgroundBrightness--;
if (goalSpotlightBrightness > spotlightBrightness) spotlightBrightness++;
else if(goalSpotlightBrightness < spotlightBrightness) spotlightBrightness--;
}
//If we toggle on time-scheduled effects
if(autoEffect) scheduleLighting();
//Regular lighting behaviour
else{
switch (spotlightPattern) {
case 0: //off
solidSpotlights(CRGB::Black);
break;
case 1: //Solid
solidUniqueSpotlights();
applySpotlightBrightness();
break;
case 2: //rainbow
rainbowSpotlights();
applySpotlightBrightness();
break;
case 3: //gradient
gradientSpotlights(spotlights[0], spotlights[1]);
applySpotlightBrightness();
break;
case 4: //rain
rain(30, CRGB::Black, spotlights[0]);
dimSpotlights(max(spotlightBrightness / 8, 2));
break;
case 5: //sparkle
sparkle(10 , spotlights[0] , NUM_SEGMENTS * LEDS_PER_LINE , WIDTH * HEIGHT, 255 - spotlightBrightness); //10 chance seems fine. Note this isnt 10% or 10/255% chance. See sparkle() for how the chance works
dimSpotlights(max(spotlightBrightness / 8, 2));
break;
case 6: //fire
fire();
dimSpotlights(max(spotlightBrightness / 8, 2));
break;
}
//Background
switch (backgroundPattern) {
case 0: //off
solidSegments(CRGB::Black); break;
case 1: //solid
solidSegments(bg);
dimSegments(255 - backgroundBrightness);
break;
case 2: //rainbow
for (int i = 0; i < NUM_SEGMENTS; i++) rainbowSegment(i, segmentLightingOffset(i)*LEDS_PER_LINE * rainbowRate, rainbowRate);
dimSegments(255 - backgroundBrightness);
break;
case 3: //gradient
for (int i = 0; i < NUM_SEGMENTS; i++) gradientSegment(i, bg, bg2);
dimSegments(255 - backgroundBrightness);
break;
case 4: //rain
rain(15, bg, CRGB::Black);
dimSegments(max(backgroundBrightness / 10, 50));
break;
case 5: //sparkle
sparkle(100 , bg , 0 , NUM_SEGMENTS * LEDS_PER_LINE, 255 - backgroundBrightness); //100 chance seems fine. Note this isnt 100% or 100/255% chance. See sparkle() for how the chance works
dimSegments(max(backgroundBrightness / 10, 2));
break;
case 6: //fire
if(spotlightPattern != 6) fire(); //call the effect if it hasnt been called already
dimSegments(max(backgroundBrightness / 8, 2));
break;
case 255: //Loading effect from manual config
loadingEffect(bg);
dimSpotlights(50);
break;
}
//Foreground
switch (foregroundPattern) {
case 0:
break;//do nothing. Just here to acknowledge this option exists
case 1: //solid
if (clockRefreshTimer == FRAMES_PER_SECOND * 30) { updateTime();clockRefreshTimer = 0;}
#ifdef _12_HR_CLOCK
render_clock_to_display(getHour12(), getMinute(), 255 - segmentBrightness);
#elif defined(_24_HR_CLOCK)
render_clock_to_display(getHour24(), getMinute(), 255 - segmentBrightness);
#endif
clockRefreshTimer++;
break;
case 2: //rainbow
if (clockRefreshTimer == FRAMES_PER_SECOND * 30) { updateTime();clockRefreshTimer = 0;}
#ifdef _12_HR_CLOCK
render_clock_to_display_rainbow(getHour12(), getMinute(), 255 - segmentBrightness);
#elif defined(_24_HR_CLOCK)
render_clock_to_display_rainbow(getHour24(), getMinute(), 255 - segmentBrightness);
#endif
clockRefreshTimer++;
break;
case 3: //gradient
if (clockRefreshTimer == FRAMES_PER_SECOND * 30) { updateTime();clockRefreshTimer = 0;}
#ifdef _12_HR_CLOCK
render_clock_to_display_gradient(getHour12(), getMinute(), 255 - segmentBrightness);
#elif defined(_24_HR_CLOCK)
render_clock_to_display_gradient(getHour24(), getMinute(), 255 - segmentBrightness);
#endif
clockRefreshTimer++;
break;
}
}
//Hyphen segment if enabled
if(hyphenColor.r != 0 || hyphenColor.g != 0 || hyphenColor.b != 0){
strip seg = segmentToLedIndex(hyphenSegment);
if(seg.start == -1) return;
//If gap is odd, round the leftLedSkip up.since typically the m_ten values will be further right (for digits 1 & 7)
//Hyphen length of 4 = 3left -hyphen- 2right. Clamp values to 0-LEDS_PER_LINE
const int leftLedSkip = min(max(0,(LEDS_PER_LINE-hyphenLength)/2 + (LEDS_PER_LINE-hyphenLength)%2),LEDS_PER_LINE);
if(seg.reverse){
for(int i=0; i<leftLedSkip+hyphenLength ; i++){
if(i<leftLedSkip) continue;
leds[seg.start-i] = hyphenColor;
}
}else{
for(int i=0; i<leftLedSkip+hyphenLength ; i++){
if(i<leftLedSkip) continue;
leds[seg.start+i] = hyphenColor;
}
}
}
//Change rainbow color
hueOffset += rainbowRate;
//Compensate for different segment diffusion if it is configured/exists
for(int i=0;i<sizeof(segmentBrightnessCompensation)/sizeof(segmentBrightnessCompensation[0]);i++)
if(segmentBrightnessCompensation[i]!=0) dimSegment(i,segmentBrightnessCompensation[i]);
//Shift LED's by 1 if enabled sacrifice LED
#ifdef SACRIFICELED
shiftLedsByOne();
#endif
}
//************************************************//
// Display Functions //
//************************************************//
//Sets each LED in the segments to black (does not toch the splotlights)
void clearDisplay() {
fill_solid(leds, NUM_LEDS+1, CRGB::Black);
#ifdef SPOTLIGHTPIN
fill_solid(spotlightLed, WIDTH*HEIGHT+1, CRGB::Black);
#endif
}
//Dim all segments/spotlights proportional to brightness. segmentBrightness = 255 means no dimming. The Dedicated function
// is for spotlights with their own dedicated data pin instead of being at the end of the segment LED's
void applySegmentBrightness() {
fadeToBlackBy(leds , NUM_SEGMENTS * LEDS_PER_LINE , 255 - segmentBrightness );
}
void applySpotlightBrightness() {
#ifndef SPOTLIGHTPIN
fadeToBlackBy(&leds[NUM_SEGMENTS * LEDS_PER_LINE] , WIDTH * HEIGHT , 255 - spotlightBrightness);
#else
fadeToBlackBy(spotlightLed , WIDTH * HEIGHT , 255 - spotlightBrightness);
#endif
}
//Dims all segments/spotlights by a certain amount. val=0 is no dimming (original brightness), val=255 is full black
void dimSegments(byte val) { fadeToBlackBy(leds , NUM_SEGMENTS * LEDS_PER_LINE , val);}
void dimSpotlights(byte val) {
#ifndef SPOTLIGHTPIN
fadeToBlackBy(&leds[NUM_SEGMENTS * LEDS_PER_LINE] , WIDTH * HEIGHT , val);
#else
fadeToBlackBy(spotlightLed , WIDTH * HEIGHT , val);
#endif
}
//Dim a single LED. Don't think this is used
void dimLed(int index, byte val) { fadeToBlackBy(&leds[index] , 1 , val);}
//Dim a single segment instead of all segments.
//Uses the 1-indexed segment index. See Config.h under "1-Index reference for segmentWiringOrder"
void dimSegment(int segment, byte val) {
if (segment == -1) return;
strip segmentStruct = segmentToLedIndex(segment);
if(segmentStruct.start == -1) return;
if (segmentStruct.reverse) fadeToBlackBy(&leds[segmentStruct.start - LEDS_PER_LINE + 1], LEDS_PER_LINE , val);
else fadeToBlackBy(&leds[segmentStruct.start] , LEDS_PER_LINE , val);
}
//************************************************//
// Clock Effects //
//************************************************//
//Adds the clock colour data into the leds[] array
void render_clock_to_display(int h, int m) {render_clock_to_display(h, m, 0);}
void render_clock_to_display(int h, int m, byte dim) {
const uint8_t light_tens_h = sevenSegment(h / 10);
const uint8_t light_ones_h = sevenSegment(h % 10);
const uint8_t light_tens_m = sevenSegment(m / 10);
const uint8_t light_ones_m = sevenSegment(m % 10);
//Set the digits we want to the color
for (int i = 0; i < 7; i++) {
//Change hours tens LEDS
if (light_tens_h & 0b01000000 >> i && (h/10 || DISPLAY_ZERO_IN_TENS_DIGIT)) { //use bitmask to see if the segment is supposed to be on for that digit
CRGB segmentColor = dimColor(h_ten_color, dim);
addSegmentColor(h_ten[i], segmentColor, foregroundTransparency);
}
//Change hours ones LEDS
if (light_ones_h & 0b01000000 >> i) {
CRGB segmentColor = dimColor(h_one_color, dim);
addSegmentColor(h_one[i], segmentColor, foregroundTransparency);
}
//Change minutes tens LEDS
if (light_tens_m & 0b01000000 >> i) {
CRGB segmentColor = dimColor(m_ten_color, dim);
addSegmentColor(m_ten[i], segmentColor, foregroundTransparency);
}
//Change minutes ones LEDS
if (light_ones_m & 0b01000000 >> i) {
CRGB segmentColor = dimColor(m_one_color, dim);
addSegmentColor(m_one[i], segmentColor, foregroundTransparency);
}
}
}
void render_clock_to_display_rainbow(int h, int m) {render_clock_to_display_rainbow(h, m, 0);}
void render_clock_to_display_rainbow(int h, int m, byte dim) {
const uint8_t light_tens_h = sevenSegment(h / 10);
const uint8_t light_ones_h = sevenSegment(h % 10);
const uint8_t light_tens_m = sevenSegment(m / 10);
const uint8_t light_ones_m = sevenSegment(m % 10);
//Set the digits we want to the color
for (int i = 0; i < 7; i++) {
//Change hours tens LEDS
if (light_tens_h & 0b01000000 >> i && (h/10 || DISPLAY_ZERO_IN_TENS_DIGIT) ) { //use bitmask to see if the segment is supposed to be on for that digit
rainbowSegment(h_ten[i], (byte)segmentLightingOffset(h_ten[i])*rainbowRate * LEDS_PER_LINE, rainbowRate, foregroundTransparency);
dimSegment(h_ten[i], dim);
}
//Change hours ones LEDS
if (light_ones_h & 0b01000000 >> i) {
rainbowSegment(h_one[i], (byte)segmentLightingOffset(h_one[i])*rainbowRate * LEDS_PER_LINE, rainbowRate, foregroundTransparency);
dimSegment(h_one[i], dim);
}
//Change minutes tens LEDS
if (light_tens_m & 0b01000000 >> i) {
rainbowSegment(m_ten[i], (byte)segmentLightingOffset(m_ten[i])*rainbowRate * LEDS_PER_LINE, rainbowRate, foregroundTransparency);
dimSegment(m_ten[i], dim);
}
//Change minutes ones LEDS
if (light_ones_m & 0b01000000 >> i) {
rainbowSegment(m_one[i], (byte)segmentLightingOffset(m_one[i])*rainbowRate * LEDS_PER_LINE, rainbowRate, foregroundTransparency);
dimSegment(m_one[i], dim);
}
}
}
void render_clock_to_display_gradient(int h, int m) {render_clock_to_display_gradient(h, m, 0);}
void render_clock_to_display_gradient(int h, int m, byte dim) {
const uint8_t light_tens_h = sevenSegment(h / 10);
const uint8_t light_ones_h = sevenSegment(h % 10);
const uint8_t light_tens_m = sevenSegment(m / 10);
const uint8_t light_ones_m = sevenSegment(m % 10);
//Set the digits we want to the color
for (int i = 0; i < 7; i++) {
//Change hours tens LEDS
if (light_tens_h & 0b01000000 >> i && (h/10 || DISPLAY_ZERO_IN_TENS_DIGIT)) { //use bitmask to see if the segment is supposed to be on for that digit
gradientSegment(h_ten[i], h_ten_color, h_one_color, foregroundTransparency);
dimSegment(h_ten[i], dim);
}
//Change hours ones LEDS
if (light_ones_h & 0b01000000 >> i) {
gradientSegment(h_one[i], h_ten_color, h_one_color, foregroundTransparency);
dimSegment(h_one[i], dim);
}
//Change minutes tens LEDS
if (light_tens_m & 0b01000000 >> i) {
gradientSegment(m_ten[i], h_ten_color, h_one_color, foregroundTransparency);
dimSegment(m_ten[i], dim);
}
//Change minutes ones LEDS
if (light_ones_m & 0b01000000 >> i) {
gradientSegment(m_one[i], h_ten_color, h_one_color, foregroundTransparency);
dimSegment(m_one[i], dim);
}
}
}
//************************************************//
// Color Effects //
//************************************************//
//convert from abstract segment index to wiring LED positions
//Segment index 1 should be the first horizontal segment in the top-left, but may not be the segment
// that is wired first to the ESP. This function gets that abstract index, references it with how
// the segment is wired according to segmentWiringOrder in Config.h, then sets that segments colour
// to the colour specified in the parameter.
void setSegmentColor(int segment, CRGB color) {
if (segment == -1) return;
stripSegment = segmentToLedIndex(segment);
if(stripSegment.start == -1) return;
if (stripSegment.reverse) {
for (int i = 0; i < LEDS_PER_LINE; i++) {
leds[stripSegment.start - i] = color;
}
} else {
for (int i = 0; i < LEDS_PER_LINE; i++) {
leds[stripSegment.start + i] = color;
}
}
}
//Adds a translucent colour layer on top of the existing colour for that LED segment.
//Used for effects such as clock transparency where we may not want the clock to be a solid colour,
// but instead a translucent layer that the background effect can still show under.
void addSegmentColor(int segment, CRGB color, byte transparency) {
if (segment == -1) return;
stripSegment = segmentToLedIndex(segment); //convert from abstract segment index to wiring LED positions
if(stripSegment.start == -1) return;
if (stripSegment.reverse) {
for (int i = 0; i < LEDS_PER_LINE; i++) {
CRGB newColor;
newColor.red = (byte)(color.red * (transparency / 255.0) + leds[stripSegment.start - i].red * (1 - (transparency / 255.0)));
newColor.green = (byte)(color.green * (transparency / 255.0) + leds[stripSegment.start - i].green * (1 - (transparency / 255.0)));
newColor.blue = (byte)(color.blue * (transparency / 255.0) + leds[stripSegment.start - i].blue * (1 - (transparency / 255.0)));
leds[stripSegment.start - i] = newColor;
}
} else {
for (int i = 0; i < LEDS_PER_LINE; i++) {
CRGB newColor;
newColor.red = (byte)(color.red * (transparency / 255.0) + leds[stripSegment.start + i].red * (1 - (transparency / 255.0)));
newColor.green = (byte)(color.green * (transparency / 255.0) + leds[stripSegment.start + i].green * (1 - (transparency / 255.0)));
newColor.blue = (byte)(color.blue * (transparency / 255.0) + leds[stripSegment.start + i].blue * (1 - (transparency / 255.0)));
leds[stripSegment.start + i] = newColor;
}
}
}
void setSpotlightColor(int index, CRGB color) {
spotlights[index] = color;
}
void solidSegments(CRGB color) {
for (int i = 0; i < NUM_SEGMENTS*LEDS_PER_LINE; i++)
leds[i] = color;
}
//Set all spotlights to a single colour.
//Used to turn all LEDS to black, so don't write to spotlights[]
// with the data, keep the original colour data
void solidSpotlights(CRGB color) {
#ifndef SPOTLIGHTPIN
for (int i = 0; i < WIDTH * HEIGHT; i++)
leds[LEDS_PER_LINE * NUM_SEGMENTS + i ] = color;
#else
for (int i = 0; i < WIDTH * HEIGHT; i++)
spotlightLed[i] = color;
#endif
}
void solidUniqueSpotlights(){
#ifndef SPOTLIGHTPIN
for (int i = 0; i < WIDTH * HEIGHT; i++)
leds[spotlightToLedIndex(i)] = spotlights[i]; //Set spotlight color to the dedicated array of
#else
for (int i = 0; i < WIDTH * HEIGHT; i++)
spotlightLed[spotlightToLedIndexDedicated(i)] = spotlights[i]; //Set spotlight color to the dedicated array of spotlights
#endif
}
//Creates a gradient segment. transparency=255 is solid, 0 is transparent
//colour1 is the colour in the top left, colour2 is the colour in the bottom right
//This function then regresses the gradient direction and colour shift based on the segment
// index position
void gradientSegment(int segment, CRGB color1, CRGB color2) {gradientSegment(segment, color1, color2, 255);}
void gradientSegment(int segment, CRGB color1, CRGB color2, byte transparency) {
const int maxLedVal = (WIDTH + HEIGHT) * LEDS_PER_LINE; //max value to use for division
const float increment = 1.0 / (maxLedVal); //The change in brightness for each consecutive LED
const int offset = segmentLightingOffset(segment); //the segment offset brightness
strip segmentStruct = segmentToLedIndex(segment);
if(segmentStruct.start == -1) return;
for (int i = 0; i < LEDS_PER_LINE; i++) {
// color2 is from 0->100% color1 is from 100->0%
// depending on the segmentLightingOffset, color2 increases and color1 increases linearly to create a gradient
CRGB color;
// totalLedOffset *increment 100% - totalLedOffset *increment
color.red = (byte)(( color2.red * (offset * LEDS_PER_LINE + i) * increment ) + ( color1.red * (maxLedVal - (offset * LEDS_PER_LINE + i)) * increment ));
color.green = (byte)(( color2.green * (offset * LEDS_PER_LINE + i) * increment ) + ( color1.green * (maxLedVal - (offset * LEDS_PER_LINE + i)) * increment ));
color.blue = (byte)(( color2.blue * (offset * LEDS_PER_LINE + i) * increment ) + ( color1.blue * (maxLedVal - (offset * LEDS_PER_LINE + i)) * increment ));
if (segmentStruct.reverse) {
leds[segmentStruct.start - i].r = color.r*(transparency/255.0) + leds[segmentStruct.start - i].r*(1-(transparency/255.0));
leds[segmentStruct.start - i].g = color.g*(transparency/255.0) + leds[segmentStruct.start - i].g*(1-(transparency/255.0));
leds[segmentStruct.start - i].b = color.b*(transparency/255.0) + leds[segmentStruct.start - i].b*(1-(transparency/255.0));
} else {
leds[segmentStruct.start + i].r = color.r*(transparency/255.0) + leds[segmentStruct.start + i].r*(1-(transparency/255.0));
leds[segmentStruct.start + i].g = color.g*(transparency/255.0) + leds[segmentStruct.start + i].g*(1-(transparency/255.0));
leds[segmentStruct.start + i].b = color.b*(transparency/255.0) + leds[segmentStruct.start + i].b*(1-(transparency/255.0));
}
}
}
void gradientSpotlights(CRGB color1, CRGB color2) {
//start at color offset 1 instead of 0. I offset by 1 since the spotlights blend between segments with offset n and offset n+1, so the midpoint color would be LEDS_PER_LINE*offset*(n+1)
//cause color with offset 'n' would have color offsets: LEDS_PER_LINE*offset*n -> LEDS_PER_LINE*offset*(n+1), and the other segment would have offsets from LEDS_PER_LINE*offset*(n+1) -> LEDS_PER_LINE*offset*(n+2)
/*
-- -- -- -- -- --
| 0| 1| 2| 3| 4| 5|
-- -- -- -- -- --
| 1| 2| 3| 4| 5| 6|
-- -- -- -- -- --
*/
const int maxLedVal = WIDTH * HEIGHT; //max value to use for division
const float increment = 1.0 / maxLedVal; //The change in brightness for each consecutive segment
for (int i = 0; i < WIDTH * HEIGHT; i++) {
const int offset = i % WIDTH + i / WIDTH;
CRGB color;
// color2 is from 0->100% color1 is from 100->0%
// depending on the segmentLightingOffset, color2 increases and color1 increases linearly to create a gradient
color.red = (byte)(( color2.red * increment * (offset + 1)) + (color1.red * ( increment * (maxLedVal - offset - 2 ) ) ));
color.green = (byte)(( color2.green * increment * (offset + 1)) + (color1.green * ( increment * (maxLedVal - offset - 2 ) ) ));
color.blue = (byte)(( color2.blue * increment * (offset + 1)) + (color1.blue * ( increment * (maxLedVal - offset - 2 ) ) ));
#ifdef SPOTLIGHTPIN
spotlightLed[spotlightToLedIndexDedicated(i)] = color;
#else
leds[spotlightToLedIndex(i)] = color;
#endif
}
}
void rainbowSegment(int segment, uint8_t offset, uint8_t rate) {rainbowSegment(segment,offset,rate,255);}
void rainbowSegment(int segment, uint8_t offset, uint8_t rate, byte transparency){
stripSegment = segmentToLedIndex(segment); //convert from abstract segment index to wiring LED positions
if(stripSegment.start == -1) return;
if (stripSegment.reverse) {
stripSegment.start -= (LEDS_PER_LINE - 1); //we need the lowest LED index since the fill_rainbow counts up
offset += rate * LEDS_PER_LINE; //moving in opposite direction, switch offset from min->max to max->min, Since its reverse, instead of starting hue at 0 then moving to 10, we should start at 10, then move to 0
rate = -rate; //moving in opposite direction, reverse hue. Probably not good practice to modify the parameter variable
}
//Save the old background colors since fill_rainbow does not save transparency
CRGB oldColors[LEDS_PER_LINE];
for (int i = 0; i < LEDS_PER_LINE; i++) {
oldColors[i] = leds[stripSegment.start + i];
}
//Fill segment with rainbow
// start of LED segment # of LEDS to change segment offset + hue color reffect rate of color change
fill_rainbow(&leds[stripSegment.start], LEDS_PER_LINE, offset + hueOffset, rate);
//Note that offset already contains the lightingOffset value depending on the location of the segment
//Add back the background color
for (int i = 0; i < LEDS_PER_LINE; i++) {
CRGB newColor;
newColor.red = leds[stripSegment.start + i].red * (foregroundTransparency / 255.0) + oldColors[i].red * (1 - (foregroundTransparency / 255.0));
newColor.green = leds[stripSegment.start + i].green * (foregroundTransparency / 255.0) + oldColors[i].green * (1 - (foregroundTransparency / 255.0));
newColor.blue = leds[stripSegment.start + i].blue * (foregroundTransparency / 255.0) + oldColors[i].blue * (1 - (foregroundTransparency / 255.0));
leds[stripSegment.start + i] = newColor;
}
}
void rainbowSpotlights(){
//This rainbow hue color is taken from the middle of the top/left segment, rather than the average/median between the 4 surronding segments.
//If you want to change this to be the average/median of the 4 surrounding segments, get rid of the '/2' at the end of the totalHueOffset variable: "rainbowRate*LEDS_PER_LINE/2"
for(int y=0 ; y<HEIGHT ; y++){
for(int x=0 ; x<WIDTH ; x++){
// current color | segmentOffset*amount of color changed/segment |
byte totalHueOffset = hueOffset + (x+y)*rainbowRate*LEDS_PER_LINE + rainbowRate*LEDS_PER_LINE/2;
#ifdef SPOTLIGHTPIN
fill_rainbow(&spotlightLed[spotlightToLedIndexDedicated(y*WIDTH+x)], 1, totalHueOffset , 0);
#else
fill_rainbow(&leds[spotlightToLedIndex(y*WIDTH+x)], 1, totalHueOffset , 0);
// spotlight LED index # of LEDS Hue offset Delta (since its only 1 LED, 0 is fine)
#endif
}
}
}
void sparkle(int chance, int ledStart, int len) { sparkle(chance, CHSV(random8(), 255, 255), ledStart, len, 0);}
void sparkle(int chance, CRGB color, int ledStart, int len) { sparkle(chance, color, ledStart, len, 0);}
void sparkle(int chance, CRGB color, int ledStart, int len, byte dim) {
while (1) {
byte randomVal = random8(128);
//give a chance to spawn multiple sparkles in 1 run. That way we arent limited to a 0-100% chance each iteration.
//Depending on the chance, it can be anywhere from 0-12800% chance to spawn, where each 100% guarantees 1 sparkle. With a 255 chance, average of ~4 particles spawn per iteration, which should be enough.
//If you have a really large array of LED's and you want to increase this, change random8(128) to a lower value like random8(64) for a ~8 particle spawn per iteration
if (chance > randomVal) {
uint16_t randNum = random16(len); //pick random index within the specified range
chance -= randomVal; //decrement counter so we could spawn more than 1 per iteration
#ifdef SPOTLIGHTPIN
//Spotlights
if(ledStart >= NUM_LEDS || len >= NUM_LEDS)
spotlightLed[(ledStart + randNum) % (WIDTH*HEIGHT)] = color;
//segments
if(ledStart < NUM_LEDS){
leds[(ledStart + randNum) % NUM_LEDS] = color; //apply sparkle to segments and spotlights if the value of ledStart and len permit
dimLed((ledStart + randNum) % NUM_LEDS, dim); //Apply initial brightness/dim LED when spawned
}
#else
leds[ledStart + randNum] = color; //apply sparkle to segments and spotlights if the value of ledStart and len permit
dimLed(ledStart + randNum, dim); //Apply initial brightness/dim LED when spawned
#endif
} else break;
}
}
void rain(byte chance, CRGB color, CRGB spotlightColor) {
const int verticalLedChanceMultiplier = 5;
//Run if we set segment effect to a non-black color
if(backgroundPattern == 4 && (color.r!=0 || color.g !=0 || color.b !=0)){
//Horizontal segment effect
//Calculate falling from a Time-To-Live (TTL) array
for(int y=0 ; y<HEIGHT ; y++){
for(int x=0 ; x<WIDTH*LEDS_PER_LINE ; x++){
if(grid2d.horizontalLedTTL[y][x] == 1){
//Dont set a TTL on the last row, since it cant fall after that and it would be out-of-bounds
if(y<HEIGHT-1) grid2d.horizontalLedTTL[y+1][x] = LEDS_PER_LINE+1;
leds[grid2d.horizontalSegments[y+1][x]] = color;
}
grid2d.horizontalLedTTL[y][x]--;
}
}
//Vertical segment effect
//Calculate falling from a Time-To-Live (TTL) array
for(int y=HEIGHT*LEDS_PER_LINE-2 ; y >= 0 ; y--){
for(int x=0 ; x < WIDTH+1 ; x++){
grid2d.verticalLedTTL[y][x]--;
if(grid2d.verticalLedTTL[y][x] == LEDS_PER_LINE){
grid2d.verticalLedTTL[y+1][x] = LEDS_PER_LINE+1; //can be any number really, the number above should be this number - 1
leds[grid2d.verticalSegments[y+1][x]] = color;
}
}
}
//Spawn new raindrops on segments
while (1) {
byte randomVal = random8(128);
//give a chance to spawn multiple raindrops in 1 run. That way we arent limited to a 0-100% chance each iteration.
//Depending on the chance, it can be anywhere from 0-12800% chance to spawn, where each 100% guarantees 1 sparkle. With a 255 chance, average of ~4 particles spawn per iteration, which should be enough.
//If you have a really large array of LED's and you want to increase this, change random8(128) to a lower value like random8(64) for a ~8 particle spawn per iteration
if (chance > randomVal) {
uint16_t randNum = random16(WIDTH*LEDS_PER_LINE + verticalLedChanceMultiplier*(WIDTH+1)); //pick random index within the specified range. The WIDTH+1 is for the vertical segments
//Spawn on a horizontal segment
if(randNum < WIDTH*LEDS_PER_LINE){
grid2d.horizontalLedTTL[0][randNum] = LEDS_PER_LINE+1;
leds[grid2d.horizontalSegments[0][randNum]] = color;
}
//Spawn on a vertical segment
else{
grid2d.verticalLedTTL[0][(randNum - WIDTH*LEDS_PER_LINE)/verticalLedChanceMultiplier] = LEDS_PER_LINE+1;
leds[grid2d.verticalSegments[0][(randNum - WIDTH*LEDS_PER_LINE)/verticalLedChanceMultiplier]] = color;
}
chance -= randomVal; //decrement counter so we could spawn more than 1 per iteration
} else break;
}
}
//if background not selected but spotlights are, generate a map of raindrops but dont render it
else if(backgroundPattern != 4 && (spotlightColor.r!=0 || spotlightColor.g !=0 || spotlightColor.b !=0)){
//standalone spotlight effect if background rain is not selected
//Use the above code for generating rain on horizontal segments, but dont render the segments
for(int y=0 ; y<HEIGHT ; y++){
for(int x=0 ; x<WIDTH*LEDS_PER_LINE ; x++){
if(grid2d.horizontalLedTTL[y][x] == 1){
if(y<HEIGHT-1){
grid2d.horizontalLedTTL[y+1][x] = LEDS_PER_LINE+1;
leds[grid2d.horizontalSegments[y+1][x]] = spotlightColor;
}
}
grid2d.horizontalLedTTL[y][x]--;
}
}
while (1) {
byte randomVal = random8(128);
float chanceOffset = (float)(WIDTH*verticalLedChanceMultiplier)/(WIDTH*(LEDS_PER_LINE+1)+1);
if (chance*chanceOffset > randomVal) {
uint16_t randNum = random16(WIDTH*LEDS_PER_LINE);
grid2d.horizontalLedTTL[0][randNum] = LEDS_PER_LINE+1;
leds[grid2d.horizontalSegments[0][randNum]] = spotlightColor;
chance -= randomVal; //decrement counter so we could spawn more than 1 per iteration
} else break;
}
}
//Calculate spotlight falling based on horizontal TTL segments
if(spotlightPattern == 4 && (spotlightColor.r!=0 || spotlightColor.g !=0 || spotlightColor.b !=0)){
for(int y=0 ; y < HEIGHT ; y++){
for(int x=0 ; x < WIDTH ; x++){
//Get average TTL for each segment above a spotlight
float avgTTL = 0;
for(int i=0 ; i<LEDS_PER_LINE ; i++)
avgTTL += max(grid2d.horizontalLedTTL[y][x*LEDS_PER_LINE+i]-1,0);
CRGB newColor;
newColor.r = spotlightColor.r * (avgTTL/(LEDS_PER_LINE+1)/LEDS_PER_LINE);
newColor.g = spotlightColor.g * (avgTTL/(LEDS_PER_LINE+1)/LEDS_PER_LINE);
newColor.b = spotlightColor.b * (avgTTL/(LEDS_PER_LINE+1)/LEDS_PER_LINE);
#ifdef SPOTLIGHTPIN
spotlightLed[spotlightToLedIndexDedicated(y*WIDTH + x)] = newColor ;
#else
leds[spotlightToLedIndex(y*WIDTH + x)] = newColor ;
#endif
}
}
}
}
void fire(){
const int fireChance = 20;
const int minHeight = HEIGHT*LEDS_PER_LINE*0.2;
const int effectHeight = HEIGHT*LEDS_PER_LINE;
const int maxWidth = WIDTH*LEDS_PER_LINE;
/* generate random vals for fireHeight[], and create a 5-wide flame
|
| | | |
| ||| || | ||
|_|||||_|__|||__||__||||_
*/
for(int i=0 ; i<maxWidth+1 ; i++){
//If it has not been initialized or we want to randomly initialize it
if(grid2d.fireHeight[i] == 0) grid2d.fireHeight[i] = max(1,minHeight)-1+random8(3);
if(fireChance > random8()){
//Create a kind of parabolic flame effect
const int maxHeight = minHeight + random8(effectHeight-minHeight);
const int heightDifference1 = maxHeight - (effectHeight*0.1 + random8((byte)(effectHeight*0.1)));
const int heightDifference2 = heightDifference1 - (effectHeight*0.2 + random8((byte)(effectHeight*0.2)));
//Clamp x-values between 0 and WIDTH*LEDS_PER_LINE and create new Y values if greater.
grid2d.fireHeight[max(0,i-2)] = max(heightDifference2 - random8(2),grid2d.fireHeight[max(0,i-2)]);
grid2d.fireHeight[max(0,i-1)] = max(heightDifference1 - random8(2),grid2d.fireHeight[max(0,i-1)]);
grid2d.fireHeight[i] = max(maxHeight ,grid2d.fireHeight[i]);
grid2d.fireHeight[min(maxWidth,i+1)] = max(heightDifference1 - random8(2),grid2d.fireHeight[min(maxWidth,i+1)]);
grid2d.fireHeight[min(maxWidth,i+2)] = max(heightDifference2 - random8(2),grid2d.fireHeight[min(maxWidth,i+2)]);
}else{
//Calm down the flame a bit
grid2d.fireHeight[i] = max(minHeight-1+random8(3), grid2d.fireHeight[i]-(1+random8((byte)(effectHeight*0.05))));
}
}
//render on spotlights.
if(spotlightPattern == 6){
for(int x=0;x<WIDTH;x++){
//calculate average flame height
int avgHeight = 0;
for(int j=0;j<LEDS_PER_LINE+1;j++){
avgHeight = grid2d.fireHeight[x*LEDS_PER_LINE+j];
}
//set spotlight position to LEDS_PER_LINE/2 + verticalOffset
int spotlightHeight = LEDS_PER_LINE/2;
CRGB color;
for(int y=0;y<HEIGHT;y++){
//Get gradient
if(spotlightHeight-LEDS_PER_LINE/2 > avgHeight){ //if average is below that segment, dim
fadeToBlackBy(&leds[spotlightToLedIndex((HEIGHT-1-y)*WIDTH + x)], 1 , 160);
}else if(spotlightHeight > avgHeight){ //if spotlight is above average height of flames, set a point between that and black
color.r = spotlights[0].r*(1-(float)(spotlightHeight-avgHeight)/LEDS_PER_LINE);
color.g = spotlights[0].g*(1-(float)(spotlightHeight-avgHeight)/LEDS_PER_LINE);
color.b = spotlights[0].b*(1-(float)(spotlightHeight-avgHeight)/LEDS_PER_LINE);
}else{ //if average is above the spotlight position, get gradient
color.r = spotlights[0].r*((float)spotlightHeight/avgHeight) + spotlights[1].r*(1 - ((float)spotlightHeight/avgHeight));
color.g = spotlights[0].g*((float)spotlightHeight/avgHeight) + spotlights[1].g*(1 - ((float)spotlightHeight/avgHeight));
color.b = spotlights[0].b*((float)spotlightHeight/avgHeight) + spotlights[1].b*(1 - ((float)spotlightHeight/avgHeight));
}
spotlightHeight += LEDS_PER_LINE;
#ifdef SPOTLIGHTPIN
spotlightLed[spotlightToLedIndexDedicated((HEIGHT-1-y)*WIDTH + x)] = color;
#else
leds[spotlightToLedIndex((HEIGHT-1-y)*WIDTH + x)] = color;
#endif
}
}
}
//render on background
if(backgroundPattern == 6){
for(int x=0;x<maxWidth+1;x++){
//We added 1 extra LED for the right-most vertical segment, which would go out of bounds for our horizontal segments
//If on a horizontal segment
if(x!=maxWidth){
for(int h=0 ; h<=HEIGHT ; h++){
int ledNum = grid2d.horizontalSegments[HEIGHT-h][x];
if(grid2d.fireHeight[x] >= h*LEDS_PER_LINE - (h!=0)){ //the h!=0 since it goes 0,8,17
float randomColorOffset = random16(grid2d.fireHeight[x]) * h==0; //so the bottom row isnt static
float gradientOffset = min((float)1,max((float)0,((float)(h*LEDS_PER_LINE+randomColorOffset)) / ((float)(grid2d.fireHeight[x]))));
leds[ledNum] = CRGB(bg.r*(gradientOffset) + bg2.r*(1 - gradientOffset),
bg.g*(gradientOffset) + bg2.g*(1 - gradientOffset),
bg.b*(gradientOffset) + bg2.b*(1 - gradientOffset) );
}else fadeToBlackBy(&leds[ledNum], 1 , 120);
}
}
}
//Vertical segments
for(int x=0;x<WIDTH+1;x++){
//Gradient the segments
for(int y=0;y<min(HEIGHT*LEDS_PER_LINE,grid2d.fireHeight[x]);y++){
int ledNum = grid2d.verticalSegments[HEIGHT*LEDS_PER_LINE-y-1][x];
leds[ledNum] = CRGB(bg.r*((float)y/grid2d.fireHeight[x]) + bg2.r*(1 - ((float)y/grid2d.fireHeight[x])),
bg.g*((float)y/grid2d.fireHeight[x]) + bg2.g*(1 - ((float)y/grid2d.fireHeight[x])),
bg.b*((float)y/grid2d.fireHeight[x]) + bg2.b*(1 - ((float)y/grid2d.fireHeight[x])));
}
//Otherwise dim those segments
for(int y=grid2d.fireHeight[x] ; y<HEIGHT*LEDS_PER_LINE ; y++){
fadeToBlackBy(&leds[grid2d.verticalSegments[HEIGHT*LEDS_PER_LINE-y-1][x]], 1 , 120);
}
}
}
}
void loadingEffect(CRGB color){
if (loadingCursorPosition < WIDTH *LEDS_PER_LINE){ //top segments
leds[grid2d.horizontalSegments[0][loadingCursorPosition]] = color;
}else if (loadingCursorPosition < ( WIDTH+HEIGHT)*LEDS_PER_LINE){ //right segments
leds[grid2d.verticalSegments[loadingCursorPosition - WIDTH*LEDS_PER_LINE][WIDTH]] = color;
}else if (loadingCursorPosition < (2*WIDTH+HEIGHT)*LEDS_PER_LINE){ //bottom segments
leds[grid2d.horizontalSegments[HEIGHT][(2*WIDTH+HEIGHT)*LEDS_PER_LINE-loadingCursorPosition-1]] = color;
}else if (loadingCursorPosition < 2*(WIDTH+HEIGHT)*LEDS_PER_LINE){ //left segments
leds[grid2d.verticalSegments[2*(WIDTH+HEIGHT)*LEDS_PER_LINE-loadingCursorPosition-1][0]] = color;
}else{
loadingCursorPosition = -1;
}
loadingCursorPosition++;
dimSegments(30);
}
//************************************************//
// Helper Functions //
//************************************************//
//Change color offset, where top-left is 0 and bottom right is the maximum value so we can get a hue/color gradient
/* Segment color reference
--0- --1- --2- --3- --4- --5-
-0 -1 -2 -3 -4 -5 -6
--1- --2- --3- --4- --5- --6-
-1 -2 -3 -4 -5 -6 -7
--2- --3- --4- --5- --6- --7-
*/
byte segmentLightingOffset(int index) {
const int rowLength = (2 * WIDTH + 1);
const int row = index / rowLength; //every set of vertical and horizontal segments is a row
const int indexOffset = index % rowLength;
if (indexOffset < WIDTH) { //if horizontal segment
return (row + indexOffset); //since each consecutive row's lighting offset starts at the row #, just add the horizontal index position after
} else { //if vertical segment
return (row + indexOffset - WIDTH); //since each consecutive row's lighting offset starts at the row #, just add the horizontal index position after
}
}
//Map from the abstracted segment index to the actual wiring index
// based on the data set in the segmentWiringOrder array in Config.h
strip segmentToLedIndex(int index) {
for (int i = 0; i < sizeof(segmentWiringOrder) / sizeof(int); i++) {
//If at the segmentWiringOrder index, we find the index of the abstracted segment index, return the wiring position
if (abs(segmentWiringOrder[i]) - 1 == index) { //subtract 1 since we start at 1 (0 cant be given a positive or negative sign)
convert.start = i * LEDS_PER_LINE;
if (segmentWiringOrder[i] < 0) { //if negative, the LED direction is opposite of the direction the LED effects go (top right to bottom left)
convert.reverse = true;
convert.start += LEDS_PER_LINE - 1;
} else {
convert.reverse = false;
}
return (convert);
}
}
//If not found, return an invalid strip struct
convert.start = -1;
convert.reverse = false;
return (convert);
}
//Map from the abstracted spotlight index to the actual wiring index
// based on the data set in the spotlightWiringOrder array in Config.h
int spotlightToLedIndex(int index) {
for(int i=0;i<WIDTH*HEIGHT;i++){
if(spotlightWiringOrder[i] == index) return(NUM_SEGMENTS * LEDS_PER_LINE + i);
}
return -1;
}
int spotlightToLedIndexDedicated(int index) {
for(int i=0;i<WIDTH*HEIGHT;i++){
if(spotlightWiringOrder[i] == index) return(i);
}
return -1;
}
//Shift the colour closer to black. amount=0 does not change anything, 255 sets the colour to bnlack
CRGB dimColor(CRGB color, byte amount) {
CRGB newColor;
newColor.red = (byte)(color.red * (1 - amount / 255.0));
newColor.green = (byte)(color.green * (1 - amount / 255.0));
newColor.blue = (byte)(color.blue * (1 - amount / 255.0));
return newColor;
}
//Convert the integer digit to a byte containing which segments to light
// up to display that digit.
uint8_t sevenSegment(int num) {
switch (num) {
case 0: return (zero);
case 1: return (one);
case 2: return (two);
case 3: return (three);
case 4: return (four);
case 5: return (five);
case 6: return (six);
case 7: return (seven);
case 8: return (eight);
case 9: return (nine);
}
return 0;
}
//Save UTC offset data to emulated EEPROM
void storeUtcOffset(double value){
EEPROM.begin(512);
int16_t toStore = (int16_t)value*60; //store in minutes
EEPROM.write(1,toStore>>8);
EEPROM.write(2,toStore%256);
EEPROM.commit();
}
double getUtcOffset(){
EEPROM.begin(512);
int16_t ans = (int16_t)(EEPROM.read(1)<<8 | EEPROM.read(2));
Serial.println("UTC Offset: " + String(ans/60));
return (ans/60.0);
}
void resetEEPROM(){
Serial.println("RESETTING EEPROM TO 0'S. ALL EEPROM SETTINGS WILL BE WIPED");
EEPROM.begin(512);
for(int i=0;i<512;i++)
EEPROM.write(i,0);
EEPROM.commit();
Serial.println("EEPROM data:");
for(int i=0;i<512;i++){
Serial.print(String(EEPROM.read(i)) + " ");
if(i%32==31) Serial.println();
}
Serial.println();
}
//Stage all settings to be saved to EEPROM
void saveAllSettings(){
lightingChanges.power = true;
lightingChanges.foregroundTransparency = true;
lightingChanges.autobrightness = true;
lightingChanges.segmentBrightness = true;
lightingChanges.spotlightBrightness = true;
lightingChanges.backgroundBrightness = true;
lightingChanges.h_ten_color = true;
lightingChanges.h_one_color = true;
lightingChanges.m_ten_color = true;
lightingChanges.m_one_color = true;
lightingChanges.bg = true;
lightingChanges.bg2 = true;
for(int i=0;i<WIDTH*HEIGHT;i++){
lightingChanges.spotlights[i] = true;
}
lightingChanges.foregroundPattern = true;
lightingChanges.backgroundPattern = true;
lightingChanges.spotlightPattern = true;
lightingChanges.rainbowRate = true;
lightingChanges.fps = true;
lightingChanges.hyphenLength = true;
lightingChanges.hyphenColor = true;
lastUpdate = 0;
updateSettings = false;
storeEEPROM();