-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathNeoMatrix-FastLED-IR.ino
5719 lines (5114 loc) · 191 KB
/
NeoMatrix-FastLED-IR.ino
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
// By Marc MERLIN <marc_soft@merlins.org>
// License: Apache v2.0
//
// Portions of demo code from Adafruit_NeoPixel/examples/strandtest apparently
// licensed under LGPLv3 as per the top level license file in there.
// Neopixel + IR is hard because the neopixel libs disable interrups while IR is trying to
// receive, and bad things happen :(
// https://github.com/z3t0/Arduino-IRremote/issues/314
//
// leds.show takes 1 to 3ms during which IR cannot run and codes get dropped, but it's more
// a problem if you constantly update for pixel animations and in that case the IR ISR gets
// almost no chance to run.
// The FastLED library is super since it re-enables interrupts mid update
// on chips that are capable of it. This allows IR + Neopixel to work on Teensy v3.1 and most
// other 32bit CPUs.
// Compile WeMos D1 R2 & mini, ESP32-dev, or ArduinoOnPC for linux
// Force 24x32 on ESP32 for bright daylight display
// as a hack, we enable this for non PSRAM boards but
// a non PSRAM board could also run a framebuffer less
// setup to drive a rPi
//#ifdef ESP32
//#ifndef BOARD_HAS_PSRAM
//#define M32BY8X3
//#endif
//#endif
// on Rpi, this is ignored and it uses a larger size
#ifdef M32BY8X3
#define gif_size 32
#else
#define gif_size 64
#endif
#include "nfldefines.h"
#include "Table_Mark_Estes.h"
#include "PacMan.h"
#define FIREWORKS_INCLUDE
#include "FireWorks2.h"
#define SUBLIME_INCLUDE
#include "Sublime_Demos.h"
#define TWINKLEFOX_INCLUDE
#include "TwinkleFOX.h"
#include "aurora.h"
#include "Table_Mark_Estes_Impl.h"
#include "AikoEvents_Impl.h"
using namespace Aiko;
#ifdef ARDUINOONPC
#include "v4lcapture.h"
#endif
#ifdef HAS_FS
// defines FSO
#include "GifAnimViewer.h"
#endif
#ifdef WIFI
#include "wifi_secrets.h"
#include <OmEspHelpers.h>
OmWebServer s;
OmWebPages *p = NULL;
// Every time things change like the current demo, brightness,
// bestof, etc, rebuild the page show to the user.
// Unfortunately this does not force a page reload.
void rebuild_main_page(bool show_summary=false);
void rebuild_advanced_page();
// Allows a connected device via serial to feed a remote IP as a 32bit number
IPAddress Remote_IP = IPAddress(0, 0, 0, 0);
#else
// Without WIFI (like rPi/ArduinoOnPC), this is a no-op
void rebuild_main_page(bool show_summary=false) { show_summary = show_summary; }
void rebuild_advanced_page() {};
#endif
#define DEMO_PREV -32768
#define DEMO_NEXT 32767
#define DEMO_TEXT_THANKYOU 90
#define DEMO_TEXT_INPUT 91
#define DEMO_CAMERA 99
#define DEMO_TEXT_FIRST DEMO_TEXT_THANKYOU
#define DEMO_TEXT_LAST DEMO_TEXT_INPUT
#ifdef ESP8266
#define DEMO_ARRAY_SIZE 120
#else
#define DEMO_ARRAY_SIZE 480
#endif
// this is for ESP32 to know it's being controlled by rPi (ignored on rPi)
bool RpiRemote = false;
// number of lines read in demo_map.txt
uint16_t CFG_LAST_INDEX = 0;
// By default text demos rotate but they can be made to stay still for a picture
bool ROTATE_TEXT = true;
// By default DJ images get scrolled a bit to add movement
bool SCROLL_IMAGE = true;
// show all demos by default,
bool SHOW_BEST_DEMOS = false;
bool MATRIX_RESET_DEMO = true;
// Different panel configurations: 24x32, 64x64 (BM), 64x96 (BM), 64x96 (Trance), 128x192
#define CONFIGURATIONS 5
const char *panelconfnames[CONFIGURATIONS] = {
"Neopixel Shirt 24x32 ESP8266",
"Burning Man Neopixel Panel 64x64 ESP32",
"SmartMatrix Shirt BM 64x96 ESP32",
"SmartMatrix Shirt Dance 64x96 ESP32",
"RPI-RGB-Panels Shirt Dance 128x192 rPi" };
typedef struct demo_entry_ {
const char *name;
uint8_t (*func)(uint32_t);
int arg;
// This is generated for the wifi menu (if used in the generated drop down menu)
// it could remain NULL, so don't dereference it blindly.
char *menu_str;
} Demo_Entry;
// demoidx() does the mapping from demo index number to a slot in demo_list
// this it to given slots like 100 to a different demo depending on the runtime
// chosen (like ESP32 64x96 to 128x192)
// Demo_Entry demo_list[] is defined below
// Those are the lines defined in demo_map.txt config
// 400(0-399) lines for 365 in demo_list
typedef struct mapping_entry_ {
// reverse mapping to the original demo slot (for demos that are
// swapped to a different slot)
// get filled with dmap which is the the one colum before last in demo_map.txt
uint16_t mapping;
// 1: enabled, 2: bestof enabled only, 3: both
uint8_t enabled[CONFIGURATIONS];
// allow reversing a demo to its original index (each demo points to its new
// slot and that new slot as an index back with .reverse)
uint16_t reverse;
} Mapping_Entry;
// demo_mapping actually does not need to be as big as DEMO_ARRAY_SIZE
// because demo_list contains elements that share the same demo slots
// (different demos can be defined for the same slot to account for different
// platforms).
// It's safe and easy to define it a bit too large, though.
Mapping_Entry demo_mapping[DEMO_ARRAY_SIZE];
// DEMO_LAST_IDX is the number of elements in the demo array, but this includes empty slots
uint16_t DEMO_CNT; // actual number of demos available at boot (different from enabled)
uint16_t BEST_CNT;
// last demo index (starting from 1, not 0), gets computed in read_config_index
uint16_t DEMO_LAST_IDX;
// index within demo_mapping of what demo is being played
uint16_t MATRIX_STATE = 0;
uint16_t MATRIX_DEMO; // this is initialized after MATRIX_STATE is updated in read_config_index
// computed in Matrix_Handler, displayed in ShowMHfps
uint32_t LAST_FPS = 0;
bool SHOW_LAST_FPS = false;
String DISPLAYTEXT="00,00,0100,0,1,Hello\nWorld";
// Compute how many GIFs have been defined (called in setup)
uint16_t GIF_CNT = 0;
// Other fonts possible on http://oleddisplay.squix.ch/#/home
// https://blog.squix.org/2016/10/font-creator-now-creates-adafruit-gfx-fonts.html
// https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts
// Default is 5x7, meaning 4.8 chars wide, 4 chars high
// Picopixel: 3x5 means 6 chars wide, 5 or 6 chars high
// #include <Fonts/Picopixel.h>
// Proportional 5x5 font, 4.8 wide, 6 high
// #include <Fonts/Org_01.h>
// TomThumb: 3x5 means 4 chars wide, 5 or 6 chars high
// -> nicer font
#include <Fonts/TomThumb.h>
// 3x3 but not readable
//#include <Fonts/Tiny3x3a2pt7b.h>
//#include <Fonts/FreeMonoBold9pt7b.h>
//#include <Fonts/FreeMonoBold12pt7b.h>
//#include <Fonts/FreeMonoBold18pt7b.h>
//#include <Fonts/FreeMonoBold24pt7b.h>
#include "fonts.h"
// Choose your prefered pixmap
#include "smileytongue24.h"
// controls how many times a demo should run its pattern
// init at -1 to indicate that a demo is run for the first time (demo switch)
int16_t MATRIX_LOOP = -1;
uint16_t GIFLOOPSEC;
uint32_t waitmillis = 0;
//----------------------------------------------------------------------------
// This file contains codes I captured and mapped myself
// using IRremote's examples/IRrecvDemo
#include "IRcodes.h"
#ifdef IR_RECV_PIN
#ifdef ESP8266
#include <IRremoteESP8266.h>
#else
#ifdef ESP32RMTIR
#include <IRRecv.h> // https://github.com/lbernstone/IR32.git
#else
#include <IRremote.h>
#endif
#endif
#ifndef ESP32RMTIR
IRrecv irrecv(IR_RECV_PIN);
#else
IRRecv irrecv;
#endif
#endif
uint32_t last_change = millis();
typedef enum {
f_nothing = 0,
f_colorWipe = 1,
f_rainbow = 2,
f_rainbowCycle = 3,
f_theaterChase = 4,
f_theaterChaseRainbow = 5,
f_cylon = 6,
f_cylonTrail = 7,
f_doubleConverge = 8,
f_doubleConvergeRev = 9,
f_doubleConvergeTrail = 10,
f_flash = 11,
f_juggle = 12,
f_bpm = 13,
} StripDemo;
const char *StripDemoName[] = {
"nothing",
"colorWipe",
"rainbow",
"rainbowCycle",
"theaterChase",
"theaterChaseRainbow",
"cylon",
"cylonTrail",
"doubleConverge",
"doubleConvergeRev",
"doubleConvergeTrail",
"flash",
"juggle",
"bpm",
};
uint8_t StripDemoCnt = ARRAY_SIZE(StripDemoName)-1;
StripDemo STRIPDEMO = f_theaterChaseRainbow;
// Is the current demo linked to a color (false for rainbow demos)
bool colorDemo = true;
int32_t demo_color = 0x00FF00; // Green
uint8_t strip_speed = 50;
//----------------------------------------------------------------------------
// look for 'magic happens here' below
#ifdef ARDUINOONPC
#define RPISERIALINPUTSIZE 10240
char ttyusbbuf[RPISERIALINPUTSIZE];
bool esp32_connected = false;
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/stat.h>
// inet_addr
#include <netinet/in.h>
#include <arpa/inet.h>
#define Swap4Bytes(val) \
( (((val) >> 24) & 0x000000FF) | (((val) >> 8) & 0x0000FF00) | \
(((val) << 8) & 0x00FF0000) | (((val) << 24) & 0xFF000000) )
char rPI_IP[20];
char rPI_IPn[11];
char *get_rPI_IP() {
FILE *fp;
//fp = popen("hostname -I", "r");
fp = popen("cat /root/IP", "r");
fgets(rPI_IP, 19, fp);
uint32_t addr = Swap4Bytes(inet_addr(rPI_IP));
snprintf(rPI_IPn, 11, "%ud", addr);
//Serial.println(IP);
//Serial.println(addr, HEX);
return rPI_IPn;
}
// if we're not talking to anything, ttyfd will be reset to -1
// however >=0 does not mean there is anything transmitting
// but we'll assume that it is. If this ends up being untrue later
// look for 'serial watchdog' lower down.
int ttyfd = -1;
int set_interface_attribs(int ttyfd, int speed)
{
struct termios tty;
if (tcgetattr(ttyfd, &tty) < 0) {
printf("Error from tcgetattr: %s\n", strerror(errno));
return -1;
}
cfsetospeed(&tty, (speed_t)speed);
cfsetispeed(&tty, (speed_t)speed);
tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; /* 8-bit characters */
tty.c_cflag &= ~PARENB; /* no parity bit */
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
/* setup for non-canonical mode */
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tty.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
// https://blog.mbedded.ninja/programming/operating-systems/linux/linux-serial-ports-using-c-cpp/
// Non blocking
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 0;
if (tcsetattr(ttyfd, TCSANOW, &tty) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
return 0;
}
int send_serial(const char *xstr) {
int wlen;
int xlen = strlen(xstr);
wlen = write(ttyfd, xstr, xlen);
// tcdrain(ttyfd);
if (wlen != xlen) {
printf("Error from write: %d, %s\n", wlen, strerror(errno));
return -1;
}
printf("ESP<<< %s\n", xstr);
return 0;
}
void openttyUSB(const char ** devname) {
static bool firstconnection = true;
// static because the name is sent back to the caller
static const char *dev[] = { "/dev/ttyUSB0", "/dev/ttyUSB1", "/dev/ttyUSB2" };
int devidx = 0;
while (devidx<3 && (ttyfd = open((*devname = dev[devidx]), O_RDWR | O_NOCTTY | O_SYNC)) < 0 && ++devidx) {
struct stat stbuf;
// warn for permission denied but not for no such file or directory
if (!stat(*devname, &stbuf)) printf("Error opening %s: %s\n", *devname, strerror(errno));
}
/* baudrate 115200, 8 bits, no parity, 1 stop bit */
if (ttyfd >= 0) {
char s;
set_interface_attribs(ttyfd, B115200);
// empty input buffer of possible garbage
while (read(ttyfd, &s, 1)) {}
esp32_connected = true;
printf("Opened %s telling ESP32 to switch to PANELCONFNUM 4\n", *devname);
// Assume we just connected to an ESP32, which starts in its PANELCONFNUM 3 (ESP32 mode)
// and switch it to PANELCONFNUM 4 (rPI with longer menus).
delay(10);
send_serial("|");
delay(10);
send_serial("d");
delay(10);
if (firstconnection) {
// First time code starts, tell ESP32 to trigger the IP screen.
send_serial("i");
firstconnection = false;
} else {
// on USB reconnect, feed the local IP to ESP32 but don't cause ESP32 to tell us to switch to the IP screen
send_serial(get_rPI_IP());
}
}
}
#endif // ARDUINOONPC
const uint16_t PROGMEM RGB_bmp[64] = {
// 10: multicolor smiley face
0x000, 0x000, 0x00F, 0x00F, 0x00F, 0x00F, 0x000, 0x000,
0x000, 0x00F, 0x000, 0x000, 0x000, 0x000, 0x00F, 0x000,
0x00F, 0x000, 0xF00, 0x000, 0x000, 0xF00, 0x000, 0x00F,
0x00F, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x00F,
0x00F, 0x000, 0x0F0, 0x000, 0x000, 0x0F0, 0x000, 0x00F,
0x00F, 0x000, 0x000, 0x0F4, 0x0F3, 0x000, 0x000, 0x00F,
0x000, 0x00F, 0x000, 0x000, 0x000, 0x000, 0x00F, 0x000,
0x000, 0x000, 0x00F, 0x00F, 0x00F, 0x00F, 0x000, 0x000, };
void(* resetFunc) (void) = 0; // jump to 0 to cause a sofware reboot
// Convert a BGR 4/4/4 bitmap to RGB 5/6/5 used by Adafruit_GFX
void fixdrawRGBBitmap(int16_t x, int16_t y, const uint16_t *bitmap, int16_t w, int16_t h) {
// work around "a15 cannot be used in asm here" compiler bug when using an array on ESP8266
static uint16_t *RGB_bmp_fixed = (uint16_t *) mallocordie("RGB_bmp_fixed", w*h*2, false);
for (uint16_t pixel=0; pixel<w*h; pixel++) {
uint8_t r,g,b;
uint16_t color = pgm_read_word(bitmap + pixel);
b = (color & 0xF00) >> 8;
g = (color & 0x0F0) >> 4;
r = color & 0x00F;
// expand from 4/4/4 bits per color to 5/6/5
b = map(b, 0, 15, 0, 31);
g = map(g, 0, 15, 0, 63);
r = map(r, 0, 15, 0, 31);
RGB_bmp_fixed[pixel] = (r << 11) + (g << 5) + b;
}
matrix->drawRGBBitmap(x, y, RGB_bmp_fixed, w, h);
}
void ShowMHfps() {
uint8_t print_width = 2;
if (SHOW_LAST_FPS) {
matrix->setTextSize(1);
matrix->setFont(&TomThumb);
matrix->fillRect(0, 0, 4 * print_width, 7, 0);
matrix->setCursor(1, 6);
matrix->setTextColor(matrix->Color(255,255,255));
matrix->print(LAST_FPS);
}
}
void matrix_show() {
ShowMHfps();
#ifdef FASTLED_NEOMATRIX
#ifdef ESP8266
// Disable watchdog interrupt so that it does not trigger in the middle of
// updates. and break timing of pixels, causing random corruption on interval
// https://github.com/esp8266/Arduino/issues/34
// Note that with https://github.com/FastLED/FastLED/pull/596 interrupts, even
// in parallel mode, should not affect output. That said, reducing their amount
// is still good.
// Well, that sure didn't work, it actually made things worse in a demo during
// fade, so I'm turning it off again.
//ESP.wdtDisable();
#endif
#ifdef NEOPIXEL_PIN
#ifdef ESP8266
FastLED[1].showLeds(matrix_brightness);
#else
matrix->show();
//FastLED[1].showLeds(matrix_brightness);
#endif
#else
matrix->show();
#endif
#ifdef ESP8266
//ESP.wdtEnable(1000);
#endif
#else
matrix->show();
#endif
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(uint8_t WheelPos) {
uint32_t wheel=0;
// Serial.print(WheelPos);
WheelPos = 255 - WheelPos;
if (WheelPos < 85) {
wheel = (((uint32_t)(255 - WheelPos * 3)) << 16) + (WheelPos * 3);
}
if (!wheel && WheelPos < 170) {
WheelPos -= 85;
wheel = (((uint32_t)(WheelPos * 3)) << 8) + (255 - WheelPos * 3);
}
if (!wheel) {
WheelPos -= 170;
wheel = (((uint32_t)(WheelPos * 3)) << 16) + (((uint32_t)(255 - WheelPos * 3)) << 8);
}
// Serial.print(" -> ");
// Serial.println(wheel, HEX);
return (wheel);
}
// ---------------------------------------------------------------------------
// Matrix Code
// ---------------------------------------------------------------------------
void display_stats() {
static uint16_t cnt=1;
// Reset to default font
matrix->setFont();
matrix->setTextSize(1);
// not wide enough;
if (mw<16) return;
matrix->clear();
// Font is 5x7, if display is too small
// 8 can only display 1 char
// 16 can almost display 3 chars
// 24 can display 4 chars
// 32 can display 5 chars
matrix->setCursor(0, 0);
matrix->setTextColor(matrix->Color(255,0,0));
if (mw>10) matrix->print(mw/10);
matrix->setTextColor(matrix->Color(255,128,0));
matrix->print(mw % 10);
matrix->setTextColor(matrix->Color(0,255,0));
matrix->print('x');
// not wide enough to print 5 chars, go to next line
if (mw<25) {
if (mh==13) matrix->setCursor(6, 7);
else if (mh>=13) {
matrix->setCursor(mw-12, 8);
} else {
// we're not tall enough either, so we wait and display
// the 2nd value on top.
matrix_show();
matrix->clear();
matrix->setCursor(mw-11, 0);
}
}
matrix->setTextColor(matrix->Color(0,255,128));
matrix->print(mh/10);
matrix->setTextColor(matrix->Color(0,128,255));
matrix->print(mh % 10);
// enough room for a 2nd line
if ((mw>25 && mh >14) || mh>16) {
matrix->setCursor(0, mh-7);
matrix->setTextColor(matrix->Color(0,255,255));
if (mw>16) matrix->print('*');
matrix->setTextColor(matrix->Color(255,0,0));
matrix->print('R');
matrix->setTextColor(matrix->Color(0,255,0));
matrix->print('G');
matrix->setTextColor(matrix->Color(0,0,255));
matrix->print("B");
matrix->setTextColor(matrix->Color(255,255,0));
// this one could be displayed off screen, but we don't care :)
matrix->print("*");
}
if (mh >=31) {
matrix->setTextColor(matrix->Color(128,255,128));
matrix->setCursor(0, mh-21);
matrix->print(DEMO_CNT);
matrix->print(" demos");
}
if (mh >= 35) {
matrix->setTextColor(matrix->Color(255,128,128));
matrix->setCursor(0, mh-28);
matrix->print(BEST_CNT);
matrix->print(" best");
}
matrix->setTextColor(matrix->Color(255,0,255));
matrix->setCursor(0, mh-14);
matrix->print(cnt++);
if (cnt == 10000) cnt=1;
matrix_show();
}
void font_test() {
static uint16_t cnt=1;
#if 0
matrix->setFont(&FreeMonoBold9pt7b);
matrix->setCursor(-1, 31);
matrix->print("T");
matrix_show();
delay(1000);
matrix->clear();
matrix->setFont(&FreeMonoBold12pt7b);
matrix->setCursor(-1, 31);
matrix->print("T");
matrix_show();
delay(1000);
matrix->clear();
matrix->setFont(&FreeMonoBold18pt7b);
matrix->setCursor(-1, 31);
matrix->print("T");
matrix_show();
delay(1000);
matrix->clear();
matrix->setFont(&FreeMonoBold24pt7b);
matrix->setCursor(-1, 31);
matrix->print("T");
matrix_show();
matrix->clear();
delay(1000);
#endif
matrix->clear();
//matrix->setFont(&Picopixel);
//matrix->setFont(&Org_01);
matrix->setFont(&TomThumb);
//matrix->setFont(&Tiny3x3a2pt7b);
matrix->setTextSize(1);
matrix->setTextColor(matrix->Color(255,0,255));
matrix->setCursor(0, 6);
matrix->print(cnt++);
matrix->setCursor(0, 12);
matrix->setTextColor(matrix->Color(255,0,0));
matrix->print("Eat");
matrix->setCursor(0, 18);
matrix->setTextColor(matrix->Color(255,128,0));
matrix->print("Sleep");
matrix->setCursor(0, 24);
matrix->setTextColor(matrix->Color(0,255,0));
matrix->print("Rave");
matrix->setCursor(0, 30);
matrix->setTextColor(matrix->Color(0,255,128));
matrix->print("REPEAT");
}
uint8_t tfsf_zoom(uint32_t zoom_type);
uint8_t tfsf(uint32_t unused) {
static uint16_t state;
static float spd;
uint8_t l = 0;
unused = unused;
static int8_t startfade;
float spdincr = 0.6;
uint16_t duration = 100;
uint8_t resetspd = 5;
uint8_t repeat = 2;
uint8_t fontsize = 1;
uint8_t idx = 3;
// For bigger screens, use the zoom version in static fully zoomed mode.
if (mheight >= 64) { tfsf_zoom(99); return 255; }
// For smaller displays, flash letters one by one.
if (MATRIX_RESET_DEMO) {
MATRIX_RESET_DEMO = false;
matrix->clear();
state = 1;
spd = 1.0;
startfade = -1;
// biggest font is 18, but this spills over
if (mw >= 48 && mh >=48) fontsize = 2;
}
matrix->setFont( &Century_Schoolbook_L_Bold[16] );
matrix->setTextSize(fontsize);
if (startfade < l && (state > (l*duration)/spd && state < ((l+1)*duration)/spd)) {
matrix->setCursor(0, mh - idx*8*fontsize/3);
matrix->clear();
matrix->setTextColor(matrix->Color(255,0,0));
matrix->print("T");
startfade = l;
}
l++; idx--;
if (startfade < l && (state > (l*duration)/spd && state < ((l+1)*duration)/spd)) {
matrix->setCursor(0, mh - idx*8*fontsize/3);
matrix->clear();
matrix->setTextColor(matrix->Color(192,192,0));
matrix->print("F");
startfade = l;
}
l++; idx--;
if (startfade < l && (state > (l*duration)/spd && state < ((l+1)*duration)/spd)) {
matrix->setCursor(2, mh - idx*8*fontsize/3);
matrix->clear();
matrix->setTextColor(matrix->Color(0,192,192));
matrix->print("S");
startfade = l;
}
l++; idx--;
if (startfade < l && (state > (l*duration)/spd && state < ((l+1)*duration)/spd)) {
matrix->setCursor(2, mh - idx*8*fontsize/3);
matrix->clear();
matrix->setTextColor(matrix->Color(0,255,0));
matrix->print("F");
startfade = l;
}
l++; idx--;
#if 0
if (startfade < l && (state > (l*duration)/spd)) {
matrix->setCursor(1, 29);
matrix->clear();
matrix->setTextColor(matrix->Color(0,255,0));
matrix->print("8");
startfade = l;
}
#endif
if (startfade > -1) {
for (uint16_t i = 0; i < NUMMATRIX; i++) matrixleds[i].nscale8(248-spd*2);
}
l++;
if (state++ > ((l+0.5)*duration)/spd) {
state = 1;
startfade = -1;
spd += spdincr;
if (spd > resetspd) {
MATRIX_RESET_DEMO = true;
return 0;
}
}
matrix_show();
return repeat;
}
// type 0 = up, type 1 = up and down
// 99 = don't zoom, just display the biggest font a bit dimmer (for pictures)
// on small displays, zooms in each letter one by one
// on bigger displays, zooms in "TF" and "SF"
uint8_t tfsf_zoom(uint32_t zoom_type) {
static uint16_t direction;
static uint16_t size;
static uint8_t l;
static int16_t faster = 0;
static bool dont_exit;
static uint16_t delayframe = 1;
const char letters[] = { 'T', 'F', 'S', 'F' };
uint8_t speed = 30;
bool done = 0;
static uint8_t repeat = 4;
if (MATRIX_RESET_DEMO) {
MATRIX_RESET_DEMO = false;
direction = 1;
size = 3;
l = 0;
if (MATRIX_LOOP == -1) { dont_exit = 1; delayframe = 2; faster = 0; };
matrix->setTextSize(1);
repeat = 4;
if (zoom_type == 99) { size = 18; repeat = 10; };
}
if (mheight >= 192) matrix->setTextSize(2);
if (--delayframe) {
// reset how long a frame is shown before we switch to the next one
// Serial.println("delayed frame");
//delay(MX_UPD_TIME); Aiko emulates this delay for us
return repeat;
}
delayframe = max((speed / 10) - faster , 1);
// before exiting, we run the full delay to show the last frame long enough
if (dont_exit == 0) { dont_exit = 1; return 0; }
// Either show one letter at a time and change the size and color on smaller
// displays (l is incremented at the bottom to switch letters), or on a bigger
// display, skip that and show all the letters at once.
if (direction == 1) {
int8_t offset = 0; // adjust some letters left or right as needed
matrix->clear();
matrix->setFont( &Century_Schoolbook_L_Bold[size] );
if (mw >= 48 && mh >=64) {
matrix->setPassThruColor(0xD7E1EB);
// Something less bright for pictures
if (zoom_type == 99) matrix->setPassThruColor(0x77818B);
matrix->setCursor(20-size+offset, (mh>=128?64:20)+size*1.5);
matrix->print("TF");
matrix->setPassThruColor(0x05C1FF);
// Something less bright for pictures
if (zoom_type == 99) matrix->setPassThruColor(0x00618F);
matrix->setCursor((mh>=128?50:24)-size+offset, (mh>=128?128:52)+size*1.5);
matrix->print("SF");
} else {
if (letters[l] == 'T') offset = -2 * size/15;
if (letters[l] == '8') offset = 2 * size/15;
matrix->setPassThruColor(Wheel(map(letters[l], '0', 'Z', 255, 0)));
#ifdef M32BY8X3
matrix->setCursor(10-size*0.55+offset, 17+size*0.75);
#else
matrix->setCursor(3*mw/6-size*1.75+offset, mh*7/12+size*1.60);
#endif
matrix->print(letters[l]);
}
matrix->setPassThruColor();
if (zoom_type != 99) {
if (size<18) size++;
else if (zoom_type == 0) { done = 1; delayframe = max((speed - faster*10) * 1, 3); }
else direction = 2;
} else {
repeat--;
}
} else if (zoom_type == 1) {
int8_t offset = 0; // adjust some letters left or right as needed
matrix->clear();
matrix->setPassThruColor(Wheel(map(letters[l], '0', 'Z', 64, 192)));
matrix->setFont( &Century_Schoolbook_L_Bold[size] );
if (mw >= 48 && mh >=64) {
matrix->setPassThruColor(0xD7E1EB);
// Something less bright for pictures
if (zoom_type == 99) matrix->setPassThruColor(0x77818B);
matrix->setCursor(20-size+offset, (mh>=128?64:20)+size*1.5);
matrix->print("TF");
matrix->setPassThruColor(0x05C1FF);
// Something less bright for pictures
if (zoom_type == 99) matrix->setPassThruColor(0x00618F);
matrix->setCursor((mh>=128?50:24)-size+offset, (mh>=128?128:50)+size*1.5);
matrix->print("SF");
} else {
if (letters[l] == 'T') offset = -2 * size/15;
if (letters[l] == '8') offset = 2 * size/15;
#ifdef M32BY8X3
matrix->setCursor(10-size*0.55+offset, 17+size*0.75);
#else
matrix->setCursor(3*mw/6-size*1.75+offset, mh*7/12+size*1.60);
#endif
matrix->print(letters[l]);
}
matrix->setPassThruColor();
if (size>3) size--; else { done = 1; direction = 1; delayframe = max((speed-faster*10)/2, 3); };
}
matrix_show();
//Serial.println("done?");
if (! done) return repeat;
direction = 1;
size = 3;
//Serial.println("more letters?");
if (++l < sizeof(letters)) return repeat;
l = 0;
//Serial.println("reverse pattern?");
if (zoom_type == 1 && direction == 2) return repeat;
//Serial.println("Done with font animation");
faster++;
MATRIX_RESET_DEMO = true;
dont_exit = 0;
// Serial.print("delayframe on last letter ");
// Serial.println(delayframe);
// After showing the last letter, pause longer
// unless it's a zoom in zoom out.
if (zoom_type == 0) delayframe *= 5; else delayframe *= 3;
return repeat;
}
uint8_t rotate_text(uint32_t whichone=0) {
static uint16_t state;
static float spd;
static bool didclear;
static bool firstpass;
float spdincr = 1.2;
uint16_t duration = 100;
uint16_t overlap = 50;
uint8_t displayall = 14;
uint8_t resetspd = 24;
uint8_t numlines[] = {
4, // 0
4,
4,
4,
5,
6, // 5
6,
3,
5,
5,
5, // 10
3,
4,
3,
5,
3,
4, // 15
4,
};
// TODO later
// https://choonwear.com/products/trance-because-1999-unisex-tee
// Trance <- blue, Because <- white, i like to , party like , it's 1999
const char *text[][6] = {
{ "EAT", "SLEEP", "RAVE", "REPEAT", "", "" }, // 0
{ "EAT", "SLEEP", "TRANCE", "REPEAT", "", "" },
{ "Trance.","Because", "I'm socially", "awkward.", "", "" },
{ "Trance.","Because", "I'm a Music", "snob.", "", "" },
{ "Trance", "Because", "Rich said", "EDM is", "dead.", "" },
{ "Trance", "Because", "I'm not", "hipster", "enough for", "Techno." },// 5
{ "Trance", "Because", "I don't love", "my sister","enough for", "Country." },
{ "Trance", "Because", "Of Course", "", "", "" },
{ "Trance", "Because", "JESUS", "has it", "on vinyl", "" },
{ "Trance", "Because", "It's what", "JESUS", "would do!", "" },
{ "She said","TRANCE", "or ME", "Sometimes","I miss her", "" }, // 10
{ "IT'S", "TIME", "TO PLAY", "", "", "" },
{ "TRANCE", "IS MUSIC", "WITH A", "SOUL", "", "" },
{ "I'M A", "MUSIC", "LOVER", "", "", "" },
{ "Trance", "Because", "You Don't", "Need a", "Microphone", "" },
{ "I'm", "Fucking", "Famous", "", "", "" }, // 15
{ "Fuck", "Me", "I'm", "Famous", "", "" },
{ "Say", "Perhaps", "To", "Drugs", "", "" },
};
if (PANELCONFNUM == 2 || PANELCONFNUM == 3) text[0][2] = "BURN";
uint32_t color[][6] = {
{ 0xFF0000, 0xB0B000, 0x00FF00, 0x00B0B0, 0, 0 }, // 0
{ 0xFF0000, 0xB0B000, 0x00FF00, 0x00B0B0, 0, 0 },
{ 0xFFA500, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0, 0 },
{ 0xFFC0CB, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0, 0 },
{ 0x00FF00, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0 },
{ 0xFFFF00, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF }, // 5
{ 0x0000FF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF },
{ 0xFF0000, 0xFFFFFF, 0xFFFFFF, 0, 0, 0 },
{ 0xFFFFFF, 0xFF0000, 0xFFFFFF, 0xFF0000, 0xFF0000, 0 },
{ 0xFFFF00, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0 },
{ 0xFFFFFF, 0xFFFF00, 0x00FF00, 0xFFFFFF, 0x00FFFF, 0 }, // 10
{ 0x00FF00, 0x00FF00, 0x00FF00, 0, 0, 0 },
{ 0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00, 0, 0 },
{ 0xFFFF00, 0xFFFF00, 0xFFFF00, 0, 0, 0 },
{ 0xFF0000, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0 },
{ 0xFFC0CB, 0xFFC0CB, 0xFFC0CB, 0, 0, 0 }, // 15
{ 0xFFC0CB, 0xFFC0CB, 0xFFC0CB, 0xFFC0CB, 0, 0 },
{ 0xFFC0CB, 0xFFC0CB, 0xFFC0CB, 0xFFC0CB, 0, 0 },
};
uint16_t y_offset_192[][6] = {
{ 0, 0, 0, 0, 0, 0 },
{ 90, 0, 0, 0, 0, 0 },
{ 70, 140, 0, 0, 0, 0 },
{ 65, 105, 145, 0, 0, 0 },
{ 48, 84, 120, 160, 0, 0 }, // 4
{ 25, 65, 105, 145, 185, 0 },
{ 30, 60, 90, 120, 150, 180 },
};
uint16_t y_offset_96[][6] = {
{ 0, 0, 0, 0, 0, 0 },
{ 48, 0, 0, 0, 0, 0 },
{ 30, 60, 0, 0, 0, 0 },
{ 32, 48, 64, 0, 0, 0 },
{ 20, 41, 60, 82, 0, 0 }, // 4
{ 16, 32, 48, 64, 82, 0 },
{ 10, 26, 42, 58, 76, 95 },
};
uint16_t y_offset_64[][6] = {
{ 0, 0, 0, 0, 0, 0 },
{ 16, 0, 0, 0, 0, 0 },
{ 10, 20, 0, 0, 0, 0 },
{ 20, 41, 62, 0, 0, 0 },
{ 15, 33, 47, 63, 0, 0 }, // 4
{ 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0 },
};
uint16_t y_offset_32[][6] = {
{ 0, 0, 0, 0, 0, 0 },
{ 16, 0, 0, 0, 0, 0 },
{ 15, 25, 0, 0, 0, 0 },
{ 10, 20, 30, 0, 0, 0 },
{ 6, 14, 22, 30, 0, 0 }, // 4
{ 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0 },
};