RSeries astromech firmware
TeecesLogics.h
Go to the documentation of this file.
1 
2 #ifndef TeecesLogics_h
3 #define TeecesLogics_h
4 
5 #include "ReelTwo.h"
6 #include "core/SetupEvent.h"
7 #include "core/CommandEvent.h"
8 #include "core/AnimatedEvent.h"
10 
27 {
28 public:
29  TeecesRearLogics(LedControl& ledControl) :
30  fLC(ledControl),
31  fID(ledControl.addDevice(3)),
32  fDisplayEffect(kNormal),
33  fPreviousEffect(~fDisplayEffect),
34  fFlipFlop(false),
35  fFlipFlopLast(false),
36  fStatusDelay(1000),
37  fStatusMillis(0),
38  fEffectSeqCount(0),
39  fEffectSeqDir(0),
40  fPrevEffectSeqCount(0),
41  fEffectLengthMillis(0),
42  fEffectStartMillis(0),
43  fDisplayEffectVal(0)
44  {
45  }
46 
47  int numRows() const
48  {
49  return SizeOfArray(LEDgrid);
50  }
51 
52  int numColumns() const
53  {
54  return 27;
55  }
56 
57  virtual void setup() override
58  {
59  fLC.setPower(fID, true, NUMDEVICES);
60  fLC.setIntensity(fID, 5, NUMDEVICES);
61 
62  selectEffect(10000);
63  fStatusMillis = millis();
64  }
65 
66  void selectEffect(long inputNum)
67  {
68  fDisplayEffectVal = inputNum;
69  }
70 
71  virtual void handleCommand(const char* cmd) override
72  {
73  if (*cmd++ == 'R' && *cmd++ == 'L')
74  {
75  long int cmdvalue = 0;
76  const char* c = cmd;
77  while (*c >= '0' && *c <= '9')
78  {
79  cmdvalue = cmdvalue * 10 + (*c++ - '0');
80  }
81  selectEffect(cmdvalue);
82  }
83  }
84 
85  virtual void animate() override
86  {
87  bool timerExpired = false;
88  unsigned long currentMillis = millis();
89  if (currentMillis - fStatusMillis >= fStatusDelay)
90  {
91  timerExpired = true;
92  fStatusMillis = currentMillis;
93  fFlipFlop = !fFlipFlop;
94  fEffectSeqCount += fEffectSeqDir;
95  randomSeed(analogRead(A0));
96  }
97 
98  int selectSequence = (fDisplayEffectVal % 1000000) / 10000;
99  // 100ms - 9s
100  int selectSpeed = (fDisplayEffectVal % 10000) / 100;
101  int selectLength = (fDisplayEffectVal % 100);
102 
103  switch (selectSequence)
104  {
105  case kNormal:
106  case kSolid:
107  case kToggle:
108  case kFlash:
109  case kAlert:
110  case kHorizontalScan:
111  case kVerticalScan:
112  case kSequence:
113  case kLife:
114  fDisplayEffect = selectSequence;
115  break;
116  default:
117  fDisplayEffect = kNormal;
118  break;
119  }
120  if (fPreviousEffect != fDisplayEffect)
121  {
122  timerExpired = true;
123  fEffectSeqDir = +1;
124  fEffectSeqCount = 0;
125  fPrevEffectSeqCount = 0;
126  fStatusDelay = (selectSpeed) ? 100 * (selectSpeed) : 75;
127  fEffectStartMillis = currentMillis;
128  fEffectLengthMillis = selectLength * 1000;
129  fLC.clearDisplay(fID, NUMDEVICES);
130  }
131  unsigned int effectMillis = currentMillis - fEffectStartMillis;
132  if (timerExpired)
133  {
134  clear();
135  switch (fDisplayEffect)
136  {
137  case kNormal:
138  /* Do nothing */
139  break;
140  case kSolid:
141  {
142  for (int dev = 0; dev < 3; dev++)
143  {
144  for (int row = 0; row < 6; row++)
145  fLC.setRowNoCache(fID+dev, row, randomRow(2));
146  }
147  break;
148  }
149  // case kToggle:
150  // {
151  // int i;
152  // for (i = 0; i < 4; i++)
153  // setRow(i, (!fFlipFlop) ? B11111111 : B00000000);
154  // for (; i < 8; i++)
155  // setRow(i, (!fFlipFlop) ? B00000000 : B11111111);
156  // break;
157  // }
158  // case kFlash:
159  // {
160  // if (!fFlipFlop)
161  // {
162  // for (int i = 0; i < 8; i++)
163  // setRow(i, B11111111);
164  // }
165  // else
166  // {
167  // for (int i = 0; i < 8; i++)
168  // setRow(i, B00000000);
169  // }
170  // break;
171  // }
172  // case kHorizontalScan:
173  // {
174  // if (fPrevEffectSeqCount != fEffectSeqCount)
175  // setCol(fPrevEffectSeqCount, 0);
176  // if (fEffectSeqCount > 7)
177  // {
178  // fEffectSeqDir = -1;
179  // fEffectSeqCount += fEffectSeqDir;
180  // }
181  // else if (fEffectSeqCount < 0)
182  // {
183  // fEffectSeqDir = +1;
184  // fEffectSeqCount += fEffectSeqDir;
185  // }
186  // setCol(fEffectSeqCount, 1);
187  // break;
188  // }
189  // case kVerticalScan:
190  // {
191  // if (fPrevEffectSeqCount != fEffectSeqCount)
192  // setRow(fPrevEffectSeqCount, B00000000);
193  // if (fEffectSeqCount > 7)
194  // {
195  // fEffectSeqDir = -1;
196  // fEffectSeqCount += fEffectSeqDir;
197  // }
198  // else if (fEffectSeqCount < 0)
199  // {
200  // fEffectSeqDir = +1;
201  // fEffectSeqCount += fEffectSeqDir;
202  // }
203  // setRow(fEffectSeqCount, B11111111);
204  // break;
205  // }
206  // case kSequence:
207  // {
208  // int frameCount;
209  // const byte* ptr = getFrame_Q(fEffectSeqCount, frameCount);
210  // for (int i = 0; i < 8; i++, ptr++)
211  // {
212  // setRow(i, pgm_read_byte(ptr));
213  // }
214  // break;
215  // }
216  // case kLife:
217  // {
218  // if (fPreviousEffect != fDisplayEffect)
219  // {
220  // for (int i = 0; i < 8; i++)
221  // setRow(i, random(255));
222  // }
223  // play();
224  // bool empty = true;
225  // for (int y = 0; y < 8; y++)
226  // {
227  // if (getRow(y) != 0)
228  // {
229  // empty = false;
230  // break;
231  // }
232  // }
233  // if (empty)
234  // {
235  // selectEffect(30000 + 100 + 2);
236  // }
237  // else
238  // {
239  // setPixel(random(8), random(8), 1);
240  // }
241  // break;
242  // }
243  }
244  fPrevEffectSeqCount = fEffectSeqCount;
245  }
246  if (fEffectLengthMillis > 0 && fEffectLengthMillis < effectMillis)
247  {
248  selectEffect(kNormalVal); //go back to normal operation if its time
249  }
250  fPreviousEffect = fDisplayEffect;
251  // show();
252  }
253 
254  enum
255  {
257  };
258 
259  enum
260  {
261  kNormal = 0,
262  kSolid = 1,
263  kToggle = 2,
264  kFlash = 3,
265  kAlert = 4,
269  kLife = 8
270  };
271 
272 private:
273  static const byte NUMDEVICES = 3;
274  LedControl& fLC;
275  byte fID;
276  unsigned long fDisplayEffect;
277  unsigned long fPreviousEffect;
278  bool fFlipFlop;
279  bool fFlipFlopLast;
280  unsigned long fStatusDelay;
281  unsigned long fStatusMillis;
282  int fEffectSeqCount;
283  int fEffectSeqDir;
284  int fPrevEffectSeqCount;
285  unsigned int fEffectLengthMillis;
286  unsigned long fEffectStartMillis;
287  unsigned long fDisplayEffectVal;
288  unsigned long LEDgrid[5];
289 
290  void setRow(byte row, uint32_t bits)
291  {
292  if (row < SizeOfArray(LEDgrid))
293  LEDgrid[row] = bits;
294  }
295 
296  void clear()
297  {
298  for (byte row = 0; row < SizeOfArray(LEDgrid); row++)
299  {
300  LEDgrid[row] = 0L;
301  }
302  }
303 
304  void setCol(byte col, uint8_t data)
305  {
306  for (byte row = 0; row < SizeOfArray(LEDgrid); row++)
307  {
308  // test if LED is on
309  byte LEDon = (data & 1<<row);
310  if (LEDon)
311  LEDgrid[4-row] |= (1L << col); // set column bit
312  else
313  LEDgrid[4-row] &= ~(1L << col); // reset column bit
314  }
315  }
316 
317  static uint8_t rev(uint8_t n)
318  {
319  // byte reversal fast RAM lookup table
320  static uint8_t revlookup[] = {
321  0x0, 0x8, 0x4, 0xC,
322  0x2, 0xA, 0x6, 0xE,
323  0x1, 0x9, 0x5, 0xD,
324  0x3, 0xB, 0x7, 0xF
325  };
326  return (revlookup[n & 0x0F] << 4) | revlookup[n >> 4];
327  }
328 
329  byte randomRow(int loop)
330  {
331  loop += random(2);
332  byte val = random(256);
333  while (loop-- > 0)
334  val |= random(256);
335  return (random(2) == 1) ? rev(val) : val;
336  }
337 
338  void show()
339  {
340  // Every 9th column of the displays maps to the 6th row of the Maxim chip
341  unsigned char col8 = 0; // 9th column of FLDs and RLD, maps to 6th row of device 0
342  unsigned char col17 = 0; // 18th column of RLD, goes to 6th row of RLD device 1
343  unsigned char col26 = 0; // 27th column of RLD, goes to 6th row of RLD device 2
344 
345  // Colums 0-7 map with a byte reversal
346  for (byte row = 0; row < SizeOfArray(LEDgrid); row++)
347  {
348  for (int dev = 0; dev < 3; dev++) // RLD has 3 Maxim chip devices
349  {
350  // extract byte at column 0, 9 and 18, reverse byte order, and send to device
351  fLC.setRowNoCache(fID+dev, row, rev((LEDgrid[row] & 255L << (9 * dev)) >> (9 * dev)));
352  }
353  // If the LED at column 8, 17 or 26 is on, add it to the extra row (starting "left" at MSB)
354  if ((LEDgrid[row] & 1L<<8) == 1L<<8) col8 += 128 >> row;
355  if ((LEDgrid[row] & 1L<<17) == 1L<<17) col17 += 128 >> row;
356  if ((LEDgrid[row] & 1L<<26) == 1L<<26) col26 += 128 >> row;
357  }
358  // send the extra columns as a 6th row or the Maxim (logical row 5)
359  fLC.setRowNoCache(fID+0, 5, col8);
360  fLC.setRowNoCache(fID+1, 5, col17);
361  fLC.setRowNoCache(fID+2, 5, col26);
362  }
363 };
364 
383 {
384 public:
385  TeecesFrontLogics(LedControl& ledControl) :
386  fLC(ledControl),
387  fID(ledControl.addDevice()),
388  fDisplayEffect(kNormal),
389  fPreviousEffect(~fDisplayEffect),
390  fFlipFlop(false),
391  fFlipFlopLast(false),
392  fStatusDelay(1000),
393  fStatusMillis(0),
394  fEffectSeqCount(0),
395  fEffectSeqDir(0),
396  fPrevEffectSeqCount(0),
397  fEffectLengthMillis(0),
398  fEffectStartMillis(0),
399  fDisplayEffectVal(0)
400  {
401  }
402 
403  int numRows() const
404  {
405  return SizeOfArray(LEDgrid);
406  }
407 
408  int numColumns() const
409  {
410  return 27;
411  }
412 
413  virtual void setup() override
414  {
415  fLC.setPower(fID, true);
416  fLC.setIntensity(fID, 5);
417 
418  selectEffect(10000);
419  fStatusMillis = millis();
420  }
421 
422  void selectEffect(long inputNum)
423  {
424  fDisplayEffectVal = inputNum;
425  }
426 
433  virtual void handleCommand(const char* cmd) override
434  {
435  if ((cmd[0] == 'F' && cmd[1] == 'L') ||
436  (cmd[0] == 'T' && cmd[1] == 'L') ||
437  (cmd[0] == 'B' && cmd[1] == 'L'))
438  {
439  long int cmdvalue = 0;
440  const char* c = &cmd[2];
441  while (*c >= '0' && *c <= '9')
442  {
443  cmdvalue = cmdvalue * 10 + (*c++ - '0');
444  }
445  selectEffect(cmdvalue);
446  }
447  }
448 
449  virtual void animate() override
450  {
451  bool timerExpired = false;
452  unsigned long currentMillis = millis();
453  if (currentMillis - fStatusMillis >= fStatusDelay)
454  {
455  timerExpired = true;
456  fStatusMillis = currentMillis;
457  fFlipFlop = !fFlipFlop;
458  fEffectSeqCount += fEffectSeqDir;
459  randomSeed(analogRead(A0));
460  }
461 
462  int selectSequence = (fDisplayEffectVal % 1000000) / 10000;
463  // 100ms - 9s
464  int selectSpeed = (fDisplayEffectVal % 10000) / 100;
465  int selectLength = (fDisplayEffectVal % 100);
466 
467  switch (selectSequence)
468  {
469  case kNormal:
470  case kSolid:
471  case kToggle:
472  case kFlash:
473  case kAlert:
474  case kHorizontalScan:
475  case kVerticalScan:
476  case kSequence:
477  case kLife:
478  fDisplayEffect = selectSequence;
479  break;
480  default:
481  fDisplayEffect = kNormal;
482  break;
483  }
484  if (fPreviousEffect != fDisplayEffect)
485  {
486  timerExpired = true;
487  fEffectSeqDir = +1;
488  fEffectSeqCount = 0;
489  fPrevEffectSeqCount = 0;
490  fStatusDelay = (selectSpeed) ? 100 * (selectSpeed) : 75;
491  fEffectStartMillis = currentMillis;
492  fEffectLengthMillis = selectLength * 1000;
493  fLC.clearDisplay(fID);
494  }
495  unsigned int effectMillis = currentMillis - fEffectStartMillis;
496  if (timerExpired)
497  {
498  clear();
499  switch (fDisplayEffect)
500  {
501  case kNormal:
502  /* Do nothing */
503  break;
504  case kSolid:
505  {
506  for (int dev = 0; dev < 3; dev++)
507  {
508  for (int row = 0; row < 6; row++)
509  fLC.setRowNoCache(fID+dev, row, randomRow(2));
510  }
511  break;
512  }
513  // case kToggle:
514  // {
515  // int i;
516  // for (i = 0; i < 4; i++)
517  // setRow(i, (!fFlipFlop) ? B11111111 : B00000000);
518  // for (; i < 8; i++)
519  // setRow(i, (!fFlipFlop) ? B00000000 : B11111111);
520  // break;
521  // }
522  // case kFlash:
523  // {
524  // if (!fFlipFlop)
525  // {
526  // for (int i = 0; i < 8; i++)
527  // setRow(i, B11111111);
528  // }
529  // else
530  // {
531  // for (int i = 0; i < 8; i++)
532  // setRow(i, B00000000);
533  // }
534  // break;
535  // }
536  // case kHorizontalScan:
537  // {
538  // if (fPrevEffectSeqCount != fEffectSeqCount)
539  // setCol(fPrevEffectSeqCount, 0);
540  // if (fEffectSeqCount > 7)
541  // {
542  // fEffectSeqDir = -1;
543  // fEffectSeqCount += fEffectSeqDir;
544  // }
545  // else if (fEffectSeqCount < 0)
546  // {
547  // fEffectSeqDir = +1;
548  // fEffectSeqCount += fEffectSeqDir;
549  // }
550  // setCol(fEffectSeqCount, 1);
551  // break;
552  // }
553  // case kVerticalScan:
554  // {
555  // if (fPrevEffectSeqCount != fEffectSeqCount)
556  // setRow(fPrevEffectSeqCount, B00000000);
557  // if (fEffectSeqCount > 7)
558  // {
559  // fEffectSeqDir = -1;
560  // fEffectSeqCount += fEffectSeqDir;
561  // }
562  // else if (fEffectSeqCount < 0)
563  // {
564  // fEffectSeqDir = +1;
565  // fEffectSeqCount += fEffectSeqDir;
566  // }
567  // setRow(fEffectSeqCount, B11111111);
568  // break;
569  // }
570  // case kSequence:
571  // {
572  // int frameCount;
573  // const byte* ptr = getFrame_Q(fEffectSeqCount, frameCount);
574  // for (int i = 0; i < 8; i++, ptr++)
575  // {
576  // setRow(i, pgm_read_byte(ptr));
577  // }
578  // break;
579  // }
580  // case kLife:
581  // {
582  // if (fPreviousEffect != fDisplayEffect)
583  // {
584  // for (int i = 0; i < 8; i++)
585  // setRow(i, random(255));
586  // }
587  // play();
588  // bool empty = true;
589  // for (int y = 0; y < 8; y++)
590  // {
591  // if (getRow(y) != 0)
592  // {
593  // empty = false;
594  // break;
595  // }
596  // }
597  // if (empty)
598  // {
599  // selectEffect(30000 + 100 + 2);
600  // }
601  // else
602  // {
603  // setPixel(random(8), random(8), 1);
604  // }
605  // break;
606  // }
607  }
608  fPrevEffectSeqCount = fEffectSeqCount;
609  }
610  if (fEffectLengthMillis > 0 && fEffectLengthMillis < effectMillis)
611  {
612  selectEffect(kNormalVal); //go back to normal operation if its time
613  }
614  fPreviousEffect = fDisplayEffect;
615  // show();
616  }
617 
618  enum
619  {
621  };
622 
623  enum
624  {
625  kNormal = 0,
626  kSolid = 1,
627  kToggle = 2,
628  kFlash = 3,
629  kAlert = 4,
633  kLife = 8
634  };
635 
636 private:
637  LedControl& fLC;
638  byte fID;
639  unsigned long fDisplayEffect;
640  unsigned long fPreviousEffect;
641  bool fFlipFlop;
642  bool fFlipFlopLast;
643  unsigned long fStatusDelay;
644  unsigned long fStatusMillis;
645  int fEffectSeqCount;
646  int fEffectSeqDir;
647  int fPrevEffectSeqCount;
648  unsigned int fEffectLengthMillis;
649  unsigned long fEffectStartMillis;
650  unsigned long fDisplayEffectVal;
651  unsigned long LEDgrid[5];
652 
653  void setRow(byte row, uint32_t bits)
654  {
655  if (row < SizeOfArray(LEDgrid))
656  LEDgrid[row] = bits;
657  }
658 
659  void clear()
660  {
661  for (byte row = 0; row < SizeOfArray(LEDgrid); row++)
662  {
663  LEDgrid[row] = 0L;
664  }
665  }
666 
667  void setCol(byte col, uint8_t data)
668  {
669  for (byte row = 0; row < SizeOfArray(LEDgrid); row++)
670  {
671  // test if LED is on
672  byte LEDon = (data & 1<<row);
673  if (LEDon)
674  LEDgrid[4-row] |= (1L << col); // set column bit
675  else
676  LEDgrid[4-row] &= ~(1L << col); // reset column bit
677  }
678  }
679 
680  static uint8_t rev(uint8_t n)
681  {
682  // byte reversal fast RAM lookup table
683  static uint8_t revlookup[] = {
684  0x0, 0x8, 0x4, 0xC,
685  0x2, 0xA, 0x6, 0xE,
686  0x1, 0x9, 0x5, 0xD,
687  0x3, 0xB, 0x7, 0xF
688  };
689  return (revlookup[n & 0x0F] << 4) | revlookup[n >> 4];
690  }
691 
692  byte randomRow(int loop)
693  {
694  loop += random(2);
695  byte val = random(256);
696  while (loop-- > 0)
697  val |= random(256);
698  return (random(2) == 1) ? rev(val) : val;
699  }
700 
701  void show()
702  {
703  // Every 9th column of the displays maps to the 6th row of the Maxim chip
704  unsigned char col8 = 0; // 9th column of FLDs and RLD, maps to 6th row of device 0
705  unsigned char col17 = 0; // 18th column of RLD, goes to 6th row of RLD device 1
706  unsigned char col26 = 0; // 27th column of RLD, goes to 6th row of RLD device 2
707 
708  // Colums 0-7 map with a byte reversal
709  for (byte row = 0; row < SizeOfArray(LEDgrid); row++)
710  {
711  for (int dev = 0; dev < 3; dev++) // RLD has 3 Maxim chip devices
712  {
713  // extract byte at column 0, 9 and 18, reverse byte order, and send to device
714  fLC.setRowNoCache(fID+dev, row, rev((LEDgrid[row] & 255L << (9 * dev)) >> (9 * dev)));
715  }
716  // If the LED at column 8, 17 or 26 is on, add it to the extra row (starting "left" at MSB)
717  if ((LEDgrid[row] & 1L<<8) == 1L<<8) col8 += 128 >> row;
718  if ((LEDgrid[row] & 1L<<17) == 1L<<17) col17 += 128 >> row;
719  if ((LEDgrid[row] & 1L<<26) == 1L<<26) col26 += 128 >> row;
720  }
721  // send the extra columns as a 6th row or the Maxim (logical row 5)
722  fLC.setRowNoCache(fID+0, 5, col8);
723  fLC.setRowNoCache(fID+1, 5, col17);
724  fLC.setRowNoCache(fID+2, 5, col26);
725  }
726 };
727 
752 
753 #endif
754 
755 #if 0
756 
757 #include <avr/pgmspace.h>
758 #include <TeecesControl.h>
759 
760 // =======================================================================================
761 // CuriousMarc Teeces Code
762 // v1.3
763 // Corrected numbers 4 to 9 in Latin Alphabet. Have not done the Aurabesh yet.
764 // v1.2
765 // - Updated to "const int PROGMEM" (added const) definitions to satisfy new compiler error
766 // - Changed timing measures to unsigned long to correct compiler warning
767 // - Changed setText(char*, char*) to (const char*, const char*) to satisfy compiler deprecation warning
768 // v1.1
769 // 11/28/2013
770 // Teeces code that runs with R2 Touch and MarcDuino's, as replacement for the JEDI
771 // Works also better as regular Teeces code:
772 // - more realistic random RLD animation, adjustable
773 // - independent text on all logics
774 // - bottom FLD text flipped right side up
775 // - two alphabets: English and Aurabesh
776 // - the two PSI are completely independent (they used to switch at the same time)
777 // - Randomly "stuck" PSI effect
778 // - flash, leia, march, star wars and march effects
779 // - implements extended JawaLite command set for external control by an Arduino
780 // - uses optimized TeecesControl library (instead of LedControl) to save on memory
781 // =======================================================================================
782 //
783 // Some of this code inspired from the following sources
784 // John Vannoy, DanF, Paul Murphy, BigHappyDude
785 //
786 // Required Libraries:
787 // TeecesControl
788 //
789 // Logic Display and PSI Boards should be wired up in two chains.
790 // Teeces board V3.1 has two headers to facilitate this.
791 // OUT1 should be connected to the Rear PSI (which adds it after the RLD to the "rear" chain)
792 // OUT2 should be connected to the Top FLD, bottom FLD, then Front PDI (this is the "front" chain)
793 //
794 // If you're using the older V3 RLD you don't have the OUT2 pins, connect it like this:
795 // RLD OUT should be connected to the Rear PSI (which adds it after the RLD to the front chain)
796 // Use Arduino Pins 9,8,7 to connect to top FLD IN D,C,L, then Bottom FLD then Front PSI
797 // (you will also need to supply +5V and GND to the front chain; it can go to any pins
798 // labeled +5V and GND on any of the FLD or front PSI boards)
799 //
800 // BOARDtype sets which Arduino we're using
801 // 1 = Arduino Pro Mini or Uno or Duemilanove ( http://arduino.cc/en/Main/ArduinoBoardProMini )
802 // 2 = Sparkfun Pro Micro ( https://www.sparkfun.com/products/11098 )
803 // 3 = Arduino Micro ( http://arduino.cc/en/Main/ArduinoBoardMicro )
804 //
805 // This program is free software: you can redistribute it and/or modify it .
806 // This program is distributed in the hope that it will be useful,
807 // but WITHOUT ANY WARRANTY; without even the implied warranty of
808 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
809 
810 
811 // Type of Arduino you are using
812 // 1 = Arduino Pro Mini or Uno or Duemilanove (the cleanest option, but you need an FTDI adapter)
813 // 2 = Sparkfun Pro Micro (but be ready to deal with USB and upload issues)
814 // 3 = Arduino Micro (but be ready to deal with a baord that's too big)
815 #define BOARDtype 1
816 
817 // PSItype sets the type of our front and rear PSI's
818 // 1 = Teeces original (6 LEDs of each color, arranged side by side)
819 // 2 = Teeces original checkerboard (6 LEDs of each color arranged in a checkerboard pattern)
820 // 3 = Teeces V3.2 PSI by John V (13 LEDs of each color, arranged side by side)
821 // 4 = Teeces V3.2 PSI checkerboard by John V (13 LEDs of each color, in a checkerboard pattern)
822 #define PSItype 4
823 
824 // Baud Rate sets the baud rate of the serial connection. Current MarcDuino HP Firmware (v1.5)
825 // uses the JEDI default rate which is very slow at 2400.
826 // If you control it from something else, you probably want to use 9600.
827 // The serial input connects to the pin marked RXI or Rx on the Arduino (and optionally TXO or Tx for answer messages)
828 #define BAUDRATE 2400
829 
830 // Startup Text
831 char TFLDtext[] = "R2-D2 "; //PUT YOUR TOP FRONT STARTUP TEXT HERE.
832 char BFLDtext[] = " ASTROMECH"; //PUT YOUR BOTTOM FRONT STARTUP TEXT HERE.
833 char RLDtext[] = "CURIOUSMARC SKETCH V1.2 "; //PUT YOUR REAR STARTUP TEXT HERE.
834 
835 //#define TESTLOGICS //uncomment to turns on all logic LEDs at once, useful for troubleshooting
836 
837 // Most builders shouldn't have to edit anything below here.
838 // =======================================================================================
839 
840 /************
842 // Command language reference - JEDI JawaLite emulation
844 
845 JawaLite command syntax uses plain text format, followed by carriage return. Format is address - command letter - value:
846 xxYzzz<cr>
847 where xx= address (one or two chars), Y=command letter, zzz=optional argument, <cr>= carriage return character ('\r'=0x13)
848 
849 Address field is interpreted as follows:
850 0 - global address, all displays that support the command are set
851 1 - TFLD (Top Front Logic Dislay)
852 2 - BFLD (Bottom Front Logic Display)
853 3 - RLD (Rear Logic Display)
854 4 - Front PSI
855 5 - Rear PSI
856 6 - Front Holo (not implemented here)
857 7 - Rear Holo (not implemented here)
858 8 - Top Holo (not implemented here)
859 
860 Implemented commands (indicated durations are the sketch defaults, they are adjustable, see below):
861 
862 Command T
863 0-5T0 - test (switches all logics to on)
864 0-5T1 - display to normal random
865 0T2 - flah, same as alarm
866 0T3 - alarm for 4 seconds (flashes displays on and off)
867 0T4 - short circuit (10 seconds sequence)
868 0T5 - scream, same as alarm
869 0T6 - leia, 34 seconds (moving horizontal bar)
870 0T10 - Star Wars. Displays "Star Wars" on RLD, "STARS" on TFLD, "WARS" on BFLD
871 0T11 - March (alternating halfs of logics, 47 seconds)
872 0-3T92 - spectrum, bargraph displays. Runs forever, reset by calling to 0T1
873 0-3T100 - Text displays. Displays text set by the M command below
874 6T1, 7T1, 8T1 -> holos on - not implemented, HP lights controlled by MarcDuino HP.
875 0D, 6D, 7D, 8D -> holos off - not implemented, HP lights controlled by MarcDuino HP.
876 
877 Command M, set message for the text. 0= all logics, 1=TFLD, 2=BFLD, 3=RLD
878 0-3Mmessage - where "message" is an ASCII string to be displayed. Only uppercase implemented.
879 
880 Command P, character set
881 0-3P61 - swtich display to aurabesh characters
882 0-3P60 - switch display to latin (e.g.english) characters
883 
884 Extended "CuriousMarc Teeces" commands:
885 
886 Extra command T to turn displays off
887 0-5T20
888 
889 Command R, adjusts random style:
890 0-3Rx where x=0 to 6.
891 
892 Command S, PSI state
893 0,4-5S0 - PSI all on (same as 4-5T0)
894 0,4-5S1 - PSI normal mode (same as 4-5T1)
895 0,4-5S2 - PSI first color
896 0,4-5S3 - PSI second color
897 0,4-5S4 - PSI off
898 
899 
900 ********************/
901 
902 // =======================================================================================
903 // You can tweak the effects by changing the numbers below
904 // =======================================================================================
905 
906 // set brightness levels here (a value of 0-15)
907 #define RLDbrightness 5 //rear Logic
908 #define RPSIbrightness 15 //rear PSI
909 #define FLDbrightness 5 //front Logics
910 #define FPSIbrightness 15 //front PSI
911 
912 // time in ms between logic display updates (lower = blink faster)
913 #define LOGICupdateDelay 75
914 
915 // default random blinking styles of the logics.
916 // Styles 0 to 2 are for special effects (I used them in Failure)
917 // Style 3 is legacy Teeces ("blocky random", about 50% off, most LED changing at a time)
918 // Styles 4 and 5 are recommended, more organic random styles (not all LEDs change at the same time, more LEDs on)
919 // Stlye 6 is most LEDs on (special effect).
920 // 0= almost all off, 1= most off, 2= some off, 3= legacy random, 4= stage 1 organic, 5= stage 2 organic, 6 = stage 3 organic
921 // This is the default starting style. It can be adjusted on the fly by sending the R JawaLite command.
922 #define LOGICRandomStyle 4
923 
924 // update speed of bargraph display
925 #define BARGRAPHupdateDelay 50
926 
927 // Delay (in ms) between color wipe steps when PSI changes color (lower= wipe faster)
928 #define PSIwipeDelay 70
929 
930 // You can make the front or rear PSI get stuck inbetween colors from time to time by setting these parameters to 1 (0 for normal operation
931 #define FPSIgetsStuck 0
932 #define RPSIgetsStuck 0
933 
934 // parameters for how long and how often it gets stuck
935 #define PSIstuckhowlong 10000 // stuck time in ms
936 #define PSIstuckhowoften 5 // how often. 5 means 1 out of 5 swipes on average. So the higher the less often.
937 
938 // text scroll speed (in ms, lower= scroll faster)
939 #define SCROLLspeed 48
940 
941 // Leia effect parameters
942 #define LEIAduration 34000 // in ms
943 #define LEIAspeed 100 //2200 // in no particular units, lower=faster
944 
945 // Alarm effect parameters
946 #define ALARMduration 4000 // in ms
947 #define ALARMspeed 100 // in ms
948 
949 // March effect parameters
950 #define MARCHduration 47000 // in ms
951 #define MARCHspeed 555 // in ms. Adjusted to the tempo of 108 of my March recording (60/108=555ms).
952 
953 // Failure effect parameters
954 #define FAILUREduration 10000
955 #define FAILUREloops 5
956 #define FAILUREspeed 75
957 
958 // =========================================================================
959 // Only make changes below here if you know what you are doing
960 
961 /*
962 Different Arduino's have different pin numbers that are used for the rear chains.
963  Arduino Pro Mini uses pins 12,11,10 for rear D,C,L
964  Sparkfun Pro Micro uses pins 14,16,10 for rear D,C,L
965  Arduino Micro uses pins A2,A1,A0 for rear D,C,L
966 */
967 
968 #if (BOARDtype==1)
969  #define DVAL 12
970  #define CVAL 11
971  #define LVAL 10
972 #elif (BOARDtype==2)
973  #define DVAL 14
974  #define CVAL 16
975  #define LVAL 10
976 #elif (BOARDtype==3)
977  #define DVAL A2
978  #define CVAL A1
979  #define LVAL A0
980 #endif
981 
982 #define FDEV 3 //3 devices for front chain (top FLD, bottom FLD, front PSI
983 #define FPSIDEV 2 //front PSI is device #2 in the front chain
984 #define RPSIDEV 3 //rear PSI is device #3 in the rear chain
985 
986 #define LETTERWIDTH 5 // letter with in pixels
987 #define MAXSTRINGSIZE 64 // maximum number of letters in a logic display message
988 #define CMD_MAX_LENGTH 64 // maximum number of characters in a command (63 chars since we need the null termination)
989 
991 
992 // This is the LED grid for the 3 logic displays.
993 // Since an "unsigned long" is 32 bits wide in gcc, this gives
994 // 3 grids of 5 rows x 32 bits (which could control 5 rows of 32 LEDs)
995 // Grid 0 if used for the top FLD, uses only the first 5x9 LEDs/bits
996 // Grid 1 is used for the bottom FLD, uses only the first 5x9 LEDs/bits
997 // Grid 3 is used for the RLD, uses only 5x27 LEDs/bits
998 // This LED grid is sent to the actual display in the function showGrid(display)
999 unsigned long LEDgrid[3][5];
1000 
1001 // starting values for text scrolling positions on each display.
1002 int scrollPositions[]={9,9,27};
1003 
1004 // textScrollCount
1005 // 0,1, and 2 are used in scrollText for each display, counts how many times the string has completely scrolled
1006 long textScrollCount[3];
1007 
1008 // alphabet 0= english, 1= aurabesh
1009 #define LATIN 0
1010 #define AURABESH 1
1011 byte alphabetType[3];
1012 
1013 // display mode
1014 #define NORM 0
1015 #define ALARM 1
1016 #define MARCH 2
1017 #define LEIA 3
1018 #define FAILURE 4
1019 byte displayEffect; // 0 = normal, 1 = alarm, 2 = march, 3 = leia, 4 = failure
1020 
1021 // display state for the logics, 0=normal random, 1=text display, 2=bargraph, 3=test, 4=off
1022 #define RANDOM 0
1023 #define TEXT 1
1024 #define BARGRAPH 2
1025 #define TEST 3
1026 #define OFF 4
1027 byte displayState[3];
1028 
1029 // display state for the PSI, 0=normal random, 1=color1, 2=color2, 3=test, 4=off (0, 3 and 4 are shared with displays above)
1030 #define COLOR1 1
1031 #define COLOR2 2
1032 byte psiState[2];
1033 
1034 #define BLUE COLOR1
1035 #define RED COLOR2
1036 #define YELLOW COLOR1
1037 #define GREEN COLOR2
1038 
1039 // start with the default display random mode. This can be altered via extended JawaLite commands.
1040 byte randomStyle[3]={LOGICRandomStyle, LOGICRandomStyle, LOGICRandomStyle};
1041 
1042 // tracks if we are in the midst of running an effect
1043 byte effectRunning;
1044 
1045 // memory for the text strings to be displayed, add one for the NULL character at the end
1046 char logicText[3][MAXSTRINGSIZE+1];
1047 
1048 // memory for command string processing
1049 char cmdString[CMD_MAX_LENGTH];
1050 
1051 // Create Maxim devices SPI driver objects for front and rear chains
1052 LedControl lcRear=LedControl(DVAL,CVAL,LVAL,4); //rear chain, 3 devices for RLD, one for the rear PSI
1053 LedControl lcFront=LedControl(9,8,7,FDEV); //front chain, 3 devices: TFLD, BFLD, front PSI
1054 
1055 // handle to the Serial object, since it's a different one Micro than Minis
1056 HardwareSerial* serialPort;
1057 
1058 // the array psiPatterns is used to program the psi wipe
1059 // The first five elements are the LED values for the first color for each row.
1060 // The second five elements are the LED values for the second color for each row.
1061 #if (PSItype==4)
1062 #define HPROW 5
1063 #define HPPAT1 B10101010
1064 #define HPPAT2 B01010101
1065 int psiPatterns[]={HPPAT1,HPPAT2,HPPAT1,HPPAT2,HPPAT1,HPPAT2,HPPAT1,HPPAT2,HPPAT1,HPPAT2};
1066 #elif (PSItype==3)
1067 #define HPROW 5
1068 #define HPPAT1 B11100000
1069 #define HPPAT2 B00011111
1070 int psiPatterns[]={HPPAT1,HPPAT1,HPPAT1,HPPAT1,HPPAT1,HPPAT2,HPPAT2,HPPAT2,HPPAT2,HPPAT2};
1071 #elif (PSItype==2)
1072 #define HPROW 4
1073 #define HPPAT1 B10101000
1074 #define HPPAT2 B01010100
1075 int psiPatterns[]={HPPAT1,HPPAT2,HPPAT1,HPPAT2,HPPAT1,HPPAT2,HPPAT1,HPPAT2,HPPAT1,HPPAT2};
1076 #elif (PSItype==1)
1077 #define HPROW 4
1078 #define HPPAT1 B11000000
1079 #define HPPAT2 B00110000
1080 int psiPatterns[]={HPPAT1,HPPAT1,HPPAT1,HPPAT1,HPPAT1,HPPAT2,HPPAT2,HPPAT2,HPPAT2,HPPAT2};
1081 #endif
1082 
1083 
1084 
1085 // =======================================================================================
1086 
1088 // Initialization
1089 void setup()
1090 {
1091  // Since the serial names are different on different platforms, need to access through a universal pointer instead
1092  // Sprakfun Pro Micro and Arduino Micro are based on Atmega32u4 chips, hardware serial is Serial1
1093  #if (BOARDtype==2 || BOARDtype==3)
1094  Serial1.begin(BAUDRATE);
1095  serialPort=&Serial1;
1096  #else
1097  // Arduino Pro Mini is based on an Atmega328, hardware serial is Serial
1098  Serial.begin(BAUDRATE);
1099  serialPort=&Serial;
1100  #endif
1101 
1102  // Serial port welcome string, using the universal serialPort pointer
1103  serialPort->println();
1104  serialPort->println();
1105  serialPort->println("--- CuriousMarc Teeces v1.0 ---");
1106 
1107  randomSeed(analogRead(0));
1108 
1109  // exit shutdown of each chip and clear displays
1110  for(int dev=0;dev<lcRear.getDeviceCount();dev++)
1111  {
1112  lcRear.shutdown(dev, false); //take the device out of shutdown (power save) mode
1113  lcRear.clearDisplay(dev);
1114  }
1115  for(int dev=0;dev<lcFront.getDeviceCount();dev++)
1116  {
1117  lcFront.shutdown(dev, false); //take the device out of shutdown (power save) mode
1118  lcFront.clearDisplay(dev);
1119  }
1120 
1121  //set intensity of devices in rear chain...
1122  lcRear.setIntensity(0, RLDbrightness); //RLD
1123  lcRear.setIntensity(1, RLDbrightness); //RLD
1124  lcRear.setIntensity(2, RLDbrightness); //RLD
1125  lcRear.setIntensity(3, RPSIbrightness); //Rear PSI
1126 
1127  //set intensity of devices in front chain...
1128  for(int dev=0;dev<(lcFront.getDeviceCount()-1);dev++)
1129  {
1130  lcFront.setIntensity(dev, FLDbrightness); //front logics (all but the last dev in chain)
1131  }
1132  lcFront.setIntensity(FPSIDEV, FPSIbrightness); //Front PSI
1133 
1134  //HP lights on constantly.
1135  //lcRear.setRow(3,4,255); //rear psi
1136  //lcFront.setRow(FPSIDEV,4,255); //front psi
1137 
1138  //startup scrolling text. This function blocks until all the strings have scrolled completely off-screen.
1139  while((textScrollCount[0]<1) || (textScrollCount[1]<1) || (textScrollCount[2]<1)) // keep scrolling until they have all scrolled once
1140  {
1141  if (textScrollCount[0]<1) scrollText(0,TFLDtext); //top front text
1142  if (textScrollCount[1]<1) scrollText(1,BFLDtext); //bottom front text
1143  if (textScrollCount[2]<1) scrollText(2,RLDtext); //rear text
1144  }
1145 
1146  // Scrolling is done, switch to random mode
1147  resetDisplays();
1148 
1149  // Ready to accept commands
1150  serialPort->println("Ready for command.");
1151  serialPort->println();
1152  serialPort->print("> ");
1153 
1154  /***************************
1155  //------------------------------------------------------------
1156  // For code testing, throw the displays into a particular mode
1157  //------------------------------------------------------------
1158 
1159  // test strings for logics
1160  setText(0,"TFLD TEST ");
1161  setText(1,"BFLD TEST ");
1162  setText(2,"RLD TEST ");
1163 
1164  // displayEffect test
1165  // 0=normal
1166  // 5=leia
1167  // 11= march
1168  // 1= alarm
1169  displayEffect=0;
1170 
1171  // alphabet selection test
1172  alphabetType[0]=0;
1173  alphabetType[1]=0;
1174  alphabetType[2]=0;
1175 
1176  // text/random state test
1177  displayState[0]=1;
1178  displayState[1]=1;
1179  displayState[2]=1;
1180 
1181  //--------end test--------
1182  ******************************/
1183 
1184 }
1185 
1187 // Main Loop
1188 void loop()
1189 {
1190 
1191  // listen to commands through the serial port
1192  if(serialPort->available())
1193  {
1194  char ch;
1195  byte command_available;
1196 
1197  ch=serialPort->read(); // get input
1198  serialPort->print(ch); // echo back
1199  command_available=buildCommand(ch, cmdString); // build command line
1200  if (command_available)
1201  {
1202  parseCommand(cmdString); // interpret the command
1203  serialPort->println(); // prompt again
1204  serialPort->print("> ");
1205  }
1206  }
1207 
1208  // PSIs are always random
1209  // randomFPSI();
1210  //setFPSI(0);
1211  //randomRPSI();
1212  //setRPSI(GREEN);
1213 
1214  // Logics can be
1215  // - in a global effect mode (all displays involved)
1216  // - in normal individual, each display can be independtly in one of the following state:
1217  // - in random display
1218  // - in text display
1219  // - in bargraph display
1220  // - all on (test)
1221  // - all off
1222 
1223  switch(displayEffect)
1224  {
1225  // go to specific effect
1226  case 1:
1227  alarmDisplay(ALARMduration);
1228  break;
1229  case 2:
1230  marchDisplay(MARCHduration);
1231  break;
1232  case 3:
1233  leiaDisplay(LEIAduration);
1234  break;
1235  case 4:
1236  failureDisplay(FAILUREduration);
1237  break;
1238  default: // default is random, text or test depending on each displayState
1239  // cycle for each display
1240  for(byte disp=0; disp<3; disp++)
1241  {
1242  switch(displayState[disp])
1243  {
1244  case RANDOM:
1245  randomDisplay(disp);
1246  break;
1247  case TEXT:
1248  textDisplay(disp);
1249  break;
1250  case TEST:
1251  testDisplay(disp);
1252  break;
1253  case OFF:
1254  offDisplay(disp);
1255  break;
1256  case BARGRAPH:
1257  bargraphDisplay(disp);
1258  break;
1259  default: // unknown mode reverts to random display
1260  randomDisplay(disp);
1261  break;
1262  }
1263  }
1264  switch(psiState[0])
1265  {
1266  case RANDOM:
1267  randomFPSI();
1268  break;
1269  case COLOR1: case COLOR2: case OFF: case TEST:
1270  setFPSI(psiState[0]);
1271  break;
1272  default:
1273  randomFPSI();
1274  break;
1275  }
1276  switch(psiState[1])
1277  {
1278  case RANDOM:
1279  randomRPSI();
1280  break;
1281  case COLOR1: case COLOR2: case OFF: case TEST:
1282  setRPSI(psiState[1]);
1283  break;
1284  default:
1285  randomRPSI();
1286  break;
1287  }
1288  }
1289 }
1290 
1292 // Command language - JawaLite emulation
1294 
1295 
1297 // command line builder, makes a valid command line from the input
1298 byte buildCommand(char ch, char* output_str)
1299 {
1300  static uint8_t pos=0;
1301  switch(ch)
1302  {
1303  case '\r': // end character recognized
1304  output_str[pos]='\0'; // append the end of string character
1305  pos=0; // reset buffer pointer
1306  return true; // return and signal command ready
1307  break;
1308  default: // regular character
1309  output_str[pos]=ch; // append the character to the command string
1310  if(pos<=CMD_MAX_LENGTH-1)pos++; // too many characters, discard them.
1311  break;
1312  }
1313  return false;
1314 }
1315 
1317 // command parser and switcher,
1318 // breaks command line in pieces,
1319 // rejects invalid ones,
1320 // switches to the right command
1321 void parseCommand(char* inputStr)
1322 {
1323  byte hasArgument=false;
1324  int argument;
1325  int address;
1326  byte pos=0;
1327  byte length=strlen(inputStr);
1328  if(length<2) goto beep; // not enough characters
1329 
1330  // get the adress, one or two digits
1331  char addrStr[3];
1332  if(!isdigit(inputStr[pos])) goto beep; // invalid, first char not a digit
1333  addrStr[pos]=inputStr[pos];
1334  pos++; // pos=1
1335  if(isdigit(inputStr[pos])) // add second digit address if it's there
1336  {
1337  addrStr[pos]=inputStr[pos];
1338  pos++; // pos=2
1339  }
1340  addrStr[pos]='\0'; // add null terminator
1341  address= atoi(addrStr); // extract the address
1342 
1343  // check for more
1344  if(!length>pos) goto beep; // invalid, no command after address
1345 
1346  // special case of M commands, which take a string argument
1347  if(inputStr[pos]=='M')
1348  {
1349  pos++;
1350  if(!length>pos) goto beep; // no message argument
1351  doMcommand(address, inputStr+pos); // pass rest of string as argument
1352  return; // exit
1353  }
1354 
1355  // other commands, get the numerical argument after the command character
1356 
1357  pos++; // need to increment in order to peek ahead of command char
1358  if(!length>pos) hasArgument=false; // end of string reached, no arguments
1359  else
1360  {
1361  for(byte i=pos; i<length; i++)
1362  {
1363  if(!isdigit(inputStr[i])) goto beep; // invalid, end of string contains non-numerial arguments
1364  }
1365  argument=atoi(inputStr+pos); // that's the numerical argument after the command character
1366  hasArgument=true;
1367  }
1368 
1369  // switch on command character
1370  switch(inputStr[pos-1]) // 2nd or third char, should be the command char
1371  {
1372  case 'T':
1373  if(!hasArgument) goto beep; // invalid, no argument after command
1374  doTcommand(address, argument);
1375  break;
1376  case 'D': // D command is weird, does not need an argument, ignore if it has one
1377  doDcommand(address);
1378  break;
1379  case 'P':
1380  if(!hasArgument) goto beep; // invalid, no argument after command
1381  doPcommand(address, argument);
1382  break;
1383  case 'R':
1384  if(!hasArgument) goto beep; // invalid, no argument after command
1385  doRcommand(address, argument);
1386  break;
1387  case 'S':
1388  if(!hasArgument) goto beep; // invalid, no argument after command
1389  doScommand(address, argument);
1390  break;
1391  default:
1392  goto beep; // unknown command
1393  break;
1394  }
1395 
1396  return; // normal exit
1397 
1398  beep: // error exit
1399  serialPort->write(0x7); // beep the terminal, if connected
1400  return;
1401 }
1402 
1404 // command executers
1405 
1406 // set text command
1407 void doMcommand(int address, char* message)
1408 {
1409  serialPort->println();
1410  serialPort->print("Command: M ");
1411  serialPort->print("Address: ");
1412  serialPort->print(address);
1413  serialPort->print(" Argument: ");
1414  serialPort->print(message);
1415 
1416  if(address==0) {setText(0, message); setText(1, message); setText(2, message); resetAllText();}
1417  if(address==1) {setText(0, message); resetText(0);}
1418  if(address==2) {setText(1, message); resetText(1);}
1419  if(address==3) {setText(2, message); resetText(2);}
1420 
1421 }
1422 
1423 // various commands for states and effects
1424 void doTcommand(int address, int argument)
1425 {
1426  serialPort->println();
1427  serialPort->print("Command: T ");
1428  serialPort->print("Address: ");
1429  serialPort->print(address);
1430  serialPort->print(" Argument: ");
1431  serialPort->print(argument);
1432 
1433 
1434  switch(argument)
1435  {
1436  case 0: // test mode
1437  exitEffects();
1438  if(address==0) {displayState[0]=displayState[1]=displayState[2]=psiState[0]=psiState[1]=TEST; resetAllText();}
1439  if(address==1) {displayState[0]=TEST; resetText(0);}
1440  if(address==2) {displayState[1]=TEST; resetText(1);}
1441  if(address==3) {displayState[2]=TEST; resetText(2);}
1442  if(address==4) {psiState[0]=TEST;}
1443  if(address==5) {psiState[1]=TEST;}
1444  break;
1445  case 1: // normal random mode, cancel effects too
1446  exitEffects();
1447  if(address==0) {displayState[0]=displayState[1]=displayState[2]=psiState[0]=psiState[1]=RANDOM; resetAllText();}
1448  if(address==1) {displayState[0]=RANDOM; resetText(0);}
1449  if(address==2) {displayState[1]=RANDOM; resetText(1);}
1450  if(address==3) {displayState[2]=RANDOM; resetText(2);}
1451  if(address==4) {psiState[0]=RANDOM;}
1452  if(address==5) {psiState[1]=RANDOM;}
1453  break;
1454  case 2: case 3: case 5: // alarm
1455  exitEffects();
1456  displayEffect=ALARM;
1457  break;
1458  case 4: // short circuit
1459  exitEffects();
1460  displayEffect=FAILURE;
1461  break;
1462  case 6: // leia
1463  exitEffects();
1464  displayEffect=LEIA;
1465  break;
1466  case 10: // star wars
1467  exitEffects();
1468  // reset text
1469  for(byte disp=0; disp<3; disp++)
1470  {
1471  resetText(disp);
1472  alphabetType[disp]=0;
1473  displayState[disp]=TEXT;
1474 
1475  }
1476  setText(0,"STAR ");
1477  setText(1," WARS");
1478  setText(2,"STAR WARS ");
1479  break;
1480  case 11: // March
1481  exitEffects();
1482  displayEffect=MARCH;
1483  break;
1484  case 20: // extra CuriousMarc command, to turn displays off.
1485  exitEffects();
1486  if(address==0) {displayState[0]=displayState[1]=displayState[2]=psiState[0]=psiState[1]=OFF; resetAllText();}
1487  if(address==1) {displayState[0]=OFF; resetText(0);}
1488  if(address==2) {displayState[1]=OFF; resetText(1);}
1489  if(address==3) {displayState[2]=OFF; resetText(2);}
1490  if(address==4) {psiState[0]=OFF;}
1491  if(address==5) {psiState[1]=OFF;}
1492  break;
1493  case 92: // bargraph mode, does not cancel effects, but resets text
1494  exitEffects();
1495  if(address==0) {displayState[0]=displayState[1]=displayState[2]=BARGRAPH; resetAllText();}
1496  if(address==1) {displayState[0]=BARGRAPH; resetText(0);}
1497  if(address==2) {displayState[1]=BARGRAPH; resetText(1);}
1498  if(address==3) {displayState[2]=BARGRAPH; resetText(2);}
1499  break;
1500  case 100: // text mode, cancel effects too
1501  exitEffects();
1502  if(address==0) {displayState[0]=displayState[1]=displayState[2]=TEXT; resetAllText();}
1503  if(address==1) {displayState[0]=TEXT; resetText(0);}
1504  if(address==2) {displayState[1]=TEXT; resetText(1);}
1505  if(address==3) {displayState[2]=TEXT; resetText(2);}
1506  break;
1507  default:
1508  exitEffects(); // default stops any running effect
1509  break;
1510  }
1511 }
1512 
1513 // holos commands
1514 void doDcommand(int address)
1515 {
1516  serialPort->println();
1517  serialPort->print("Command: D ");
1518  serialPort->print("Address: ");
1519  serialPort->print(address);
1520 
1521  // for turning off holos, not implemented
1522 }
1523 
1524 // alphabet switching
1525 void doPcommand(int address, int argument)
1526 {
1527  serialPort->println();
1528  serialPort->print("Command: P ");
1529  serialPort->print("Address: ");
1530  serialPort->print(address);
1531  serialPort->print(" Argument: ");
1532  serialPort->print(argument);
1533  switch(argument)
1534  {
1535  case 60: // latin
1536  if(address==0) {alphabetType[0]=alphabetType[1]=alphabetType[2]=LATIN;}
1537  if(address==1) {alphabetType[0]=LATIN;}
1538  if(address==2) {alphabetType[1]=LATIN;}
1539  if(address==3) {alphabetType[2]=LATIN;}
1540  break;
1541  case 61: // Aurabesh
1542  if(address==0) {alphabetType[0]=alphabetType[1]=alphabetType[2]=AURABESH;}
1543  if(address==1) {alphabetType[0]=AURABESH;}
1544  if(address==2) {alphabetType[1]=AURABESH;}
1545  if(address==3) {alphabetType[2]=AURABESH;}
1546  break;
1547  default:
1548  // should I do back to latin on default argument?
1549  break;
1550  }
1551 }
1552 
1553 // random styles for Logics
1554 void doRcommand(int address, int argument)
1555 {
1556  serialPort->println();
1557  serialPort->print("Command: R ");
1558  serialPort->print("Address: ");
1559  serialPort->print(address);
1560  serialPort->print(" Argument: ");
1561  serialPort->print(argument);
1562 
1563  if(address==0) {randomStyle[0]=randomStyle[1]=randomStyle[2]=argument;}
1564  if(address==1) {randomStyle[0]=argument;}
1565  if(address==2) {randomStyle[1]=argument;}
1566  if(address==3) {randomStyle[2]=argument;}
1567 }
1568 
1569 void doScommand(int address, int argument)
1570 {
1571  serialPort->println();
1572  serialPort->print("Command: S ");
1573  serialPort->print("Address: ");
1574  serialPort->print(address);
1575  serialPort->print(" Argument: ");
1576  serialPort->print(argument);
1577  switch(argument)
1578  {
1579  case 0: // test, all PSI leds on
1580  if(address==0) {psiState[0]=psiState[1]=TEST;}
1581  if(address==4) {psiState[0]=TEST;}
1582  if(address==5) {psiState[1]=TEST;}
1583  break;
1584  case 1: // normal, random mode
1585  if(address==0) {psiState[0]=psiState[1]=RANDOM;}
1586  if(address==4) {psiState[0]=RANDOM;}
1587  if(address==5) {psiState[1]=RANDOM;}
1588  break;
1589  case 2: // color 1
1590  if(address==0) {psiState[0]=psiState[1]=COLOR1;}
1591  if(address==4) {psiState[0]=COLOR1;}
1592  if(address==5) {psiState[1]=COLOR1;}
1593  break;
1594  case 3: // color 2
1595  if(address==0) {psiState[0]=psiState[1]=COLOR2;}
1596  if(address==4) {psiState[0]=COLOR2;}
1597  if(address==5) {psiState[1]=COLOR2;}
1598  break;
1599  case 4: // off
1600  if(address==0) {psiState[0]=psiState[1]=OFF;}
1601  if(address==4) {psiState[0]=OFF;}
1602  if(address==5) {psiState[1]=OFF;}
1603  break;
1604  default:
1605  // should I do back to latin on default argument?
1606  break;
1607  }
1608 }
1609 
1610 // =======================================================================================
1611 
1613 // Logic Display modes (random, text, test, bargraph, off) and random PSI functions
1615 
1616 // Utility Random
1617 void randomDisplay(byte disp)
1618 {
1619  switch(disp)
1620  {
1621  case 0:
1622  randomDisplayTFLD();
1623  break;
1624  case 1:
1625  randomDisplayBFLD();
1626  break;
1627  case 2:
1628  randomDisplayRLD();
1629  break;
1630  default:
1631  break;
1632  }
1633 }
1634 
1635 // Utility Scrolling Text
1636 void textDisplay(byte disp)
1637 {
1638  if(disp>2) return;
1639  scrollText(disp, logicText[disp]);
1640 }
1641 
1643 // Test (all on)
1644 void testDisplay(byte disp)
1645 {
1646  if(disp>2) return;
1647  for(byte i=0; i<5; i++)
1648  {
1649  LEDgrid[disp][i]=~0L;
1650  }
1651  showGrid(disp);
1652 }
1653 
1655 // Utility: Off
1656 void offDisplay(byte disp)
1657 {
1658  if(disp>2) return;
1659  for(byte i=0; i<5; i++)
1660  {
1661  LEDgrid[disp][i]=0L;
1662  }
1663  showGrid(disp);
1664 }
1665 
1667 // bargraph
1668 void bargraphDisplay(byte disp)
1669 {
1670  static byte bargraphdata[3][27]; // status of bars
1671 
1672  if(disp>2) return;
1673 
1674  // speed control
1675  static long previousDisplayUpdate[3]={0,0,0};
1676  unsigned long currentMillis = millis();
1677  if(currentMillis - previousDisplayUpdate[disp] < BARGRAPHupdateDelay) return;
1678  previousDisplayUpdate[disp] = currentMillis;
1679 
1680  byte maxcol;
1681  if(disp==0 || disp==1) maxcol=9;
1682  else maxcol=27;
1683 
1684  // loop over each column
1685  for(byte column=0; column<maxcol; column++)
1686  {
1687  //byte value=random(0,5);
1688  byte value = updatebar(disp, column, bargraphdata[disp]);
1689  byte data=0;
1690  for(int i=0; i<=value; i++)
1691  {
1692  data |= 0x01<<i;
1693  }
1694  //data=B00011111;
1695  fillColumn( disp, column, data);
1696  }
1697  showGrid(disp);
1698 }
1699 
1700 // helper for updating bargraph values
1701 byte updatebar(byte disp, byte column, byte* bargraphdata)
1702 {
1703  // bargraph values go up or down one pixel at a time
1704  int variation = random(0,3); // 0= move down, 1= stay, 2= move up
1705  int value=(int)bargraphdata[column]; // get the previous value
1706  if (value==5) value=3; // special case, staying stuck at maximum does not look realistic, knock it down
1707  else value += (variation-1); // vary it
1708  if (value<=0) value=0; // can't belower than 0
1709  if (value>5) value=5; // can't be higher than 5
1710  bargraphdata[column]=(byte)value; // store new value, use byte type to save RAM
1711  return (byte)value; // return new value
1712 }
1713 
1714 // helper for dealing with setting LEDgrid by column instead of by row
1715 void fillColumn(byte disp, byte column, byte data)
1716 {
1717  if (disp==2 && column>27) return;
1718  if (disp!=2 && column>9) return;
1719  for(byte row=0; row<5; row++)
1720  {
1721  // test if LED is on
1722  byte LEDon=(data & 1<<row);
1723  if(LEDon)
1724  LEDgrid[disp][4-row] |= (1L << column); // set column bit
1725  else
1726  LEDgrid[disp][4-row] &= ~(1L << column); // reset column bit
1727  }
1728 }
1729 
1730 // helper for generating more interesting random patterns for the logics
1731 long randomRow(byte randomMode)
1732 {
1733  switch(randomMode)
1734  {
1735  case 0: // stage -3
1736  return (random(256)&random(256)&random(256)&random(256));
1737  break;
1738  case 1: // stage -2
1739  return (random(256)&random(256)&random(256));
1740  break;
1741  case 2: // stage -1
1742  return (random(256)&random(256));
1743  break;
1744  case 3: // legacy "blocky" mode
1745  return random(256);
1746  break;
1747  case 4: // stage 1
1748  return (random(256)|random(256));
1749  break;
1750  case 5: // stage 2
1751  return (random(256)|random(256)|random(256));
1752  break;
1753  case 6: // stage 3
1754  return (random(256)|random(256)|random(256)|random(256));
1755  break;
1756  default:
1757  return random(256);
1758  break;
1759  }
1760 
1761 
1762 }
1763 
1764 
1766 // Random Rear Logic
1767 void randomDisplayRLD()
1768 {
1769  // static parameter for each display
1770  static long previousDisplayUpdate=0;
1771 
1772  // wait until delay before randomizing again
1773  unsigned long currentMillis = millis();
1774  if(currentMillis - previousDisplayUpdate < LOGICupdateDelay) return;
1775  previousDisplayUpdate = currentMillis;
1776 
1777 #if defined(TESTLOGICS) //turn on all logic LEDs to make sure they're all working
1778 
1779  for (int dev=0; dev<3; dev++) // loop on all devices, all rows, rear chain
1780  {
1781  for (int row=0; row<6; row++)
1782  lcRear.setRow(dev,row,255);
1783  }
1784 
1785 
1786 #else // regular random code
1787 
1788  // loop on all devices, all rows for RLD
1789  for (int dev=0; dev<3; dev++)
1790  {
1791  for (int row=0; row<6; row++)
1792  lcRear.setRow(dev,row,randomRow(randomStyle[2]));
1793  // Or-ing 3 times prevents too many LEDs from blinking off together
1794  // nice trick from BHD...
1795  }
1796 
1797 #endif
1798 
1799 }
1800 
1802 // random top FLD
1803 void randomDisplayTFLD()
1804 {
1805  // static parameter for each display
1806  static long previousDisplayUpdate=0;
1807 
1808  // wait until delay before randomizing again
1809  unsigned long currentMillis = millis();
1810  if(currentMillis - previousDisplayUpdate < LOGICupdateDelay) return;
1811  previousDisplayUpdate = currentMillis;
1812 
1813 #if defined(TESTLOGICS) //turn on all logic LEDs to make sure they're all working
1814 
1815  int dev=0; // loop on FLD, all rows, front chain
1816  for (int row=0; row<6; row++)
1817  lcFront.setRow(dev,row,255);
1818 
1819 #else // regular random code
1820 
1821  // loop on top FLD devices, all rows on front chain
1822  int dev=0;
1823  for (int row=0; row<6; row++)
1824  lcFront.setRow(dev,row,randomRow(randomStyle[0]));
1825 
1826 #endif
1827 
1828 }
1829 
1830 
1832 // random bottom FLD
1833 void randomDisplayBFLD()
1834 {
1835  // static parameter for each display
1836  static long previousDisplayUpdate=0;
1837 
1838  // wait until delay before randomizing again
1839  unsigned long currentMillis = millis();
1840  if(currentMillis - previousDisplayUpdate < LOGICupdateDelay) return;
1841  previousDisplayUpdate = currentMillis;
1842 
1843 #if defined(TESTLOGICS) //turn on all logic LEDs to make sure they're all working
1844 
1845  int dev=1; // loop on FLD, all rows, front chain
1846  for (int row=0; row<6; row++)
1847  lcFront.setRow(dev,row,255);
1848 
1849 #else // regular random code
1850 
1851  // loop on top FLD devices, all rows on front chain
1852  int dev=1;
1853  for (int row=0; row<6; row++)
1854  lcFront.setRow(dev,row,randomRow(randomStyle[1]));
1855 
1856 #endif
1857 
1858 }
1859 
1861 // PSI Modes
1863 
1865 // Front PSI static modes (on, off, color1, color2
1866 
1867 
1868 
1869 
1870 void setFPSI(byte mode)
1871 {
1872  switch(mode)
1873  {
1874  case OFF:
1875  for(byte row=0; row<HPROW; row++)
1876  {
1877  lcFront.setRow(2, row, 0x00);
1878  }
1879  break;
1880  case TEST: // all on
1881  for(byte row=0; row<HPROW; row++)
1882  {
1883  lcFront.setRow(2, row, 0xFF);
1884  }
1885  break;
1886  case COLOR1:
1887  for(byte row=0; row<HPROW; row++)
1888  {
1889  lcFront.setRow(2, row, psiPatterns[row]);
1890  }
1891  break;
1892  case COLOR2:
1893  for(byte row=0; row<HPROW; row++)
1894  {
1895  lcFront.setRow(2, row, psiPatterns[row+5]);
1896  }
1897  break;
1898  default:
1899  break;
1900  }
1901 }
1902 
1903 void setRPSI(byte mode)
1904 {
1905  switch(mode)
1906  {
1907  case OFF:
1908  for(byte row=0; row<HPROW; row++)
1909  {
1910  lcRear.setRow(3, row, 0x00);
1911  }
1912  break;
1913  case TEST:
1914  for(byte row=0; row<HPROW; row++)
1915  {
1916  lcRear.setRow(3, row, 0xFF);
1917  }
1918  break;
1919  case COLOR1:
1920  for(byte row=0; row<HPROW; row++)
1921  {
1922  lcRear.setRow(3, row, psiPatterns[row]);
1923  }
1924  break;
1925  case COLOR2:
1926  for(byte row=0; row<HPROW; row++)
1927  {
1928  lcRear.setRow(3, row, psiPatterns[row+5]);
1929  }
1930  break;
1931  default:
1932  break;
1933  }
1934 }
1935 
1936 
1937 
1939 // Front PSI random swipe
1940 void randomFPSI()
1941 {
1942  // Static variables per PSI function
1943  // PSI wipe timers
1944  static unsigned long psiMillisChangeDir=0; // wait time between change of directions
1945  static unsigned long psiMillisSwipe=0; // wait time between swipe tests
1946  // psi delay timer, currently only one, both PSI change at the same time
1947  static long unsigned psiChangeColorDelay=0; // variable time for changing colors
1948  // psi color and row counter numbers
1949  static int psiColor=0;
1950  static int psiCurrentSwipeRow=0;
1951  // "stuck" flag
1952  static byte isStuck=0;
1953 
1954  unsigned long currentMillis = millis();
1955  if(isStuck==1)
1956  {
1957  // put direction changing on hold
1958  psiMillisChangeDir = currentMillis;
1959  }
1960  else
1961  {
1962  // if time has elapsed, reverse color and direction of swipe, chose another random time
1963  if(currentMillis - psiMillisChangeDir > psiChangeColorDelay*500) // delay between .5 to 5 seconds
1964  {
1965  // choose another random delay
1966  psiMillisChangeDir = currentMillis;
1967  psiChangeColorDelay = random(1,11);
1968 
1969  // reverse color and swipe direction
1970  if (psiColor ==0)
1971  {
1972  psiColor = 5;
1973  psiCurrentSwipeRow=0;
1974  }
1975  else
1976  {
1977  psiColor = 0;
1978  psiCurrentSwipeRow=HPROW-1;
1979  }
1980  }
1981  }
1982 
1983  // do next swipe step only if time has elapsed.
1984  if(isStuck==1) // pause swipe with long time if PSI is stuck.
1985  {if (currentMillis - psiMillisSwipe < PSIstuckhowlong) return;}
1986  else // regular swipe speed if not
1987  {if(currentMillis - psiMillisSwipe < PSIwipeDelay) return;}
1988 
1989  // get unstuck
1990  if(isStuck) isStuck=0;
1991 
1992  // if we are going to color 2 and haven't reach the end row, do next row
1993  if (psiCurrentSwipeRow<HPROW && psiColor == 5)
1994  {
1995  psiMillisSwipe = currentMillis;
1996  lcFront.setRow(2, psiCurrentSwipeRow, psiPatterns[psiCurrentSwipeRow+psiColor]);
1997  psiCurrentSwipeRow++;
1998  }
1999  // if we are going to color 1 and haven't reached the first row, do next row
2000  else if (psiCurrentSwipeRow>=0 && psiColor == 0)
2001  {
2002  psiMillisSwipe = currentMillis;
2003  lcFront.setRow(2, psiCurrentSwipeRow, psiPatterns[psiCurrentSwipeRow+psiColor]);
2004  psiCurrentSwipeRow--;
2005  }
2006 
2007  // let's get stuck once in a while
2008  if(FPSIgetsStuck && psiCurrentSwipeRow==2 && psiColor==5)
2009  // && isStuck==0 && isStuck!=2)
2010  {
2011  byte onceinawhile=random(PSIstuckhowoften); // one chance out of 20
2012  if(onceinawhile==1) isStuck=1;
2013  }
2014 }
2015 
2017 // Rear PSI random swipe
2018 void randomRPSI()
2019 {
2020  // Static variables, don't need to be globals
2021  // PSI wipe timers
2022  static unsigned long psiMillisChangeDir=0; // wait time between change of directions
2023  static unsigned long psiMillisSwipe=0; // wait time between swipe tests
2024  // psi delay timer, currently only one, both PSI change at the same time
2025  static unsigned long psiChangeColorDelay=0; // variable time for changing colors
2026  // psi color and row counter numbers
2027  static int psiColor=0;
2028  static int psiCurrentSwipeRow=0;
2029  // "stuck" flag
2030  static byte isStuck=0;
2031 
2032 
2033  unsigned long currentMillis = millis();
2034  if(isStuck==1)
2035  {
2036  // put direction changing on hold
2037  psiMillisChangeDir = currentMillis;
2038  }
2039  else
2040  {
2041  // if time has elapsed, reverse color and direction of swipe, chose another random time
2042  if(currentMillis - psiMillisChangeDir > psiChangeColorDelay*500) // delay between .5 to 5 seconds
2043  {
2044  // choose another random delay
2045  psiMillisChangeDir = currentMillis;
2046  psiChangeColorDelay = random(1,11);
2047 
2048  // reverse color and swipe direction
2049  if (psiColor ==0)
2050  {
2051  psiColor = 5;
2052  psiCurrentSwipeRow=0;
2053  }
2054  else
2055  {
2056  psiColor = 0;
2057  psiCurrentSwipeRow=HPROW-1;
2058  }
2059  }
2060  }
2061 
2062  // do next swipe step only if time has elapsed.
2063  if(isStuck==1) // pause swipe with long time if PSI is stuck.
2064  {if (currentMillis - psiMillisSwipe < PSIstuckhowlong) return;}
2065  else // regular swipe speed if not
2066  {if(currentMillis - psiMillisSwipe < PSIwipeDelay) return;}
2067 
2068  // get unstuck
2069  if(isStuck) isStuck=0;
2070 
2071  // if we are going to color 2 and haven't reach the end row, do next row
2072  if (psiCurrentSwipeRow<HPROW && psiColor == 5)
2073  {
2074  psiMillisSwipe = currentMillis;
2075  lcRear.setRow(3, psiCurrentSwipeRow, psiPatterns[psiCurrentSwipeRow+psiColor]);
2076  psiCurrentSwipeRow++;
2077  }
2078  // if we are going to color 1 and haven't reached the first row, do next row
2079  else if (psiCurrentSwipeRow>=0 && psiColor == 0)
2080  {
2081  psiMillisSwipe = currentMillis;
2082  lcRear.setRow(3, psiCurrentSwipeRow, psiPatterns[psiCurrentSwipeRow+psiColor]);
2083  psiCurrentSwipeRow--;
2084  }
2085 
2086  // let's get stuck once in a while
2087  if(RPSIgetsStuck && psiCurrentSwipeRow==2 && psiColor==5)
2088  // && isStuck==0 && isStuck!=2)
2089  {
2090  byte onceinawhile=random(PSIstuckhowoften); // one chance out of 20
2091  if(onceinawhile==1) isStuck=1;
2092  }
2093 }
2094 
2096 // Special Effect Routines
2098 
2099 
2101 // Reset Utilities
2102 
2103 // resets text scrolling except alphabet
2104 void resetText(byte display)
2105 {
2106  // reset text
2107  scrollPositions[display]= (display==2? 27 : 9);
2108  textScrollCount[display]=0;
2109 }
2110 
2111 // same for all the displays
2112 void resetAllText()
2113 {
2114  for(byte disp=0; disp<3; disp++)
2115  {
2116  resetText(disp);
2117  }
2118 }
2119 
2120 // forces exit from effects immediately
2121 void exitEffects()
2122 {
2123  displayEffect=NORM;
2124 // psiState[0]=NORM;
2125 // psiState[1]=NORM;
2126  effectRunning=0;
2127 }
2128 
2129 // exit effects, reset scrolling, alphabet back to latin, mode to random
2130 void resetDisplays()
2131 {
2132  resetAllText();
2133  exitEffects();
2134  for(byte disp=0; disp<3; disp++)
2135  {
2136  alphabetType[disp]=LATIN;
2137  displayState[disp]=NORM;
2138  }
2139 }
2140 
2141 
2142 
2144 // Leia Display: horizontal moving line on all displays
2145 void leiaDisplay(unsigned long playTime)
2146 {
2147  static byte a = 0; // row counter a for writes
2148  static byte b = 0; // row counter b for erase
2149  //static byte color = 0; // counter for PSI color
2150 
2151  unsigned long currentMillis = millis();
2152  static unsigned long swtchMillis;
2153  static unsigned long enterMillis;
2154 
2155  // entry and exit timing
2156  if(effectRunning==0)
2157  {
2158  enterMillis=currentMillis;
2159  effectRunning=1;
2160  // blank display out
2161  clearGrid(0); showGrid(0);
2162  clearGrid(1); showGrid(1);
2163  clearGrid(2); showGrid(2);
2164  }
2165  // exit on playTime elapsed. playTime=0 means run forever.
2166  if(playTime && (currentMillis - enterMillis > playTime))
2167  {
2168  effectRunning=0;
2169  displayEffect=0;
2170  return;
2171  }
2172 
2173  // PSI stay random during effect (see at the end of function for an alternate effect
2174  randomFPSI();
2175  randomRPSI();
2176 
2177  // move the line when it's time
2178  if (currentMillis - swtchMillis > LEIAspeed)
2179  {
2180  swtchMillis=currentMillis;
2181  // draw moving line on RLD
2182  for(int dev=0;dev<3;dev++)
2183  {
2184  // set line LEDs on
2185  lcRear.setRow(dev,a,255);
2186  lcRear.setLed(dev,5,a,true);
2187 
2188  // set previous line LEDs off
2189  lcRear.setRow(dev,b,0);
2190  lcRear.setLed(dev,5,b,false);
2191  }
2192 
2193  // draw moving line on TFLD
2194  int dev=0;
2195  {
2196  // set line LEDs on
2197  lcFront.setRow(dev,a,255);
2198  lcFront.setLed(dev,5,a,true);
2199 
2200  // set line LEDs off
2201  lcFront.setRow(dev,b,0);
2202  lcFront.setLed(dev,5,b,false);
2203  }
2204 
2205  // draw moving line inverted on BFLD
2206  dev=1;
2207  {
2208  // set line LEDs on
2209  lcFront.setRow(dev,4-a,255);
2210  lcFront.setLed(dev,5,4-a,true);
2211 
2212  // set line LEDs off
2213  lcFront.setRow(dev,4-b,0);
2214  lcFront.setLed(dev,5,4-b,false);
2215  }
2216 
2217 
2218 
2219  // update row count
2220  b=a; // remember last row
2221  a++; // go to next row
2222  if (a>4)
2223  {
2224  a=0; // restart if done with the 5 rows
2225  /****** nah, don't like it
2226  // and switch PSI colors
2227  if(color)
2228  {
2229  setFPSI(BLUE);
2230  setRPSI(YELLOW);
2231  color=0;
2232  }
2233  else
2234  {
2235  setFPSI(RED);
2236  setRPSI(GREEN);
2237  color=1;
2238  }
2239  ************/
2240  }
2241  }
2242 }
2243 
2245 //alarmDisplay, alternating full on/full off
2246 
2247 void alarmDisplay(unsigned long playTime)
2248 {
2249  static byte swtch = 0;
2250 
2251 
2252  static unsigned long swtchMillis;
2253  unsigned long currentMillis = millis();
2254 
2255  // entry and exit timing
2256  static unsigned long enterMillis;
2257  if(effectRunning==0)
2258  {
2259  enterMillis=currentMillis;
2260  effectRunning=1;
2261  }
2262  // exit on playTime elapsed. playTime=0 means run forever.
2263  if(playTime && (currentMillis - enterMillis > playTime))
2264  {
2265  effectRunning=0;
2266  displayEffect=0;
2267  return;
2268  }
2269 
2270  // wait for the next period
2271  if (currentMillis - swtchMillis > ALARMspeed)
2272  {
2273  if (swtch == 0)
2274  {
2275  clearGrid(0);
2276  clearGrid(1);
2277  clearGrid(2);
2278 
2279  // everything on
2280  for(int row=0;row<5;row++)
2281  {
2282  LEDgrid[0][row]=0xFFFFFFFFL;
2283  LEDgrid[1][row]=0xFFFFFFFFL;
2284  LEDgrid[2][row]=0xFFFFFFFFL;
2285  }
2286 
2287  showGrid(0);
2288  showGrid(1);
2289  showGrid(2);
2290 
2291  setFPSI(RED);
2292  setRPSI(YELLOW);
2293 
2294  swtchMillis = millis();
2295  swtch = 1;
2296  }
2297  else if (swtch == 1)
2298  {
2299  // everything off
2300  clearGrid(0);
2301  clearGrid(1);
2302  clearGrid(2);
2303 
2304  showGrid(0);
2305  showGrid(1);
2306  showGrid(2);
2307 
2308  setFPSI(OFF);
2309  setRPSI(OFF);
2310 
2311  swtchMillis = millis();
2312  swtch = 0;
2313  }
2314  }
2315 }
2316 
2318 // March Effect, blocks alternating sideways
2319 void marchDisplay(unsigned long playTime)
2320 {
2321  unsigned long currentMillis = millis();
2322  static unsigned long swtchMillis = millis();
2323  static byte swtch = 0;
2324 
2325  // entry and exit timing
2326  static unsigned long enterMillis;
2327  if(effectRunning==0)
2328  {
2329  enterMillis=currentMillis;
2330  effectRunning=1;
2331  }
2332  // exit on playTime elapsed. playTime=0 means run forever.
2333  if(playTime && (currentMillis - enterMillis > playTime))
2334  {
2335  effectRunning=0;
2336  displayEffect=0;
2337  return;
2338  }
2339 
2340  if (currentMillis - swtchMillis > MARCHspeed)
2341  {
2342  if (swtch == 0)
2343  {
2344  clearGrid(0);
2345  clearGrid(1);
2346  clearGrid(2);
2347 
2348  for(int row=0;row<5;row++)
2349  {
2350  LEDgrid[0][row]=31L; // first 5 column on
2351  LEDgrid[1][row]=31L; // first 5 column on
2352  LEDgrid[2][row]=16383L; // first 14 column on
2353  }
2354 
2355  showGrid(0);
2356  showGrid(1);
2357  showGrid(2);
2358 
2359  setFPSI(RED);
2360  setRPSI(YELLOW);
2361 
2362  swtchMillis = millis();
2363  swtch = 1;
2364  }
2365  else if (swtch == 1)
2366  {
2367  clearGrid(0);
2368  clearGrid(1);
2369  clearGrid(2);
2370 
2371  for(int row=0;row<5;row++)
2372  {
2373  LEDgrid[0][row]= ~15L; // first 4 column off
2374  LEDgrid[1][row]= ~15L; // first 4 column off
2375  LEDgrid[2][row]= ~8191L; // first 13 column off
2376  }
2377 
2378  showGrid(0);
2379  showGrid(1);
2380  showGrid(2);
2381 
2382  setFPSI(BLUE);
2383  setRPSI(GREEN);
2384 
2385  swtchMillis = millis();
2386  swtch = 0;
2387  }
2388  }
2389 }
2390 
2391 
2393 // Failure: screen having less and less dots
2394 
2395 // failure helper function
2396 void showFailure(byte style)
2397 {
2398  // TLFLD
2399  for (int row=0; row<6; row++)
2400  lcFront.setRow(0,row,randomRow(style));
2401  // BFLD
2402  for (int row=0; row<6; row++)
2403  lcFront.setRow(1,row,randomRow(style));
2404  // RLD
2405  for (int dev=0; dev<3; dev++)
2406  {
2407  for (int row=0; row<6; row++)
2408  lcRear.setRow(dev,row,randomRow(style));
2409  }
2410  // FPSI
2411  for (int row=0; row<HPROW; row++)
2412  lcFront.setRow(2,row,randomRow(style));
2413  // RPSI
2414  for (int row=0; row<HPROW; row++)
2415  lcRear.setRow(3,row,randomRow(style));
2416 }
2417 
2418 // failure main function
2419 void failureDisplay(unsigned long playTime)
2420 {
2421  static int loopCount=0; // number of loops
2422  static unsigned long lastMillis;
2423  unsigned long currentMillis = millis();
2424  static unsigned long blinkSpeed = FAILUREspeed;
2425 
2426  // entry and exit timing
2427  static unsigned long enterMillis;
2428  if(effectRunning==0)
2429  {
2430  blinkSpeed = FAILUREspeed;
2431  loopCount=0;
2432  enterMillis=currentMillis;
2433  effectRunning=1;
2434  }
2435  // exit on playTime elapsed. playTime=0 means run forever.
2436  if(playTime && (currentMillis - enterMillis > playTime))
2437  {
2438  effectRunning=0;
2439  displayEffect=0;
2440  return;
2441  }
2442 
2443  // speed control
2444  if (currentMillis - lastMillis < blinkSpeed) return;
2445  lastMillis = currentMillis;
2446  loopCount++;
2447 
2448  // every 2200 counts, move the line
2449  if (loopCount<FAILUREloops)
2450  {
2451  blinkSpeed=FAILUREspeed;
2452  showFailure(4);
2453  }
2454  else if(loopCount<2*FAILUREloops)
2455  {
2456  blinkSpeed=2*FAILUREspeed;
2457  showFailure(3);
2458  }
2459  else if(loopCount<3*FAILUREloops)
2460  {
2461  blinkSpeed=3*FAILUREspeed;
2462  showFailure(2);
2463  }
2464  else if(loopCount<4*FAILUREloops)
2465  {
2466  blinkSpeed=4*FAILUREspeed;
2467  showFailure(1);
2468  }
2469  else if(loopCount<5*FAILUREloops)
2470  {
2471  showFailure(0);
2472  }
2473  else
2474  {
2475  // just stay stuck at the end.
2476  }
2477 }
2478 
2479 
2480 
2482 // showGrid: main function to display LED grid on Logics
2484 
2485 // byte reversal fast RAM lookup table
2486 uint8_t revlookup[16] = {
2487  0x0, 0x8, 0x4, 0xC,
2488  0x2, 0xA, 0x6, 0xE,
2489  0x1, 0x9, 0x5, 0xD,
2490  0x3, 0xB, 0x7, 0xF };
2491 // byte reversal function
2492 uint8_t rev( uint8_t n )
2493 {
2494  //This should be just as fast and it is easier to understand.
2495  //return (lookup[n%16] << 4) | lookup[n/16];
2496  return (revlookup[n&0x0F] << 4) | revlookup[n>>4];
2497 }
2498 
2499 // blank grid, to turn of all LEDs
2500 void clearGrid(byte display)
2501 {
2502  for (byte row=0; row<5; row++) LEDgrid[display][row]=0L;
2503 }
2504 
2505 // Sends LED Grid to actual LEDs on the logic displays
2506 void showGrid(byte display)
2507 {
2508  // didplay=0 Top FLD
2509  // display=1 Bottom FLD
2510  // display=2 RLD
2511 
2512  // Every 9th column of the displays maps to the 6th row of the Maxim chip
2513  unsigned char col8=0; // 9th column of FLDs and RLD, maps to 6th row of device 0
2514  unsigned char col17=0; // 18th column of RLD, goes to 6th row of RLD device 1
2515  unsigned char col26=0; // 27th column of RLD, goes to 6th row of RLD device 2
2516 
2517  // Colums 0-7 map with a byte reversal
2518 
2519  switch(display)
2520  {
2521  case 0: // Top FLD
2522 
2523  for (byte row=0; row<5; row++) // loop on first 5 rows
2524  {
2525  // extract first 8 bits, reverse, send to device 0, front chain which is top FLD
2526  lcFront.setRow(0, row, rev(LEDgrid[display][row] & 255L));
2527  // If the LED at column 8 is on, add it to the extra row (starting "left" at MSB)
2528  if ( (LEDgrid[display][row] & 1L<<8) == 1L<<8) col8 += 128>>row;
2529  }
2530  // send the 9th column (logical 8) as a 6th row or the Maxim (logical 5)
2531  lcFront.setRow(0, 5, col8);
2532 
2533  break;
2534 
2535  case 1: // Bottom FLD
2536  // Bottom FLD is upside down. So rows are inverted. Top is bottom, left is right
2537  for (byte row=0; row<5; row++) // loop on first 5 rows
2538  {
2539  // extract bits 2-9, do not reverse, send to device 1, start with device row 4 (invert top and bottom)
2540  lcFront.setRow(1, 4-row, (LEDgrid[display][row] & 255L<<1) >> 1);
2541  // If the LED at first column is on, add it to the extra row (starting "left" at MSB)
2542  // we still call it col8, but with the inverted display it really is col 0
2543  // we fill in the forward direction starting from bit 3
2544  if ( (LEDgrid[display][row] & 1L) == 1L) col8 += 8<<row;
2545  }
2546  // send the column 0 as a 6th row or the Maxim (logical row 5)
2547  lcFront.setRow(1, 5, col8);
2548  break;
2549 
2550  case 2: // RLD
2551  for (byte row=0; row<5; row++) // loop on first 5 rows
2552  {
2553  int loops = 0;
2554  for (byte dev=0; dev < 3; dev++) // RLD has 3 Maxim chip devices
2555  {
2556  // extract byte at column 0, 9 and 18, reverse byte order, and send to device
2557  lcRear.setRow(dev, row, rev( (LEDgrid[display][row] & 255L<<(9*loops)) >> (9*loops) ));
2558  loops++;
2559  }
2560  // If the LED at column 8, 17 or 26 is on, add it to the extra row (starting "left" at MSB)
2561  if ( (LEDgrid[display][row] & 1L<<8) == 1L<<8) col8 += 128>>row;
2562  if ( (LEDgrid[display][row] & 1L<<17) == 1L<<17) col17 += 128>>row;
2563  if ( (LEDgrid[display][row] & 1L<<26) == 1L<<26) col26 += 128>>row;
2564  }
2565  // send the extra columns as a 6th row or the Maxim (logical row 5)
2566  lcRear.setRow(0, 5, col8);
2567  lcRear.setRow(1, 5, col17);
2568  lcRear.setRow(2, 5, col26);
2569  break;
2570 
2571  default:
2572  break;
2573  }
2574 }
2576 
2577 
2578 
2580 // Text Display Routines
2582 
2584 // Set String
2585 void setText(byte disp, const char* message)
2586 {
2587  strncpy(logicText[disp], message, MAXSTRINGSIZE);
2588  logicText[disp][MAXSTRINGSIZE]=0; // just in case
2589 }
2590 
2592 // Latin Alphabet, put in PROGMEM so save RAM
2593 const int cA[] PROGMEM = { B00000110,
2594  B00001001,
2595  B00001111,
2596  B00001001,
2597  B00001001 };
2598 
2599 const int cB[] PROGMEM = { B00000111,
2600  B00001001,
2601  B00000111,
2602  B00001001,
2603  B00000111 };
2604 
2605 const int cC[] PROGMEM = { B00000110,
2606  B00001001,
2607  B00000001,
2608  B00001001,
2609  B00000110 };
2610 
2611 const int cD[] PROGMEM = { B0000111,
2612  B0001001,
2613  B0001001,
2614  B0001001,
2615  B0000111 };
2616 
2617 const int cE[] PROGMEM = { B00001111,
2618  B00000001,
2619  B00000111,
2620  B00000001,
2621  B00001111 };
2622 
2623 const int cF[] PROGMEM = { B00001111,
2624  B00000001,
2625  B00000111,
2626  B00000001,
2627  B00000001 };
2628 
2629 const int cG[] PROGMEM = { B00001110,
2630  B00000001,
2631  B00001101,
2632  B00001001,
2633  B00000110 };
2634 
2635 const int cH[] PROGMEM = { B00001001,
2636  B00001001,
2637  B00001111,
2638  B00001001,
2639  B00001001 };
2640 
2641 const int cI[] PROGMEM = { B00000111,
2642  B00000010,
2643  B00000010,
2644  B00000010,
2645  B00000111 };
2646 
2647 const int cJ[] PROGMEM = { B00001000,
2648  B00001000,
2649  B00001000,
2650  B00001001,
2651  B00000110 };
2652 
2653 const int cK[] PROGMEM = { B00001001,
2654  B00000101,
2655  B00000011,
2656  B00000101,
2657  B00001001 };
2658 
2659 const int cL[] PROGMEM = { B00000001,
2660  B00000001,
2661  B00000001,
2662  B00000001,
2663  B00001111 };
2664 
2665 const int cM[] PROGMEM = { B00010001,
2666  B00011011,
2667  B00010101,
2668  B00010001,
2669  B00010001 };
2670 
2671 const int cN[] PROGMEM = { B00001001,
2672  B00001011,
2673  B00001101,
2674  B00001001,
2675  B00001001 };
2676 
2677 const int cO[] PROGMEM = { B00000110,
2678  B00001001,
2679  B00001001,
2680  B00001001,
2681  B00000110 };
2682 
2683 const int cP[] PROGMEM = { B00000111,
2684  B00001001,
2685  B00000111,
2686  B00000001,
2687  B00000001 };
2688 
2689 const int cQ[] PROGMEM = { B00000110,
2690  B00001001,
2691  B00001101,
2692  B00001001,
2693  B00010110 };
2694 
2695 const int cR[] PROGMEM = { B00000111,
2696  B00001001,
2697  B00000111,
2698  B00000101,
2699  B00001001 };
2700 
2701 const int cS[] PROGMEM = { B00001110,
2702  B00000001,
2703  B00000110,
2704  B00001000,
2705  B00000111 };
2706 const int cT[] PROGMEM = { B00001111,
2707  B00000110,
2708  B00000110,
2709  B00000110,
2710  B00000110 };
2711 const int cU[] PROGMEM = { B00001001,
2712  B00001001,
2713  B00001001,
2714  B00001001,
2715  B00000110 };
2716 const int cV[] PROGMEM = { B00001001,
2717  B00001001,
2718  B00001001,
2719  B00000110,
2720  B00000110 };
2721 const int cW[] PROGMEM = { B00010001,
2722  B00010001,
2723  B00010101,
2724  B00011011,
2725  B00010001 };
2726 const int cX[] PROGMEM = { B00001001,
2727  B00001001,
2728  B00000110,
2729  B00001001,
2730  B00001001 };
2731 const int cY[] PROGMEM = { B00001001,
2732  B00001001,
2733  B00000110,
2734  B00000110,
2735  B00000110 };
2736 const int cZ[] PROGMEM = { B00001111,
2737  B00000100,
2738  B00000010,
2739  B00000001,
2740  B00001111 };
2741 const int c0[] PROGMEM = {
2742  B00001100,
2743  B00010010,
2744  B00010010,
2745  B00010010,
2746  B00001100 };
2747 //Non-letters
2748 const int c1[] PROGMEM = {
2749  B00001100,
2750  B00001010,
2751  B00001000,
2752  B00001000,
2753  B00011110 };
2754 const int c2[] PROGMEM = {
2755  B00011100,
2756  B00010010,
2757  B00001000,
2758  B00000100,
2759  B00011110 };
2760 const int c3[] PROGMEM = {
2761  B00011110,
2762  B00010000,
2763  B00011100,
2764  B00010000,
2765  B00011110 };
2766 const int c4[] PROGMEM = {
2767  B00000010,
2768  B00000010,
2769  B00001010,
2770  B00011110,
2771  B00001000 };
2772 const int c5[] PROGMEM = {
2773  B00011110,
2774  B00000010,
2775  B00001110,
2776  B00010000,
2777  B00001110 };
2778 const int c6[] PROGMEM = {
2779  B00011100,
2780  B00000010,
2781  B00001110,
2782  B00010010,
2783  B00001100 };
2784 const int c7[] PROGMEM = {
2785  B00011110,
2786  B00010000,
2787  B00001000,
2788  B00000100,
2789  B00000010 };
2790 const int c8[] PROGMEM = {
2791  B00001100,
2792  B00010010,
2793  B00001100,
2794  B00010010,
2795  B00001100 };
2796 const int c9[] PROGMEM = {
2797  B00001100,
2798  B00010010,
2799  B00011100,
2800  B00010000,
2801  B00001110 };
2802 // Heart Symbol
2803 const int ch[] PROGMEM = { B00110110,
2804  B01001001,
2805  B01000001,
2806  B00100010,
2807  B00001000 };
2808 // Tie Fighter Symbol
2809 const int ct[] PROGMEM = { B00100010,
2810  B00101010,
2811  B00110110,
2812  B00101010,
2813  B00100010 };
2814 // R2D2 Symbol
2815 const int cr[] PROGMEM = { B00001110,
2816  B00011011,
2817  B00011111,
2818  B00010101,
2819  B00010001 } ;
2820 // dash - Symbol
2821 const int cd[] PROGMEM = { B00000000,
2822  B00000000,
2823  B00001110,
2824  B00000000,
2825  B00000000 };
2826 // Film Bar Symbol for use with Leia message
2827 const int cf[] PROGMEM = { B00000100,
2828  B00000100,
2829  B00000100,
2830  B00000100,
2831  B00000100 };
2832 
2833 //Blank Symbol
2834 const int cb[] PROGMEM = { B00000000,
2835  B00000000,
2836  B00000000,
2837  B00000000,
2838  B00000000 };
2839 
2840 //upSymbol
2841 const int cu[] PROGMEM = { B00000001,
2842  B00000010,
2843  B00000100,
2844  B00001000,
2845  B00010000 };
2846 
2847 //down Symbol
2848 const int cn[] PROGMEM = { B00010000,
2849  B00001000,
2850  B00000100,
2851  B00000010,
2852  B00000001 };
2853 
2854 //Dot Symbol
2855 const int cdot[] PROGMEM = { B00000000,
2856  B00000000,
2857  B00000000,
2858  B00000000,
2859  B00000100 };
2860 
2861 
2862 // retrieve latin alphabet letter from progam memory
2863 void getLatinLetter(int* letterbitmap, char let)
2864 {
2865  // pLetter will be a pointer to program memory
2866  const int* pLetter;
2867 
2868  // get pointer to program memory from character
2869  switch (let)
2870  {
2871  case 'A': pLetter=cA; break;
2872  case 'B': pLetter=cB; break;
2873  case 'C': pLetter=cC; break;
2874  case 'D': pLetter=cD; break;
2875  case 'E': pLetter=cE; break;
2876  case 'F': pLetter=cF; break;
2877  case 'G': pLetter=cG; break;
2878  case 'H': pLetter=cH; break;
2879  case 'I': pLetter=cI; break;
2880  case 'J': pLetter=cJ; break;
2881  case 'K': pLetter=cK; break;
2882  case 'L': pLetter=cL; break;
2883  case 'M': pLetter=cM; break;
2884  case 'N': pLetter=cN; break;
2885  case 'O': pLetter=cO; break;
2886  case 'P': pLetter=cP; break;
2887  case 'Q': pLetter=cQ; break;
2888  case 'R': pLetter=cR; break;
2889  case 'S': pLetter=cS; break;
2890  case 'T': pLetter=cT; break;
2891  case 'U': pLetter=cU; break;
2892  case 'V': pLetter=cV; break;
2893  case 'W': pLetter=cW; break;
2894  case 'X': pLetter=cX; break;
2895  case 'Y': pLetter=cY; break;
2896  case 'Z': pLetter=cZ; break;
2897  //non-letters
2898  //numbers
2899  case '0': pLetter=c0; break;
2900  case '1': pLetter=c1; break;
2901  case '2': pLetter=c2; break;
2902  case '3': pLetter=c3; break;
2903  case '4': pLetter=c4; break;
2904  case '5': pLetter=c5; break;
2905  case '6': pLetter=c6; break;
2906  case '7': pLetter=c7; break;
2907  case '8': pLetter=c8; break;
2908  case '9': pLetter=c9; break;
2909  //special characters
2910  case '*': pLetter=ch; break;
2911  case '#': pLetter=ct; break;
2912  case '@': pLetter=cr; break;
2913  case '-': pLetter=cd; break;
2914  case '|': pLetter=cf; break;
2915  case '.': pLetter=cdot; break;
2916  //whitespace
2917  case ' ': pLetter=cb; break;
2918  case '<': pLetter=cu; break;
2919  case '>': pLetter=cn; break;
2920  default : pLetter=cb; break;
2921  break;
2922  }
2923 
2924  // move data back from program memory to RAM
2925  for(byte i=0; i<5; i++)
2926  {
2927  letterbitmap[i]=(int)pgm_read_word(&(pLetter[i]));
2928  }
2929 }
2930 
2931 
2932 // Aurabesh Alphabet, in PROGMEM
2933 const int a2[] PROGMEM = {
2934  B00001111,
2935  B00001001,
2936  B00000100,
2937  B00001001,
2938  B00001111 };
2939 const int aA[] PROGMEM = {
2940  B00010001,
2941  B00001111,
2942  B00000000,
2943  B00001111,
2944  B00010001 };
2945 const int aB[] PROGMEM = {
2946  B00001110,
2947  B00010001,
2948  B00001110,
2949  B00010001,
2950  B00001110 };
2951 const int aC[] PROGMEM = {
2952  B00000001,
2953  B00000001,
2954  B00000100,
2955  B00010000,
2956  B00010000 };
2957 const int aD[] PROGMEM = {
2958  B00011111,
2959  B00001000,
2960  B00000111,
2961  B00000010,
2962  B00000001 };
2963 const int aE[] PROGMEM = {
2964  B00011001,
2965  B00011001,
2966  B00011001,
2967  B00010110,
2968  B00010100 };
2969 const int aF[] PROGMEM = {
2970  B00010000,
2971  B00001010,
2972  B00000111,
2973  B00000011,
2974  B00011111 };
2975 const int aG[] PROGMEM = {
2976  B00011101,
2977  B00010101,
2978  B00010001,
2979  B00001001,
2980  B00000111 };
2981 const int aH[] PROGMEM = {
2982  B00011111,
2983  B00000000,
2984  B00001110,
2985  B00000000,
2986  B00011111 };
2987 const int aI[] PROGMEM = {
2988  B00000100,
2989  B00000110,
2990  B00000100,
2991  B00000100,
2992  B00000100 };
2993 const int aJ[] PROGMEM = {
2994  B00010000,
2995  B00011000,
2996  B00001111,
2997  B00000100,
2998  B00000011 };
2999 const int aK[] PROGMEM = {
3000  B00011111,
3001  B00010000,
3002  B00010000,
3003  B00010000,
3004  B00011111 };
3005 const int aL[] PROGMEM = {
3006  B00010000,
3007  B00010001,
3008  B00010010,
3009  B00010100,
3010  B00011000 };
3011 const int aM[] PROGMEM = {
3012  B00011100,
3013  B00010010,
3014  B00000001,
3015  B00010001,
3016  B00011111 };
3017 const int aN[] PROGMEM = {
3018  B00001010,
3019  B00010101,
3020  B00010101,
3021  B00010011,
3022  B00010010 };
3023 const int aO[] PROGMEM = {
3024  B00000000,
3025  B00001110,
3026  B00010001,
3027  B00010001,
3028  B00011111 };
3029 const int aP[] PROGMEM = {
3030  B00010110,
3031  B00010101,
3032  B00010001,
3033  B00010001,
3034  B00011110 };
3035 const int aQ[] PROGMEM = {
3036  B00011111,
3037  B00010001,
3038  B00000001,
3039  B00000001,
3040  B00000111 };
3041 const int aR[] PROGMEM = {
3042  B00011111,
3043  B00001000,
3044  B00000100,
3045  B00000010,
3046  B00000001 };
3047 const int aS[] PROGMEM = {
3048  B00010000,
3049  B00010010,
3050  B00010101,
3051  B00011010,
3052  B00010100 };
3053 const int aT[] PROGMEM = {
3054  B00001111,
3055  B00000010,
3056  B00000010,
3057  B00000010,
3058  B00000010 };
3059 const int aU[] PROGMEM = {
3060  B00000100,
3061  B00000100,
3062  B00010101,
3063  B00001110,
3064  B00000100 };
3065 const int aV[] PROGMEM = {
3066  B00010001,
3067  B00001010,
3068  B00000100,
3069  B00000100,
3070  B00000100 };
3071 const int aW[] PROGMEM = {
3072  B00011111,
3073  B00010001,
3074  B00010001,
3075  B00010001,
3076  B00011111 };
3077 const int aX[] PROGMEM = {
3078  B00000100,
3079  B00001010,
3080  B00010001,
3081  B00010001,
3082  B00011111 };
3083 const int aY[] PROGMEM = {
3084  B00010011,
3085  B00010101,
3086  B00001010,
3087  B00001010,
3088  B00000100 };
3089 const int aZ[] PROGMEM = {
3090  B00010110,
3091  B00010101,
3092  B00010000,
3093  B00010001,
3094  B00011111 };
3095 const int aZZ[] PROGMEM = {
3096  B00000000,
3097  B00000000,
3098  B00000000,
3099  B00000000,
3100  B00000000 };
3101 
3102 // retrieve latin alphabet letter from progam memory
3103 void getAurabeshLetter(int* letterbitmap, char let)
3104 {
3105  // pLetter will be a pointer to program memory
3106  const int* pLetter;
3107 
3108  // get pointer to program memory from character
3109  switch (let)
3110  {
3111  case '2': pLetter=a2; break;
3112  case 'A': pLetter=aA; break;
3113  case 'B': pLetter=aB; break;
3114  case 'C': pLetter=aC; break;
3115  case 'D': pLetter=aD; break;
3116  case 'E': pLetter=aE; break;
3117  case 'F': pLetter=aF; break;
3118  case 'G': pLetter=aG; break;
3119  case 'H': pLetter=aH; break;
3120  case 'I': pLetter=aI; break;
3121  case 'J': pLetter=aJ; break;
3122  case 'K': pLetter=aK; break;
3123  case 'L': pLetter=aL; break;
3124  case 'M': pLetter=aM; break;
3125  case 'N': pLetter=aN; break;
3126  case 'O': pLetter=aO; break;
3127  case 'P': pLetter=aP; break;
3128  case 'Q': pLetter=aQ; break;
3129  case 'R': pLetter=aR; break;
3130  case 'S': pLetter=aS; break;
3131  case 'T': pLetter=aT; break;
3132  case 'U': pLetter=aU; break;
3133  case 'V': pLetter=aV; break;
3134  case 'W': pLetter=aW; break;
3135  case 'X': pLetter=aX; break;
3136  case 'Y': pLetter=aY; break;
3137  case 'Z': pLetter=aZ; break;
3138  case ' ': pLetter=aZZ; break;
3139  default : pLetter=aZZ; break;
3140  break;
3141  }
3142 
3143  // move data back from program memory to RAM
3144  for(byte i=0; i<5; i++)
3145  {
3146  letterbitmap[i]=(int)pgm_read_word(&(pLetter[i]));
3147  }
3148 }
3149 
3150 // Draws in a letter on the LED grid
3151 // shift=0 draws starting from the left edge (column 0)
3152 // shift>0 slide letter further towards the right
3153 // shift<0 slide letter further towards the left (becomes only partly visible)
3154 void drawLetter(byte display, char let, int shift)
3155 {
3156 
3157  // return immediately if the letter won't show
3158  if(shift < -LETTERWIDTH || shift>27) return;
3159 
3160  // allocate RAM space for the bitmap
3161  int letterBitmap[5];
3162 
3163  // retrieve letter bitmap from program memory to RAM using either alphabet
3164  switch(alphabetType[display])
3165  {
3166  case 0:
3167  getLatinLetter(letterBitmap, let);
3168  break;
3169  case 1:
3170  getAurabeshLetter(letterBitmap, let);
3171  break;
3172  default:
3173  getLatinLetter(letterBitmap, let);
3174  break;
3175  }
3176 
3177  //loop thru rows of the letter
3178  // shift=0 draws starting from the left edge (column 0)
3179  // shift>0 slide letter further towards the right
3180  // shift<0 slide letter further towards the left (becomes only partly visible)
3181  for (byte i=0; i<5; i++)
3182  {
3183  if (shift>0) //positive shift means letter is slid to the right on the display
3184  LEDgrid[display][i] |= ((long)letterBitmap[i]) << shift;
3185  else //negative shift means letter is slid to the left so that only part of it is visible
3186  LEDgrid[display][i] |= ((long)letterBitmap[i]) >> -shift;
3187  }
3188 }
3189 
3190 
3191 // Scrolls the given text string on the given display
3192 // Call repeatedly to keep the text scrolling.
3193 // A scrolling count global tells you how many times the string has scrolled entirely
3194 // There is no reset of the original position, should be accomplished elsewhere
3195 // (9, 9 and 27 respectively)
3196 void scrollText(byte display, char text[])
3197 {
3198  static unsigned long previousTextScroll[3];
3199 
3200  // wait until next update cycle
3201  unsigned long currentMillis = millis();
3202  if((currentMillis - previousTextScroll[display]) < SCROLLspeed) return;
3203  previousTextScroll[display] = currentMillis;
3204 
3205  // LED grid to all off
3206  clearGrid(display);
3207 
3208  // draw all letters in the grid, scrolled according to the global scrollPosition[display]
3209  // each letter is moved 5 pixels from the next letter.
3210  // Positive scroll means moves towards the right
3211  // So scrollPosition should start at the last column and be decremented
3212  for (unsigned int i=0; i<strlen(text); i++)
3213  {
3214  int shift=i*LETTERWIDTH + scrollPositions[display];
3215  //if(shift > -(LETTERWIDTH+1) && shift<28)
3216  {
3217  drawLetter(display, text[i], shift);
3218  }
3219  }
3220 
3221  // this moves the text one step to the left, it will eventually become negative
3222  // and some text will start to disappear
3223  scrollPositions[display]--;
3224 
3225  // if the whole text is off screen to the left
3226  if (scrollPositions[display] < -LETTERWIDTH*(int)strlen(text))
3227  {
3228  // resets the scroll to just off screen to the right
3229  if (display==2) scrollPositions[display]=27;
3230  else scrollPositions[display]=9;
3231 
3232  // increment global scroll count
3233  // Right now this is used once at startup to stop calling this in the loop
3234  // once the startup text has scrolled once
3235  // warning there is no reset to this
3236  textScrollCount[display]++;
3237  }
3238 
3239  // show text on logics
3240  showGrid(display);
3241 }
3242 
3243 
3244 // =======================================================================================
3245 
3246 
3247 #endif
TeecesRearLogics::kNormalVal
@ kNormalVal
Definition: TeecesLogics.h:256
CommandEvent
Base class for all command enabled devices. CommandEvent::handleCommand() is called for each device e...
Definition: CommandEvent.h:17
TeecesBFLD
TeecesFrontLogics TeecesBFLD
Definition: TeecesLogics.h:751
TeecesFrontLogics::kFlash
@ kFlash
Definition: TeecesLogics.h:628
TeecesFrontLogics::kNormal
@ kNormal
Definition: TeecesLogics.h:625
ReelTwo.h
SetupEvent.h
AnimatedEvent
Base class for all animated devices. AnimatedEvent::animate() is called for each device once through ...
Definition: AnimatedEvent.h:18
SetupEvent
Base class for all devices that require setup that cannot happen in the constructor....
Definition: SetupEvent.h:15
TeecesFrontLogics::numColumns
int numColumns() const
Definition: TeecesLogics.h:408
LedControlMAX7221.h
TeecesRearLogics::selectEffect
void selectEffect(long inputNum)
Definition: TeecesLogics.h:66
AnimatedEvent.h
TeecesRearLogics::kNormal
@ kNormal
Definition: TeecesLogics.h:261
TeecesRLD
TeecesRearLogics TeecesRLD
Definition: TeecesLogics.h:735
TeecesRearLogics::setup
virtual void setup() override
Subclasses must implement this function to perform any necessary setup that cannot happen in the cons...
Definition: TeecesLogics.h:57
TeecesFrontLogics::kLife
@ kLife
Definition: TeecesLogics.h:633
TeecesRearLogics::kLife
@ kLife
Definition: TeecesLogics.h:269
TeecesFrontLogics::kToggle
@ kToggle
Definition: TeecesLogics.h:627
TeecesFrontLogics
Teeces Front Logics Device.
Definition: TeecesLogics.h:382
TeecesRearLogics::kHorizontalScan
@ kHorizontalScan
Definition: TeecesLogics.h:266
TeecesRearLogics::kSequence
@ kSequence
Definition: TeecesLogics.h:268
TeecesTFLD
TeecesFrontLogics TeecesTFLD
Definition: TeecesLogics.h:743
TeecesFrontLogics::setup
virtual void setup() override
Subclasses must implement this function to perform any necessary setup that cannot happen in the cons...
Definition: TeecesLogics.h:413
TeecesFrontLogics::kVerticalScan
@ kVerticalScan
Definition: TeecesLogics.h:631
TeecesFrontLogics::animate
virtual void animate() override
Subclasses must implement this function to run through a single frame of animation/activity.
Definition: TeecesLogics.h:449
TeecesFrontLogics::handleCommand
virtual void handleCommand(const char *cmd) override
Command Prefix: FL (top and bottom logics) Command Prefix: TL (top logics) Command Prefix: BL (bottom...
Definition: TeecesLogics.h:433
TeecesRearLogics::kSolid
@ kSolid
Definition: TeecesLogics.h:262
TeecesFrontLogics::kAlert
@ kAlert
Definition: TeecesLogics.h:629
TeecesRearLogics::handleCommand
virtual void handleCommand(const char *cmd) override
Subclasses should implement this function to process commands specific to their device.
Definition: TeecesLogics.h:71
TeecesRearLogics::kToggle
@ kToggle
Definition: TeecesLogics.h:263
TeecesFrontLogics::selectEffect
void selectEffect(long inputNum)
Definition: TeecesLogics.h:422
OFF
#define OFF
Definition: ButtonController.h:24
TeecesRearLogics::animate
virtual void animate() override
Subclasses must implement this function to run through a single frame of animation/activity.
Definition: TeecesLogics.h:85
TeecesFrontLogics::kSequence
@ kSequence
Definition: TeecesLogics.h:632
TeecesRearLogics::numRows
int numRows() const
Definition: TeecesLogics.h:47
TeecesRearLogics::kFlash
@ kFlash
Definition: TeecesLogics.h:264
TeecesFrontLogics::kHorizontalScan
@ kHorizontalScan
Definition: TeecesLogics.h:630
TeecesRearLogics::numColumns
int numColumns() const
Definition: TeecesLogics.h:52
TeecesRearLogics
Teeces Rear Logics Device.
Definition: TeecesLogics.h:26
TeecesRearLogics::kAlert
@ kAlert
Definition: TeecesLogics.h:265
TeecesFrontLogics::kSolid
@ kSolid
Definition: TeecesLogics.h:626
TeecesFrontLogics::kNormalVal
@ kNormalVal
Definition: TeecesLogics.h:620
CommandEvent.h
TeecesFrontLogics::numRows
int numRows() const
Definition: TeecesLogics.h:403
TeecesRearLogics::TeecesRearLogics
TeecesRearLogics(LedControl &ledControl)
Definition: TeecesLogics.h:29
TeecesFrontLogics::TeecesFrontLogics
TeecesFrontLogics(LedControl &ledControl)
Definition: TeecesLogics.h:385
atoi
int atoi(const char *cmd, int numdigits)
Definition: StringUtils.h:4
SizeOfArray
#define SizeOfArray(arr)
Definition: ReelTwo.h:213
TeecesRearLogics::kVerticalScan
@ kVerticalScan
Definition: TeecesLogics.h:267