27浏览
查看: 27|回复: 2

[项目] 【Arduino 动手做】100颗WS2812 LED 构建了一个 10x10 LED 魔方

[复制链接]
不久前,我用其中一些 WS2812 LED 构建了一个 10x10 LED-Coffetable,但即使可以在连接智能手机的情况下玩老式游戏 Snake,我也想要更特别的东西。所以我决定再给它放几个 LED,排列成一个立方体,以便在创建动画和游戏时获得更多可能性,这就是我们:RGB-Brick。

这里有一些图片和一个小视频,其中包含 Brick 的一些功能,包括大量动画、用于加热气氛的(正在开发中的)火焰、音乐可视化器以及游戏 Snake 和 Tetris。

以下是您需要的所有材料的清单,其中一些不是必需的,其他一些可以根据您的喜好进行交换:
500 个 WS2812 LED 30 像素/米
5V 30A 电源
小小 3.2
ESP8266 wifi-modul
一些木头:
1x:27.2 厘米 x 27.2 厘米 x 1.0 厘米,用于盖子
2x:29.6 厘米 x 27.2 厘米 x 1.0 厘米,用于大侧板
2x:25.2 厘米 x 29.6 厘米 x 1.0 厘米,用于小侧板
1x:34.0 厘米 x 34.0 厘米 x 1.9 厘米,用于底部
8x:34.0cm x 4.6cm x 0.3cm,用于 LED 网格的边缘
100x:34.0 厘米 x 3.3 厘米 x 0.3 厘米,用于 LED 网格
一些亚克力玻璃片:
1x:34.0 厘米 x 34.0 厘米 x 0.3 厘米
2x:34.0 厘米 x 36.3 厘米 x 0.3 厘米
2x:34.6 厘米 x 36.3 厘米 x 0.3 厘米
1x:10.0cm x 7.5cm x 0.3cm(可选,用于终端)
Teensy 音频板(可选)
电线、稳压器、电缆夹、蜂鸣器、按钮、温度传感器(可选)
木头胶、亚克力玻璃胶、螺丝等小物
如果你想在立方体的底部有一个端子(除了电源插孔之外,它是可选的):
230V 电源插孔
230V 开关
音频插孔
USB 延长线

首先,我们将构建木箱和 LED 网格。立方体的尺寸由 LED 灯条上像素的距离指定。在这种情况下,像素的距离为 3,4 厘米,因此立方体必须为 34 x 34 x 34 厘米。使用此尺寸将节省大量时间,因为您无需在每个像素后切割条带并通过小电缆将其重新组装在一起。

所有产品都配有一些木胶。你必须正常工作,因为亚克力外壳与木箱的顶部完美匹配。有你身边的一些志愿者会变得容易得多,或者像我一样使用框架张紧器。

网格的边缘和网格本身由高密度纤维板 (HDF) 制成。使用台锯是最好的选择,因为您必须切割 100 多件。您可以在上图中找到尺寸。网格需要每 0,3 厘米有一个小间隙(约 3,4 厘米),以便将 x 和 y 壁架放在一起。完成后,您可以将边缘放在立方体上并用大量木胶固定它们。这有点困难,特别是因为它们的角度应该接近 45 度左右。在将网格连接到立方体之前,您必须添加 LED 灯条。

侧面的 LED 灯条绕立方体转一圈,因此切割 10 个长度为 40 像素的灯条。对于立方体顶部的 LED,切割 10 个长度为 10 像素的条带。小心地根据条带上的箭头正确对齐条带。一旦你从立方体上取下胶条,它就不会像第一次那样保持住。
电源用一些螺丝固定在内部的侧面。LED 的电源线通过每个 LED 灯条附近的一些小孔进入盒子。
控制器由一个 Teensy 3.2、一个 ESP8266 和 Teensy 音频板组成,运行立方体不需要。DHT11 仅用于检查立方体内部的温度,但经过大约几个小时的多次测试,我可以说您可以将其省略。
在终端上,您可以找到电源插孔和电源开关(当我意识到这不是切换的最佳位置时,为时已晚)。USB 插孔用于对 Teensy 进行编程。音频输入转到 Teensy 音频板,以便根据音乐执行 LED。所有这一切都汇集在一个由两个铝型材固定的小型芳基玻璃上。刚在车库里找到这个,你可以使用任何你想要的东西,因为它被底部木板覆盖,对立方体的外观没有贡献。
请注意,一个 LED 使用 60mA,总共是 30A!连接它们时要小心!在将电路连接到电源之前,您必须验证所有电路!

完成之前的所有步骤后,就可以将所有部分组合在一起了。当您还没有合并 LED 网格时,现在是时候这样做了。我不会将网格粘在立方体上,因为不需要它,如果 LED 损坏,您可以毫无问题地更换它,但是您需要两只以上的手才能将五个网格固定到立方体上并将其放入亚克力盖中。最后但并非最不重要的一点是,您可以将底部的木板拧到立方体上。盖子通过八个非常小的螺丝固定在底部木板上。

Teensy 上的草图基于 FastLED 库,其中包括几个基本动画。将 RGBLEDS 库包添加到您的草图中,可以带来强大的矩阵代数,用于显示文本和“精灵”以及大量示例草图。如果您还想玩俄罗斯方块,请参考 jollifactory 的 instructable,即使它只使用双色矩阵。
智能手机应用程序基于 David Eickhoff 的 NetIO,它有一个非常好的文档。使用 NetIO-UI-Designer,您可以创建自己的用户界面,其中包含按钮、滑块、标签等。您可以在设计器中选择传出消息的协议。就我而言,我选择了最简单的一个 - UDP。这些消息通过我的家庭网络发送到 ESP8266,Teensy 将评估内容并处理指定的命令。您可以使用附件开始创建自己的界面,或者只使用您选择的应用程序。

【Arduino 动手做】100颗WS2812 LED 构建了一个 10x10 LED 魔方图1

【Arduino 动手做】100颗WS2812 LED 构建了一个 10x10 LED 魔方图3

【Arduino 动手做】100颗WS2812 LED 构建了一个 10x10 LED 魔方图2

【Arduino 动手做】100颗WS2812 LED 构建了一个 10x10 LED 魔方图4

【Arduino 动手做】100颗WS2812 LED 构建了一个 10x10 LED 魔方图5

【Arduino 动手做】100颗WS2812 LED 构建了一个 10x10 LED 魔方图6

【Arduino 动手做】100颗WS2812 LED 构建了一个 10x10 LED 魔方图8

【Arduino 动手做】100颗WS2812 LED 构建了一个 10x10 LED 魔方图7

【Arduino 动手做】100颗WS2812 LED 构建了一个 10x10 LED 魔方图9

【Arduino 动手做】100颗WS2812 LED 构建了一个 10x10 LED 魔方图11

【Arduino 动手做】100颗WS2812 LED 构建了一个 10x10 LED 魔方图10

驴友花雕  中级技神
 楼主|

发表于 16 小时前

【Arduino 动手做】100颗WS2812 LED 构建了一个 10x10 LED 魔方

项目代码

  1. #define FASTLED_ALLOW_INTERRUPTS 0
  2. #include <FastLED.h>
  3. #include <LEDMatrix.h>
  4. #include <LEDText.h>
  5. #include <FontMatrise.h>
  6. #include <LEDSprites.h>
  7. #include <EEPROM.h>
  8. #include <Wire.h>
  9. #include <SD.h>
  10. #include <SPI.h>
  11. #include <Audio.h>
  12. //dht -> 16 (A2)
  13. // GUItool: begin automatically generated code
  14. AudioPlaySdWav           wavEffects;
  15. AudioInputI2S            audioInput;
  16. AudioOutputI2S           audioOutput;
  17. AudioMixer4              mixerInput;
  18. AudioAnalyzeFFT1024      fft;
  19. AudioAnalyzePeak         peak1;
  20. AudioConnection          patchCord5(wavEffects, 0, audioOutput, 1);
  21. AudioConnection          patchCord6(wavEffects, 1, audioOutput, 0);
  22. AudioConnection          patchCord7(audioInput, 0, mixerInput, 0);
  23. AudioConnection          patchCord8(audioInput, 1, mixerInput, 1);
  24. AudioConnection          patchCord9(mixerInput, fft);
  25. AudioConnection          patchCord10(mixerInput, peak1);
  26. AudioControlSGTL5000     audioShield;
  27. const int nSoundeffects = 10;
  28. const char * soundeffects[10] = { "COIN.WAV", "COIN2.WAV", "BOSSFIGHT.WAV", "BONUS.WAV", "JUMP.WAV",
  29.                                   "PITCH.WAV",  "GAMEOVER.WAV", "HUNGERGAMES.WAV", "LEVELUP.WAV", "SUCCESS.WAV"
  30.                                 };
  31. boolean playeffects = false;
  32. float fft_values[40];
  33. int fft_show[40];
  34. float peak;
  35. int peak_show;
  36. byte xx1, yy1, bb1;
  37. float vol = 0.5;
  38. uint16_t gradient = 0;
  39. uint8_t volume = 0, last = 0;
  40. float maxVol = 15;
  41. float avgVol = 0;
  42. float avgBump = 0;
  43. bool bump = false;
  44. #define LED_TOTAL 15
  45. #define LED_HALF  7
  46. const int nMusicVisualiser = 2;
  47. int musicVisualiser = 1;
  48. #define esp Serial1
  49. #define COLOR_ORDER            GRB
  50. #define CHIPSET                WS2811
  51. #define LED_PIN_TOP            3
  52. #define MATRIX_WIDTH_TOP       10
  53. #define MATRIX_HEIGHT_TOP      10
  54. #define MATRIX_TYPE_TOP        HORIZONTAL_ZIGZAG_MATRIX
  55. #define LED_PIN_BOTTOM         20
  56. #define MATRIX_WIDTH_BOTTOM    40
  57. #define MATRIX_HEIGHT_BOTTOM   10
  58. #define MATRIX_TYPE_BOTTOM     HORIZONTAL_MATRIX
  59. cLEDMatrix < -MATRIX_WIDTH_TOP, MATRIX_HEIGHT_TOP, MATRIX_TYPE_TOP > matrix_top;
  60. cLEDMatrix < -MATRIX_WIDTH_BOTTOM, MATRIX_HEIGHT_BOTTOM, MATRIX_TYPE_BOTTOM > matrix_bottom;
  61. //SNAKE
  62. //wie lang kann die Snake maximal werden
  63. const int anzahlPixel = 500;
  64. byte SnakeItems = 0;
  65. byte oldSnakeItems = 0;
  66. //groesse des Spielfeldes
  67. #define BaneGridXmax 50
  68. #define BaneGridYmax 10
  69. //Snakeitems
  70. byte oldSnakeItemPosX[anzahlPixel];
  71. byte oldSnakeItemPosY[anzahlPixel];
  72. byte SnakeItemPosX[anzahlPixel];
  73. byte SnakeItemPosY[anzahlPixel];
  74. //Position des Apples
  75. byte ApplePosX;
  76. byte ApplePosY;
  77. //Farben der 3 Arten
  78. #define BLANK 0
  79. #define SNAKE 1
  80. #define APPLE 2
  81. //Spielfeld
  82. byte Playfield[BaneGridXmax + 1][BaneGridYmax + 1];
  83. byte SnakeHeadID = 0;
  84. byte SnakeBackID = 0;
  85. byte AppleCount = 0;
  86. //define Richtungen
  87. #define SNAKE_RIGHT 0
  88. #define SNAKE_LEFT 1
  89. #define SNAKE_UP 2
  90. #define SNAKE_DOWN 3
  91. //Laufrichtungen
  92. byte movingDirection = SNAKE_RIGHT;
  93. byte snakeDirection = SNAKE_RIGHT;
  94. byte AddSnakeItem = 0;
  95. //eins addieren wenn apple gefunden
  96. byte SnakeItemsToAddAtApple = 1;
  97. //snakespeed
  98. #define SNAKESPEED_BEGIN 200
  99. int snakespeed = SNAKESPEED_BEGIN;
  100. int snakespeedmax = 20;
  101. //spiel laeuft
  102. boolean snakerun = false;
  103. boolean snakeOntop = false;
  104. unsigned long SnakeScore = 0;
  105. unsigned long SnakeHighscore = 0;
  106. const unsigned int ScoreEEPROMaddress = 100;
  107. //Tetris
  108. #define TETRIS_WIDTH 10
  109. #define TETRIS_HIGHT 20
  110. boolean tetrisrun = false;
  111. int tetrisspeed = 400;
  112. byte PlayfieldTetris[TETRIS_WIDTH][TETRIS_HIGHT];
  113. byte Block[TETRIS_WIDTH][TETRIS_HIGHT + 2];
  114. byte Pile[TETRIS_WIDTH][TETRIS_HIGHT];
  115. int currentblock = 0;
  116. int currentrotation = 0;
  117. boolean blockflying = false;
  118. const struct CRGB TetrisColor[] = {CRGB(0, 0, 0), CRGB(0, 255, 255), CRGB(0, 0, 255), CRGB(255, 165, 0), CRGB(255, 255, 0),
  119.         CRGB(50, 205, 50), CRGB(255, 0, 255), CRGB(255, 0, 0), CRGB(255, 255, 255)
  120. };
  121. #define TETRIS_BLACK 0
  122. #define TETRIS_WHITE 8
  123. byte blockcolor[] = {0, 1, 2, 3, 4, 5, 6, 7};
  124. int TetrisScore = 0;
  125. int TetrisHighScore = 0;
  126. boolean doSnake = false;
  127. boolean doTetris = false;
  128. boolean doText = false;
  129. boolean doAnimation = false;
  130. boolean doFire = false;
  131. boolean doNoise = false;
  132. boolean doMusic = false;
  133. const int nAnimation = 4;
  134. int animation = 1;
  135. boolean NoisePalette = false;
  136. int paletteH[4] = {0};
  137. int paletteS[4] = {0};
  138. int paletteV[4] = {0};
  139. int wait = 50;
  140. int Brightness = 200, Saturation = 255;
  141. cLEDText scrollingtext;
  142. #define MAX_BUFFER  100
  143. unsigned char TextBuffer[MAX_BUFFER];
  144. static uint16_t x;
  145. static uint16_t y;
  146. static uint16_t z;
  147. uint16_t speed = 20;
  148. uint16_t scale = 30;
  149. uint8_t noise[MATRIX_WIDTH_BOTTOM + MATRIX_WIDTH_TOP][MATRIX_HEIGHT_BOTTOM];
  150. CRGBPalette16 currentPalette( PartyColors_p );
  151. uint8_t colorLoop = 1;
  152. int palette = 0;
  153. int maxpalette = 11;
  154. CRGBPalette16 gPal;
  155. #define SQUARE_WIDTH    7
  156. #define SQUARE_HEIGHT   7
  157. const uint8_t SquareData1[] =
  158. {
  159.   B8_2BIT(11111110),
  160.   B8_2BIT(12222210),
  161.   B8_2BIT(12333210),
  162.   B8_2BIT(12333210),
  163.   B8_2BIT(12333210),
  164.   B8_2BIT(12222210),
  165.   B8_2BIT(11111110)
  166. };
  167. const uint8_t SquareMask1[] =
  168. {
  169.   B8_2BIT(11111110),
  170.   B8_2BIT(11111110),
  171.   B8_2BIT(11111110),
  172.   B8_2BIT(11111110),
  173.   B8_2BIT(11111110),
  174.   B8_2BIT(11111110),
  175.   B8_2BIT(11111110)
  176. };
  177. cLEDSprites Sprites(&matrix_bottom);
  178. #define MAX_SQUARES  8
  179. #define NUM_COLS  3
  180. cSprite Shape1[MAX_SQUARES];
  181. struct CRGB ColTabs[MAX_SQUARES][NUM_COLS];
  182. int NumSquares;
  183. #define SHAPE_WIDTH    5
  184. #define SHAPE_HEIGHT   5
  185. const uint8_t ShapeData2[] =
  186. {
  187.   B8_1BIT(00100000),
  188.   B8_1BIT(01110000),
  189.   B8_1BIT(11111000),
  190.   B8_1BIT(01110000),
  191.   B8_1BIT(00100000),
  192. };
  193. struct CRGB ColTable[1] = { CRGB(64, 128, 255) };
  194. cSprite Shape2(SHAPE_WIDTH, SHAPE_HEIGHT, ShapeData2, 1, _1BIT, ColTable, ShapeData2);
  195. #define COOLING  55
  196. #define SPARKING 120
  197. bool gReverseDirection = false;
  198. uint8_t hue;
  199. void setup() {
  200.   AudioMemory(48);
  201.   Serial.begin(9600);
  202.   delay(500);
  203.   FastLED.addLeds<CHIPSET, LED_PIN_TOP, COLOR_ORDER>(matrix_top[0], matrix_top.Size());
  204.   FastLED.addLeds<CHIPSET, LED_PIN_BOTTOM, COLOR_ORDER>(matrix_bottom[0], matrix_bottom.Size());
  205.   FastLED.clear();
  206.   FastLED.show();
  207.   delay(500);
  208.   gPal = HeatColors_p;
  209.   audioShield.enable();
  210.   audioShield.inputSelect(AUDIO_INPUT_LINEIN);
  211.   mixerInput.gain(0, vol);
  212.   mixerInput.gain(1, vol);
  213.   audioShield.volume(0.5);
  214.   fft.windowFunction(AudioWindowHanning1024);
  215.   SPI.setMOSI(7);
  216.   SPI.setSCK(14);
  217.   if (!(SD.begin(10))) while (1) delay(500);
  218.   x = random16();
  219.   y = random16();
  220.   z = random16();
  221.   esp.begin(9600);
  222.   esp.setTimeout(100);
  223. }//END SETUP
  224. long movingtime = millis();
  225. long t2 = millis();
  226. long lev = millis();
  227. void loop() {
  228.   random16_add_entropy( random());
  229.   if (doText) Text();
  230.   if (doSnake) Snake();
  231.   if (doAnimation) Animation(animation);
  232.   if (doFire) Fire();
  233.   if (doNoise) Noise();
  234.   if (doMusic) Music();
  235.   if (doTetris) Tetris();
  236.   if (esp.available() > 0) {
  237.     String in = "";
  238.     while (esp.available()) {
  239.       in += char(esp.read());
  240.       delay(10);
  241.     }
  242.     handleIncoming(in);
  243.   }
  244. }//END LOOP
  245. void handleIncoming(String incoming) {
  246.   if (incoming.indexOf(/*IP*/) > -1) {
  247.     //ESP has start
  248.   }
  249.   if (incoming.indexOf("Animation") > -1) {
  250.     if (doAnimation) {
  251.       Sprites.RemoveAllSprites();
  252.       increaseMode(animation, nAnimation);
  253.     }
  254.     else {
  255.       resetDos();
  256.       doAnimation = true;
  257.     }
  258.     InitAnimation(animation);
  259.   }
  260.   if (incoming.indexOf("Fire") > -1) {
  261.     resetDos();
  262.     doFire = true;
  263.   }
  264.   if (incoming.indexOf("Noise") > -1) {
  265.     if (doNoise) {
  266.       ChangePaletteAndSettings();
  267.     }
  268.     else {
  269.       resetDos();
  270.       doNoise = true;
  271.     }
  272.   }
  273.   if (incoming.indexOf("NoisePalette") > -1) {
  274.     if (doNoise) {
  275.       NoisePalette = true;
  276.     }
  277.   }
  278.   if (incoming.indexOf("Music") > -1) {
  279.     if (doMusic) {
  280.       increaseMode(musicVisualiser, nMusicVisualiser);
  281.     }
  282.     else {
  283.       resetDos();
  284.       doMusic = true;
  285.     }
  286.   }
  287.   if (incoming.indexOf("Volume") > -1) {
  288.     int v = map(getValue(incoming), 0, 100, 10, 1);
  289.     vol = 1.0 / ((float) v);
  290.     mixerInput.gain(0, vol);
  291.     mixerInput.gain(1, vol);
  292.   }
  293.   if (incoming.indexOf("Text") > -1) {
  294.     StringToTextBuffer(getText(incoming), 0);
  295.     resetDos();
  296.     doText = true;
  297.   }
  298.   if (incoming.indexOf("Saturation") > -1) {
  299.     Saturation = map(getValue(incoming), 0, 100, 0, 255);
  300.   }
  301.   if (incoming.indexOf("Brightness") > -1) {
  302.     Brightness = map(getValue(incoming), 0, 100, 0, 255);
  303.     FastLED.setBrightness(Brightness);
  304.     FastLED.show();
  305.   }
  306.   if (incoming.indexOf("Speed") > -1) {
  307.     wait = map(getValue(incoming), 0, 100, 100, 0);
  308.   }
  309.   if (incoming.indexOf("Aus") > -1) {
  310.     Sprites.RemoveAllSprites();
  311.     resetDos();
  312.   }
  313.   handleNoise(incoming);
  314.   handleGame(incoming);
  315. }//end handleIncoming()
  316. void handleGame(String in) {
  317.   if (doSnake) handleSnake(in);
  318.   else if (doTetris) handleTetris(in);
  319.   if (in.indexOf("SnakeTop") > -1) {
  320.     resetDos();
  321.     doSnake = true;
  322.     NewGameSnake();
  323.   }
  324.   if (in.indexOf("TetrisTop") > -1) {
  325.     resetDos();
  326.     doTetris = true;
  327.     newGameTetris();
  328.   }
  329. }//end handleGame()
  330. void resetDos() {
  331.   FastLED.clear();
  332.   FastLED.show();
  333.   doSnake = false;
  334.   snakerun = false;
  335.   doText = false;
  336.   doAnimation = false;
  337.   doFire = false;
  338.   doNoise = false;
  339.   doMusic = false;
  340.   doTetris = false;
  341.   NoisePalette = false;
  342. }//end resetDos()
  343. String getText(String str) {
  344.   int sep1 = str.indexOf("-");
  345.   int endsep = str.indexOf("-", sep1 + 1);
  346.   return str.substring(sep1 + 1, endsep);
  347. }//end Text()
  348. int getValue(String str) {
  349.   int sep1 = str.indexOf("-");
  350.   int endsep = str.indexOf("-", sep1 + 1);
  351.   return (str.substring(sep1 + 1, endsep)).toInt();
  352. }//end int getValue()
  353. void getValue(String str, int &r, int &g, int &b) {
  354.   int sep1 = str.indexOf("-");
  355.   int sep2 = str.indexOf("-", sep1 + 1);
  356.   int sep3 = str.indexOf("-", sep2 + 1);
  357.   int endsep = str.indexOf("-", sep3 + 1);
  358.   r = (str.substring(sep1 + 1, sep2)).toInt();
  359.   g = (str.substring(sep2 + 1, sep3)).toInt();
  360.   b = (str.substring(sep3 + 1, endsep)).toInt();
  361. }//end getValue()
  362. void increaseMode(int &mode, int max) {
  363.   mode = mode + 1;
  364.   if (mode > max) mode = 1;
  365. }//end increase()
  366. void EEPROMWritelong(int address, long value) {
  367.   //Decomposition from a long to 4 bytes by using bitshift.
  368.   //One = Most significant -> Four = Least significant byte
  369.   byte four = (value & 0xFF);
  370.   byte three = ((value >> 8) & 0xFF);
  371.   byte two = ((value >> 16) & 0xFF);
  372.   byte one = ((value >> 24) & 0xFF);
  373.   //Write the 4 bytes into the eeprom memory.
  374.   EEPROM.write(address, four);
  375.   EEPROM.write(address + 1, three);
  376.   EEPROM.write(address + 2, two);
  377.   EEPROM.write(address + 3, one);
  378. }//end EEPROMWritelong()
  379. long EEPROMReadlong(long address) {
  380.   //Read the 4 bytes from the eeprom memory.
  381.   long four = EEPROM.read(address);
  382.   long three = EEPROM.read(address + 1);
  383.   long two = EEPROM.read(address + 2);
  384.   long one = EEPROM.read(address + 3);
  385.   //Return the recomposed long by using bitshift.
  386.   return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
  387. }//end EEPROMReadlong()
  388. void playEffect(int i) {
  389.   if (playeffects && i < nSoundeffects) {
  390.     wavEffects.play(soundeffects[i]);
  391.     delay(10);
  392.   }
  393. }//end playEffects()
  394. void Animation(int animation) {
  395.   int16_t sx, sy, x, y;
  396.   uint8_t h;
  397.   if (millis() > t2 + wait) {
  398.     switch (animation) {
  399.       case 1:
  400.         FastLED.clear();
  401.         Sprites.UpdateSprites();
  402.         for (int i = 0; i < NumSquares; i++) {
  403.           if (Shape1[i].GetFlags() & SPRITE_EDGE_X_MAX)
  404.             Sprites.ChangePriority(&Shape1[i], SPR_BACK);
  405.           else if (Shape1[i].GetFlags() & SPRITE_EDGE_X_MIN)
  406.             Sprites.ChangePriority(&Shape1[i], SPR_FRONT);
  407.         }
  408.         Sprites.RenderSprites();
  409.         FastLED.show();
  410.         break;
  411.       case 2:
  412.         FastLED.clear();
  413.         Sprites.UpdateSprites();
  414.         Sprites.RenderSprites();
  415.         FastLED.show();
  416.         break;
  417.       case 3:
  418.         FastLED.clear();
  419.         h = hue;
  420.         for (x = 0; x < (matrix_bottom.Width() + matrix_bottom.Height()); ++x) {
  421.           matrix_bottom.DrawLine(x - matrix_bottom.Height(), matrix_bottom.Height() - 1, x, 0, CHSV(h, Saturation, Brightness));
  422.           matrix_top.DrawFilledRectangle(0, 0, 9, 9, CHSV(h, Saturation, Brightness));
  423.           h += 16;
  424.         }
  425.         hue += 4;
  426.         FastLED.show();
  427.         break;
  428.       case 4:
  429.         FastLED.clear();
  430.         h = hue;
  431.         for (y = 0; y < matrix_bottom.Height(); ++y) {
  432.           matrix_bottom.DrawLine(0, y, matrix_bottom.Width() - 1, y, CHSV(h, Saturation, Brightness));
  433.           h += 16;
  434.         }
  435.         for (y = 0; y < matrix_top.Width() / 2; ++y) {
  436.           matrix_top.DrawRectangle(y, y, matrix_top.Width() - y - 1, matrix_top.Height() - y - 1, CHSV(h, Saturation, Brightness));
  437.           h += 16;
  438.         }
  439.         hue += 4;
  440.         FastLED.show();
  441.         break;
  442.     }
  443.     t2 = millis();
  444.   }
  445. }//end Animation()
  446. void InitAnimation(int animaiton) {
  447.   if (animation == 1) {
  448.     int16_t x = MATRIX_WIDTH_BOTTOM - SQUARE_WIDTH - 1, y = 0;
  449.     int8_t xc = -1, yc = 1;
  450.     NumSquares = 0;
  451.     while ((NumSquares < MAX_SQUARES) && (x < (MATRIX_WIDTH_BOTTOM - SQUARE_WIDTH))) {
  452.       for (int i = 0; i < NUM_COLS; i++)
  453.         ColTabs[NumSquares][i] = CHSV(NumSquares * 32, 255, 127 + (i * 64));
  454.       Shape1[NumSquares].Setup(SQUARE_WIDTH, SQUARE_HEIGHT, SquareData1, 1, _2BIT, ColTabs[NumSquares], SquareMask1);
  455.       Shape1[NumSquares].SetPositionFrameMotionOptions(x, y, 0, 0, xc, 2, 0, 0, SPRITE_DETECT_EDGE | SPRITE_X_KEEPIN);
  456.       Sprites.AddSprite(&Shape1[NumSquares]);
  457.       ++NumSquares;
  458.       x += (((SQUARE_WIDTH * 5) / 3) * xc);
  459.       if (x <= 0) {
  460.         x = abs(x);
  461.         xc = +1;
  462.       }
  463.       y += yc;
  464.       if ( (y == 0) || (y == (MATRIX_HEIGHT_BOTTOM - SQUARE_HEIGHT)) )
  465.         yc = 0 - yc;
  466.     }
  467.   }
  468.   else if (animation == 2) {
  469.     Shape2.SetPositionFrameMotionOptions(0/*X*/, 0/*Y*/, 0/*Frame*/, 0/*FrameRate*/, +1/*XChange*/, 1/*XRate*/, +1/*YChange*/, 1/*YRate*/, SPRITE_DETECT_EDGE | SPRITE_X_KEEPIN | SPRITE_Y_KEEPIN);
  470.     Sprites.AddSprite(&Shape2);
  471.   }
  472.   else if (animation == 3 || animation == 4) {
  473.     hue = 0;
  474.   }
  475. }//end InitAnimation()
  476. void Fire() {
  477.   if (millis() > t2 + wait) {
  478.     Fire2012WithPalette();
  479.     FastLED.show();
  480.     t2 = millis();
  481.   }
  482. }//end Fire()
  483. void Fire2012WithPalette() {
  484.   // Array of temperature readings at each simulation cell
  485.   static byte heat[MATRIX_WIDTH_BOTTOM][MATRIX_HEIGHT_BOTTOM];
  486.   // Step 1.  Cool down every cell a little
  487.   for ( int i = 0; i < MATRIX_WIDTH_BOTTOM; i++) {
  488.     for ( int j = 0; j < MATRIX_HEIGHT_BOTTOM; j++) {
  489.       heat[i][j] = qsub8( heat[i][j],  random8(0, ((COOLING * 10) / MATRIX_HEIGHT_BOTTOM) + 2));
  490.     }
  491.   }
  492.   // Step 2.  Heat from each cell drifts 'up' and diffuses a little
  493.   for ( int i = 0; i < MATRIX_WIDTH_BOTTOM; i++) {
  494.     for ( int k = MATRIX_HEIGHT_BOTTOM - 1; k >= 2; k--) {
  495.       heat[i][k] = (heat[i][k - 1] + heat[i][k - 2] + heat[i][k - 2] ) / 3;
  496.     }
  497.   }
  498.   // Step 3.  Randomly ignite new 'sparks' of heat near the bottom
  499.   for ( int i = 0; i < MATRIX_WIDTH_BOTTOM; i++) {
  500.     if ( random8() < SPARKING ) {
  501.       int x = random8(40);
  502.       int y = random8(2);
  503.       heat[x][y] = qadd8( heat[x][y], random8(160, 255) );
  504.     }
  505.   }
  506.   // Step 4.  Map from heat cells to LED colors
  507.   for ( int i = 0; i < MATRIX_WIDTH_BOTTOM; i++) {
  508.     for ( int j = 0; j < MATRIX_HEIGHT_BOTTOM; j++) {
  509.       // Scale the heat value from 0-255 down to 0-240
  510.       // for best results with color palettes.
  511.       byte colorindex = scale8(heat[i][j], 240);
  512.       CRGB color = ColorFromPalette( gPal, colorindex);
  513.       int pixelnumber;
  514.       if ( gReverseDirection ) {
  515.         pixelnumber = (MATRIX_WIDTH_BOTTOM - 1) - j;
  516.       } else {
  517.         pixelnumber = j;
  518.       }
  519.       matrix_bottom(i, pixelnumber) = color;
  520.     }
  521.   }
  522. }//end Fire2012WithPalette()
  523. void Noise() {
  524.   if (millis() > t2 + wait) {
  525.     fillnoise8();
  526.     mapNoiseToLEDsUsingPalette();
  527.     FastLED.show();
  528.     t2 = millis();
  529.   }
  530. }//end Noise()
  531. void fillnoise8() {
  532.   //file in NoisePaletteDemo in FastLED examples
  533.   uint8_t dataSmoothing = 0;
  534.   if ( speed < 50) {
  535.     dataSmoothing = 200 - (speed * 4);
  536.   }
  537.   for (int i = 0; i < MATRIX_WIDTH_BOTTOM + MATRIX_WIDTH_TOP; i++) {
  538.     int ioffset = scale * i;
  539.     for (int j = 0; j < MATRIX_HEIGHT_BOTTOM; j++) {
  540.       int joffset = scale * j;
  541.       uint8_t data = inoise8(x + ioffset, y + joffset, z);
  542.       data = qsub8(data, 16);
  543.       data = qadd8(data, scale8(data, 39));
  544.       if ( dataSmoothing ) {
  545.         uint8_t olddata = noise[i][j];
  546.         uint8_t newdata = scale8( olddata, dataSmoothing) + scale8( data, 256 - dataSmoothing);
  547.         data = newdata;
  548.       }
  549.       noise[i][j] = data;
  550.     }
  551.   }
  552.   z += speed;
  553.   x += speed / 8;
  554.   y -= speed / 16;
  555. }//end fillnoise8()
  556. void mapNoiseToLEDsUsingPalette() {
  557.   static uint8_t ihue = 0;
  558.   for (int i = 0; i < MATRIX_WIDTH_BOTTOM + MATRIX_WIDTH_TOP; i++) {
  559.     for (int j = 0; j < MATRIX_HEIGHT_BOTTOM; j++) {
  560.       uint8_t index = noise[j][i];
  561.       uint8_t bri =   noise[i][j];
  562.       if ( colorLoop) {
  563.         index += ihue;
  564.       }
  565.       if ( bri > 127 ) {
  566.         bri = 255;
  567.       } else {
  568.         bri = dim8_raw( bri * 2);
  569.       }
  570.       CRGB color1 = ColorFromPalette( currentPalette, index, bri);
  571.       if (i < MATRIX_WIDTH_BOTTOM) matrix_bottom(i, j) = color1;
  572.       else matrix_top(i - MATRIX_WIDTH_BOTTOM, j) = color1;
  573.     }
  574.   }
  575.   ihue += 1;
  576. }//end mapNoiseToLEDsUsingPalette()
  577. void ChangePaletteAndSettings() {
  578.   palette++;
  579.   if (palette > maxpalette) palette = 0;
  580.   if (palette ==  0)  {
  581.     currentPalette = RainbowColors_p;
  582.     speed = 20;
  583.     scale = 30;
  584.     colorLoop = 1;
  585.   }
  586.   if (palette == 1) {
  587.     SetupPurpleAndGreenPalette();
  588.     speed = 10;
  589.     scale = 50;
  590.     colorLoop = 1;
  591.   }
  592.   if (palette == 2) {
  593.     SetupBlackAndWhiteStripedPalette();
  594.     speed = 20;
  595.     scale = 30;
  596.     colorLoop = 1;
  597.   }
  598.   if (palette == 3) {
  599.     currentPalette = ForestColors_p;
  600.     speed =  8;
  601.     scale = 120;
  602.     colorLoop = 0;
  603.   }
  604.   if (palette == 4) {
  605.     currentPalette = CloudColors_p;
  606.     speed =  4;
  607.     scale = 30;
  608.     colorLoop = 0;
  609.   }
  610.   if (palette == 5) {
  611.     currentPalette = LavaColors_p;
  612.     speed =  8;
  613.     scale = 50;
  614.     colorLoop = 0;
  615.   }
  616.   if (palette == 6) {
  617.     currentPalette = OceanColors_p;
  618.     speed = 20;
  619.     scale = 90;
  620.     colorLoop = 0;
  621.   }
  622.   if (palette == 7) {
  623.     currentPalette = PartyColors_p;
  624.     speed = 20;
  625.     scale = 30;
  626.     colorLoop = 1;
  627.   }
  628.   if (palette == 8) {
  629.     SetupRandomPalette();
  630.     speed = 20;
  631.     scale = 20;
  632.     colorLoop = 1;
  633.   }
  634.   if (palette == 9) {
  635.     SetupRandomPalette();
  636.     speed = 50;
  637.     scale = 50;
  638.     colorLoop = 1;
  639.   }
  640.   if (palette == 10) {
  641.     SetupRandomPalette();
  642.     speed = 90;
  643.     scale = 90;
  644.     colorLoop = 1;
  645.   }
  646.   if (palette == 11) {
  647.     currentPalette = RainbowStripeColors_p;
  648.     speed = 30;
  649.     scale = 20;
  650.     colorLoop = 1;
  651.   }
  652. }//end ChangePaletteAndSettingsPeriodically()
  653. void SetupRandomPalette() {
  654.   currentPalette = CRGBPalette16(
  655.                      CHSV( random8(), 255, 32),
  656.                      CHSV( random8(), 255, 255),
  657.                      CHSV( random8(), 128, 255),
  658.                      CHSV( random8(), 255, 255));
  659. }//end SetupRandomPalette()
  660. void SetupBlackAndWhiteStripedPalette() {
  661.   fill_solid( currentPalette, 16, CRGB::Black);
  662.   currentPalette[0] = CRGB::White;
  663.   currentPalette[4] = CRGB::White;
  664.   currentPalette[8] = CRGB::White;
  665.   currentPalette[12] = CRGB::White;
  666. }//end SetupBlackAndWhiteStripedPalette()
  667. void SetupPurpleAndGreenPalette() {
  668.   CRGB purple = CHSV( HUE_PURPLE, 255, 255);
  669.   CRGB green  = CHSV( HUE_GREEN, 255, 255);
  670.   CRGB black  = CRGB::Black;
  671.   currentPalette = CRGBPalette16(
  672.                      green,  green,  black,  black,
  673.                      purple, purple, black,  black,
  674.                      green,  green,  black,  black,
  675.                      purple, purple, black,  black );
  676. }//end SetupPurpleAndGreenPalette()
  677. void handleNoise(String incoming) {
  678.   if (doNoise && NoisePalette) {
  679.     if (incoming.indexOf("PaletteH0") > -1) paletteH[0] = getValue(incoming);
  680.     if (incoming.indexOf("PaletteH1") > -1) paletteH[1] = getValue(incoming);
  681.     if (incoming.indexOf("PaletteH2") > -1) paletteH[2] = getValue(incoming);
  682.     if (incoming.indexOf("PaletteH3") > -1) paletteH[3] = getValue(incoming);
  683.     if (incoming.indexOf("PaletteS0") > -1) paletteS[0] = getValue(incoming);
  684.     if (incoming.indexOf("PaletteS1") > -1) paletteS[1] = getValue(incoming);
  685.     if (incoming.indexOf("PaletteS2") > -1) paletteS[2] = getValue(incoming);
  686.     if (incoming.indexOf("PaletteS3") > -1) paletteS[3] = getValue(incoming);
  687.     if (incoming.indexOf("PaletteV0") > -1) paletteV[0] = getValue(incoming);
  688.     if (incoming.indexOf("PaletteV1") > -1) paletteV[1] = getValue(incoming);
  689.     if (incoming.indexOf("PaletteV2") > -1) paletteV[2] = getValue(incoming);
  690.     if (incoming.indexOf("PaletteV3") > -1) paletteV[3] = getValue(incoming);
  691.     if (incoming.indexOf("PaletteSpeed") > -1) speed = getValue(incoming);
  692.     if (incoming.indexOf("PaletteScale") > -1) scale = getValue(incoming);
  693.     if (incoming.indexOf("PaletteLoop") > -1) colorLoop = (colorLoop ? 0 : 1);
  694.     if (NoisePalette) {
  695.       currentPalette = CRGBPalette16(CHSV(paletteH[0], paletteS[0], paletteV[0]),
  696.                                      CHSV(paletteH[1], paletteS[1], paletteV[1]),
  697.                                      CHSV(paletteH[2], paletteS[2], paletteV[2]),
  698.                                      CHSV(paletteH[3], paletteS[3], paletteV[3]));
  699.     }
  700.   }
  701. }//end handleNoise()
  702. void Music() {
  703.   if (musicVisualiser == 1) Music1();
  704.   if (musicVisualiser == 2) Music2();
  705. }//end Music()
  706. void Music1() {
  707.   if (fft.available()) {
  708.     double a = 2, b = 0;
  709.     fft_values[0] = fft.read(0);
  710.     fft_values[1] = fft.read(1);
  711.     for (int i = 2; i < 40; i++) {
  712.       if (i < 10) b = a + a / 3;
  713.       else if (i < 20) b = a + a / 6;
  714.       else b = a + a / 8;
  715.       fft_values[i] = fft.read((int) a, (int) b);
  716.       a = b;
  717.     }
  718.   }
  719.   if (peak1.available()) {
  720.     peak = peak1.read() * 25;
  721.   }
  722.   if (millis() > t2 + 30) {
  723.     FastLED.clear();
  724.     for (int i = 0; i < 40; i++) {
  725.       int z = min(db(fft_values[i] * 100.0), 20);
  726.       if (z > fft_show[i]) fft_show[i] = z;
  727.       else if (fft_show[i] > 0) fft_show[i]--;
  728.       for (int x = 0; x < map(fft_show[i], 0, 20, 0, 10); x++) {
  729.         CRGB color = ColorFromPalette(PartyColors_p, x * 12);
  730.         matrix_bottom(i, x) = color;
  731.       }
  732.     }
  733.     if (peak > peak_show + 3 && peak > 5) {
  734.       peak_show = peak;
  735.       xx1 = random(0, 8);
  736.       yy1 = random(0, 8);
  737.       bb1 = random(0, 255);
  738.     }
  739.     else if (peak_show > 0) {
  740.       peak_show--;
  741.     }
  742.     matrix_top.DrawRectangle(xx1, yy1, xx1 + 1, yy1 + 1, CHSV(bb1, 200, peak_show * 10));
  743.     FastLED.show();
  744.     t2 = millis();
  745.   }
  746. }//end Music1()
  747. void Music2() {
  748.   if (millis() > t2 + wait) {
  749.     if (peak1.available()) {
  750.       volume = peak1.read() * 30.0;
  751.       avgVol = (avgVol + volume) / 2.0;
  752.       if (volume < avgVol / 2.0 || volume < 15) volume = 0;
  753.       if (volume > maxVol) maxVol = volume;
  754.       if (gradient > 1529) {
  755.         gradient %= 1530;
  756.         maxVol = (maxVol + volume) / 2.0;
  757.       }
  758.       if (volume - last > avgVol - last && avgVol - last > 0) avgBump = (avgBump + (volume - last)) / 2.0;
  759.       bump = (volume - last) > avgBump;
  760.       Pulse();
  761.       gradient++;
  762.       last = volume;
  763.     }
  764.     t2 = millis();
  765.   }
  766. }//end Music2()
  767. void Pulse() {
  768.   fade(0.85);
  769.   if (bump) gradient += 64;
  770.   if (volume > 0) {
  771.     CRGB color1 = ColorFromPalette( currentPalette, gradient % 255);
  772.     int start = LED_HALF - (LED_HALF * (volume / maxVol));
  773.     int finish = LED_HALF + (LED_HALF * (volume / maxVol));
  774.     for (int i = start; i < finish; i++) {
  775.       float damp = float(((finish - start) / 2.0) - abs((i - start) - ((finish - start) / 2.0))) / float((finish - start) / 2.0);
  776.       if (i < 10)
  777.         matrix_bottom.DrawLine(0, i, matrix_bottom.Width() - 1, i,
  778.                                CRGB(color1.r * pow(damp, 2.0) * vol, color1.g * pow(damp, 2.0) * vol, color1.b * pow(damp, 2.0) * vol));
  779.       else
  780.         matrix_top.DrawRectangle(i - 10, i - 10, 19 - i, 19 - i,
  781.                                  CRGB(color1.r * pow(damp, 2.0) * vol, color1.g * pow(damp, 2.0) * vol, color1.b * pow(damp, 2.0) * vol));
  782.     }
  783.     FastLED.setBrightness(255.0 * pow(volume / maxVol, 2));
  784.   }
  785.   FastLED.show();
  786. }
  787. void fade(float damper) {
  788.   if (damper >= 1) damper = 0.99;
  789.   for (int i = 0; i < MATRIX_WIDTH_BOTTOM; i++) {
  790.     for (int j = 0; j < MATRIX_HEIGHT_BOTTOM; j++) {
  791.       matrix_bottom(i, j).r = matrix_bottom(i, j).r * damper;
  792.       matrix_bottom(i, j).g = matrix_bottom(i, j).g * damper;
  793.       matrix_bottom(i, j).b = matrix_bottom(i, j).b * damper;
  794.     }
  795.   }
  796.   for (int i = 0; i < MATRIX_WIDTH_TOP; i++) {
  797.     for (int j = 0; j < MATRIX_HEIGHT_TOP; j++) {
  798.       matrix_top(i, j).r = matrix_top(i, j).r * damper;
  799.       matrix_top(i, j).g = matrix_top(i, j).g * damper;
  800.       matrix_top(i, j).b = matrix_top(i, j).b * damper;
  801.     }
  802.   }
  803. }
  804. float db(float n) {
  805.   if (n <= 0) return 0;  // or whatever you consider to be "off"
  806.   return log10f(n) * 20.0f;
  807. }//end db()
  808. void Tetris() {
  809.   if (millis() > movingtime + tetrisspeed && tetrisrun) {
  810.     moveDown();
  811.     movingtime = millis();
  812.   }
  813. }//end Tetris()
  814. void handleTetris(String incoming) {
  815.   if (incoming.indexOf("GameStart") > -1) {
  816.     tetrisrun = true;
  817.   }
  818.   if (incoming.indexOf("GamePause") > -1) {
  819.     tetrisrun = false;
  820.   }
  821.   if (incoming.indexOf("GameRotate") > -1) {
  822.     if (tetrisrun) rotateBlock();
  823.   }
  824.   if (incoming.indexOf("GameLeft") > -1) {
  825.     if (tetrisrun) moveLeft(true);
  826.   }
  827.   if (incoming.indexOf("GameRight") > -1) {
  828.     if (tetrisrun) moveRight(true);
  829.   }
  830.   if (incoming.indexOf("GameDown") > -1) {
  831.     if (tetrisrun) moveDown();
  832.   }
  833. }//end handleTetris()
  834. void newGameTetris() {
  835.   tetrisrun = false;
  836.   removePile();
  837.   newBlock();
  838.   drawBorder();
  839.   updatePlayfield();
  840. }//end newGame()
  841. void removePile() {
  842.   for (int i = 0; i < TETRIS_WIDTH; i++) {
  843.     for (int j = 0; j < TETRIS_HIGHT; j++) {
  844.       Pile[i][j] = TETRIS_BLACK;
  845.     }
  846.   }
  847. }//end removePile()
  848. void newBlock() {
  849.   for (int i = 0; i < TETRIS_WIDTH; i++) {
  850.     for (int j = 0; j < TETRIS_HIGHT; j++) {
  851.       Block[i][j] = TETRIS_BLACK;
  852.     }
  853.   }
  854.   removeFullRow();
  855.   currentblock = random(7);
  856.   currentrotation = 0;
  857.   if (currentblock == 0) {
  858.     //generate I
  859.     Block[3][0] = blockcolor[currentblock + 1];
  860.     Block[4][0] = blockcolor[currentblock + 1];
  861.     Block[5][0] = blockcolor[currentblock + 1];
  862.     Block[6][0] = blockcolor[currentblock + 1];
  863.   }
  864.   if (currentblock == 1) {
  865.     //generate J
  866.     Block[3][0] = blockcolor[currentblock + 1];
  867.     Block[3][1] = blockcolor[currentblock + 1];
  868.     Block[4][1] = blockcolor[currentblock + 1];
  869.     Block[5][1] = blockcolor[currentblock + 1];
  870.   }
  871.   if (currentblock == 2) {
  872.     //generate L
  873.     Block[5][0] = blockcolor[currentblock + 1];
  874.     Block[5][1] = blockcolor[currentblock + 1];
  875.     Block[4][1] = blockcolor[currentblock + 1];
  876.     Block[3][1] = blockcolor[currentblock + 1];
  877.   }
  878.   if (currentblock == 3) {
  879.     //generate O
  880.     Block[3][0] = blockcolor[currentblock + 1];
  881.     Block[4][0] = blockcolor[currentblock + 1];
  882.     Block[3][1] = blockcolor[currentblock + 1];
  883.     Block[4][1] = blockcolor[currentblock + 1];
  884.   }
  885.   if (currentblock == 4) {
  886.     //generate S
  887.     Block[4][0] = blockcolor[currentblock + 1];
  888.     Block[5][0] = blockcolor[currentblock + 1];
  889.     Block[4][1] = blockcolor[currentblock + 1];
  890.     Block[3][1] = blockcolor[currentblock + 1];
  891.   }
  892.   if (currentblock == 5) {
  893.     //generate Z
  894.     Block[3][0] = blockcolor[currentblock + 1];
  895.     Block[4][0] = blockcolor[currentblock + 1];
  896.     Block[4][1] = blockcolor[currentblock + 1];
  897.     Block[5][1] = blockcolor[currentblock + 1];
  898.   }
  899.   if (currentblock == 6) {
  900.     //generate T
  901.     Block[4][0] = blockcolor[currentblock + 1];
  902.     Block[3][1] = blockcolor[currentblock + 1];
  903.     Block[4][1] = blockcolor[currentblock + 1];
  904.     Block[5][1] = blockcolor[currentblock + 1];
  905.   }
  906.   for (int j = 0; j < TETRIS_HIGHT; j++) {
  907.     for (int i = 0; i < TETRIS_WIDTH - 1; i++) {
  908.       if (Block[i][j] != TETRIS_BLACK && Pile[i][j] != TETRIS_BLACK) {
  909.         GameOverTetris();
  910.         return;
  911.       }
  912.     }
  913.   }
  914. }//end newBlock()
  915. void rotateBlock() {
  916.   if (currentblock == 3) return;
  917.   int xx, yy;
  918.   for (int i = TETRIS_WIDTH - 1; i >= 0; i--) {
  919.     for (int j = 0; j < TETRIS_HIGHT; j++) {
  920.       if (Block[i][j] != TETRIS_BLACK) xx = i;
  921.     }
  922.   }
  923.   for (int j = TETRIS_HIGHT - 1; j >= 0; j--) {
  924.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  925.       if (Block[i][j] != TETRIS_BLACK) yy = j;
  926.     }
  927.   }
  928.   if (currentblock == 0) {
  929.     if (currentrotation == 0 || currentrotation == 2) {
  930.       Block[xx][yy] = TETRIS_BLACK;
  931.       Block[xx + 2][yy] = TETRIS_BLACK;
  932.       Block[xx + 3][yy] = TETRIS_BLACK;
  933.       Block[xx + 1][yy - 1] = blockcolor[currentblock + 1];
  934.       Block[xx + 1][yy + 1] = blockcolor[currentblock + 1];
  935.       Block[xx + 1][yy + 2] = blockcolor[currentblock + 1];
  936.     }
  937.     if (currentrotation == 1 || currentrotation == 3) {
  938.       if (!spaceLeft()) {
  939.         if (spaceRight3()) {
  940.           if (!moveRight(false))
  941.             return;
  942.           xx++;;
  943.         }
  944.         else {
  945.           return;
  946.         }
  947.       }
  948.       else if (!spaceRight()) {
  949.         if (spaceLeft3()) {
  950.           if (!moveLeft(false))
  951.             return;
  952.           if (!moveLeft(false))
  953.             return;
  954.           xx--;
  955.           xx--;
  956.         }
  957.         else {
  958.           return;
  959.         }
  960.       }
  961.       else if (!spaceRight2()) {
  962.         if (spaceLeft2()) {
  963.           if (!moveLeft(false))
  964.             return;
  965.           xx--;
  966.         }
  967.         else {
  968.           return;
  969.         }
  970.       }
  971.       Block[xx][yy] = TETRIS_BLACK;
  972.       Block[xx][yy + 2] = TETRIS_BLACK;
  973.       Block[xx][yy + 3] = TETRIS_BLACK;
  974.       Block[xx - 1][yy + 1] = blockcolor[currentblock + 1];
  975.       Block[xx + 1][yy + 1] = blockcolor[currentblock + 1];
  976.       Block[xx + 2][yy + 1] = blockcolor[currentblock + 1];
  977.     }
  978.   }
  979.   xx++;
  980.   yy++;
  981.   if (currentblock == 1) {
  982.     if (currentrotation == 0) {
  983.       Block[xx - 1][yy - 1] = TETRIS_BLACK;
  984.       Block[xx - 1][yy] = TETRIS_BLACK;
  985.       Block[xx + 1][yy] = TETRIS_BLACK;
  986.       Block[xx][yy - 1] = blockcolor[currentblock + 1];
  987.       Block[xx + 1][yy - 1] = blockcolor[currentblock + 1];
  988.       Block[xx][yy + 1] = blockcolor[currentblock + 1];
  989.     }
  990.     if (currentrotation == 1) {
  991.       if (!spaceLeft()) {
  992.         if (!moveRight(false))
  993.           return;
  994.         xx++;
  995.       }
  996.       xx--;
  997.       Block[xx][yy - 1] = TETRIS_BLACK;
  998.       Block[xx + 1][yy - 1] = TETRIS_BLACK;
  999.       Block[xx][yy + 1] = TETRIS_BLACK;
  1000.       Block[xx - 1][yy] = blockcolor[currentblock + 1];
  1001.       Block[xx + 1][yy] = blockcolor[currentblock + 1];
  1002.       Block[xx + 1][yy + 1] = blockcolor[currentblock + 1];
  1003.     }
  1004.     if (currentrotation == 2) {
  1005.       yy--;
  1006.       Block[xx - 1][yy] = TETRIS_BLACK;
  1007.       Block[xx + 1][yy] = TETRIS_BLACK;
  1008.       Block[xx + 1][yy + 1] = TETRIS_BLACK;
  1009.       Block[xx][yy - 1] = blockcolor[currentblock + 1];
  1010.       Block[xx][yy + 1] = blockcolor[currentblock + 1];
  1011.       Block[xx - 1][yy + 1] = blockcolor[currentblock + 1];
  1012.     }
  1013.     if (currentrotation == 3) {
  1014.       if (!spaceRight()) {
  1015.         if (!moveLeft(false))
  1016.           return;
  1017.         xx--;
  1018.       }
  1019.       Block[xx][yy - 1] = TETRIS_BLACK;
  1020.       Block[xx][yy + 1] = TETRIS_BLACK;
  1021.       Block[xx - 1][yy + 1] = TETRIS_BLACK;
  1022.       Block[xx - 1][yy - 1] = blockcolor[currentblock + 1];
  1023.       Block[xx - 1][yy] = blockcolor[currentblock + 1];
  1024.       Block[xx + 1][yy] = blockcolor[currentblock + 1];
  1025.     }
  1026.   }
  1027.   if (currentblock == 2) {
  1028.     if (currentrotation == 0) {
  1029.       Block[xx + 1][yy - 1] = TETRIS_BLACK;
  1030.       Block[xx - 1][yy] = TETRIS_BLACK;
  1031.       Block[xx + 1][yy] = TETRIS_BLACK;
  1032.       Block[xx][yy - 1] = blockcolor[currentblock + 1];
  1033.       Block[xx + 1][yy + 1] = blockcolor[currentblock + 1];
  1034.       Block[xx][yy + 1] = blockcolor[currentblock + 1];
  1035.     }
  1036.     if (currentrotation == 1) {
  1037.       if (!spaceLeft()) {
  1038.         if (!moveRight(false))
  1039.           return;
  1040.         xx++;
  1041.       }
  1042.       xx--;
  1043.       Block[xx][yy - 1] = TETRIS_BLACK;
  1044.       Block[xx + 1][yy + 1] = TETRIS_BLACK;
  1045.       Block[xx][yy + 1] = TETRIS_BLACK;
  1046.       Block[xx - 1][yy] = blockcolor[currentblock + 1];
  1047.       Block[xx + 1][yy] = blockcolor[currentblock + 1];
  1048.       Block[xx - 1][yy + 1] = blockcolor[currentblock + 1];
  1049.     }
  1050.     if (currentrotation == 2) {
  1051.       yy--;
  1052.       Block[xx - 1][yy] = TETRIS_BLACK;
  1053.       Block[xx + 1][yy] = TETRIS_BLACK;
  1054.       Block[xx - 1][yy + 1] = TETRIS_BLACK;
  1055.       Block[xx][yy - 1] = blockcolor[currentblock + 1];
  1056.       Block[xx][yy + 1] = blockcolor[currentblock + 1];
  1057.       Block[xx - 1][yy - 1] = blockcolor[currentblock + 1];
  1058.     }
  1059.     if (currentrotation == 3) {
  1060.       if (!spaceRight()) {
  1061.         if (!moveLeft(false))
  1062.           return;
  1063.         xx--;
  1064.       }
  1065.       Block[xx][yy - 1] = TETRIS_BLACK;
  1066.       Block[xx][yy + 1] = TETRIS_BLACK;
  1067.       Block[xx - 1][yy - 1] = TETRIS_BLACK;
  1068.       Block[xx + 1][yy - 1] = blockcolor[currentblock + 1];
  1069.       Block[xx - 1][yy] = blockcolor[currentblock + 1];
  1070.       Block[xx + 1][yy] = blockcolor[currentblock + 1];
  1071.     }
  1072.   }
  1073.   if (currentblock == 4) {
  1074.     if (currentrotation == 0 || currentrotation == 2) {
  1075.       Block[xx + 1][yy - 1] = TETRIS_BLACK;
  1076.       Block[xx - 1][yy] = TETRIS_BLACK;
  1077.       Block[xx + 1][yy] = blockcolor[currentblock + 1];
  1078.       Block[xx + 1][yy + 1] = blockcolor[currentblock + 1];
  1079.     }
  1080.     if (currentrotation == 1 || currentrotation == 3) {
  1081.       if (!spaceLeft()) {
  1082.         if (!moveRight(false))
  1083.           return;
  1084.         xx++;
  1085.       }
  1086.       xx--;
  1087.       Block[xx + 1][yy] = TETRIS_BLACK;
  1088.       Block[xx + 1][yy + 1] = TETRIS_BLACK;
  1089.       Block[xx - 1][yy] = blockcolor[currentblock + 1];
  1090.       Block[xx + 1][yy - 1] = blockcolor[currentblock + 1];
  1091.     }
  1092.   }
  1093.   if (currentblock == 5) {
  1094.     if (currentrotation == 0 || currentrotation == 2) {
  1095.       Block[xx - 1][yy - 1] = TETRIS_BLACK;
  1096.       Block[xx][yy - 1] = TETRIS_BLACK;
  1097.       Block[xx + 1][yy - 1] = blockcolor[currentblock + 1];
  1098.       Block[xx][yy + 1] = blockcolor[currentblock + 1];
  1099.     }
  1100.     if (currentrotation == 1 || currentrotation == 3) {
  1101.       if (!spaceLeft()) {
  1102.         if (!moveRight(false))
  1103.           return;
  1104.         xx++;
  1105.       }
  1106.       xx--;
  1107.       Block[xx + 1][yy - 1] = TETRIS_BLACK;
  1108.       Block[xx][yy + 1] = TETRIS_BLACK;
  1109.       Block[xx - 1][yy - 1] = blockcolor[currentblock + 1];
  1110.       Block[xx][yy - 1] = blockcolor[currentblock + 1];
  1111.     }
  1112.   }
  1113.   if (currentblock == 6) {
  1114.     if (currentrotation == 0) {
  1115.       Block[xx][yy - 1] = TETRIS_BLACK;
  1116.       Block[xx - 1][yy] = TETRIS_BLACK;
  1117.       Block[xx + 1][yy] = TETRIS_BLACK;
  1118.       Block[xx][yy - 1] = blockcolor[currentblock + 1];
  1119.       Block[xx + 1][yy] = blockcolor[currentblock + 1];
  1120.       Block[xx][yy + 1] = blockcolor[currentblock + 1];
  1121.     }
  1122.     if (currentrotation == 1) {
  1123.       if (!spaceLeft()) {
  1124.         if (!moveRight(false))
  1125.           return;
  1126.         xx++;
  1127.       }
  1128.       xx--;
  1129.       Block[xx][yy - 1] = TETRIS_BLACK;
  1130.       Block[xx + 1][yy] = TETRIS_BLACK;
  1131.       Block[xx][yy + 1] = TETRIS_BLACK;
  1132.       Block[xx - 1][yy] = blockcolor[currentblock + 1];
  1133.       Block[xx + 1][yy] = blockcolor[currentblock + 1];
  1134.       Block[xx][yy + 1] = blockcolor[currentblock + 1];
  1135.     }
  1136.     if (currentrotation == 2) {
  1137.       yy--;
  1138.       Block[xx - 1][yy] = TETRIS_BLACK;
  1139.       Block[xx + 1][yy] = TETRIS_BLACK;
  1140.       Block[xx][yy + 1] = TETRIS_BLACK;
  1141.       Block[xx][yy - 1] = blockcolor[currentblock + 1];
  1142.       Block[xx - 1][yy] = blockcolor[currentblock + 1];
  1143.       Block[xx][yy + 1] = blockcolor[currentblock + 1];
  1144.     }
  1145.     if (currentrotation == 3) {
  1146.       if (!spaceRight()) {
  1147.         if (!moveLeft(false))
  1148.           return;
  1149.         xx--;
  1150.       }
  1151.       Block[xx][yy - 1] = TETRIS_BLACK;
  1152.       Block[xx - 1][yy] = TETRIS_BLACK;
  1153.       Block[xx][yy + 1] = TETRIS_BLACK;
  1154.       Block[xx][yy - 1] = blockcolor[currentblock + 1];
  1155.       Block[xx - 1][yy] = blockcolor[currentblock + 1];
  1156.       Block[xx + 1][yy] = blockcolor[currentblock + 1];
  1157.     }
  1158.   }
  1159.   while (!checkOverlap())
  1160.   {
  1161.     for (int j = 0; j < TETRIS_HIGHT; j++)
  1162.     {
  1163.       for (int i = 0; i < TETRIS_WIDTH; i++)
  1164.       {
  1165.         Block[i][j] = Block[i][j + 1];
  1166.       }
  1167.     }
  1168.     movingtime = millis() + tetrisspeed;
  1169.   }
  1170.   currentrotation++;
  1171.   if (currentrotation == 4) currentrotation = 0;
  1172.   updatePlayfield();
  1173. }//end rotateBlock()
  1174. void moveDown() {
  1175.   if (spaceBelow()) {
  1176.     byte copyBlock[TETRIS_WIDTH][TETRIS_HIGHT];
  1177.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1178.       for (int j = 0; j < TETRIS_HIGHT; j++) {
  1179.         copyBlock[i][j] = Block[i][j];
  1180.         Block[i][j] = TETRIS_BLACK;
  1181.       }
  1182.     }
  1183.     for (int j = TETRIS_HIGHT - 1; j > 0; j--) {
  1184.       for (int i = 0; i < TETRIS_WIDTH; i++) {
  1185.         Block[i][j] = copyBlock[i][j - 1];
  1186.       }
  1187.     }
  1188.   }
  1189.   else {
  1190.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1191.       for (int j = 0; j < TETRIS_HIGHT; j++) {
  1192.         if (Block[i][j] != TETRIS_BLACK)
  1193.           Pile[i][j] = Block[i][j];
  1194.       }
  1195.     }
  1196.     newBlock();
  1197.   }
  1198.   updatePlayfield();
  1199. }//end moveDown
  1200. boolean moveRight(boolean refresh) {
  1201.   if (spaceRight()) {
  1202.     byte copyBlock[TETRIS_WIDTH][TETRIS_HIGHT];
  1203.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1204.       for (int j = 0; j < TETRIS_HIGHT; j++) {
  1205.         copyBlock[i][j] = Block[i][j];
  1206.         Block[i][j] = TETRIS_BLACK;
  1207.       }
  1208.     }
  1209.     for (int i = 1; i < TETRIS_WIDTH; i++) {
  1210.       for (int j = 0; j < TETRIS_HIGHT; j++) {
  1211.         Block[i][j] = copyBlock[i - 1][j];
  1212.       }
  1213.     }
  1214.     if (refresh) updatePlayfield();
  1215.     return true;
  1216.   }
  1217.   return false;
  1218. }//end moveRight()
  1219. boolean moveLeft(boolean refresh) {
  1220.   if (spaceLeft()) {
  1221.     byte copyBlock[TETRIS_WIDTH][TETRIS_HIGHT];
  1222.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1223.       for (int j = 0; j < TETRIS_HIGHT; j++) {
  1224.         copyBlock[i][j] = Block[i][j];
  1225.         Block[i][j] = TETRIS_BLACK;
  1226.       }
  1227.     }
  1228.     for (int i = 0; i < TETRIS_WIDTH - 1; i++) {
  1229.       for (int j = 0; j < TETRIS_HIGHT; j++) {
  1230.         Block[i][j] = copyBlock[i + 1][j];
  1231.       }
  1232.     }
  1233.     if (refresh) updatePlayfield();
  1234.     return true;
  1235.   }
  1236.   return false;
  1237. }//end moveLeft()
  1238. boolean spaceBelow() {
  1239.   for (int j = TETRIS_HIGHT - 1; j >= 0; j--) {
  1240.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1241.       if (Block[i][j] != TETRIS_BLACK) {
  1242.         if (j == TETRIS_HIGHT - 1)
  1243.           return false;
  1244.         if (Pile[i][j + 1] != TETRIS_BLACK)
  1245.           return false;
  1246.       }
  1247.     }
  1248.   }
  1249.   return true;
  1250. }//end spaceBelow()
  1251. boolean spaceLeft() {
  1252.   for (int j = TETRIS_HIGHT - 1; j >= 0; j--) {
  1253.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1254.       if (Block[i][j] != TETRIS_BLACK) {
  1255.         if (i == 0)
  1256.           return false;
  1257.         if (Pile[i - 1][j] != TETRIS_BLACK)
  1258.           return false;
  1259.       }
  1260.     }
  1261.   }
  1262.   return true;
  1263. }//end spaceLeft()
  1264. boolean spaceLeft2() {
  1265.   for (int j = TETRIS_HIGHT - 1; j >= 0; j--) {
  1266.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1267.       if (Block[i][j] != TETRIS_BLACK) {
  1268.         if (i == 0 || i == 1)
  1269.           return false;
  1270.         if (Pile[i - 1][j] != TETRIS_BLACK || Pile[i - 2][j] != TETRIS_BLACK)
  1271.           return false;
  1272.       }
  1273.     }
  1274.   }
  1275.   return true;
  1276. }//end spaceLeft2()
  1277. boolean spaceLeft3() {
  1278.   for (int j = TETRIS_HIGHT - 1; j >= 0; j--) {
  1279.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1280.       if (Block[i][j] != TETRIS_BLACK) {
  1281.         if (i == 0 || i == 1 || i == 2)
  1282.           return false;
  1283.         if (Pile[i - 1][j] != TETRIS_BLACK || Pile[i - 2][j] != TETRIS_BLACK || Pile[i - 3][j] != TETRIS_BLACK)
  1284.           return false;
  1285.       }
  1286.     }
  1287.   }
  1288.   return true;
  1289. }//end spaceLeft3()
  1290. boolean spaceRight() {
  1291.   for (int j = TETRIS_HIGHT - 1; j >= 0; j--) {
  1292.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1293.       if (Block[i][j] != TETRIS_BLACK) {
  1294.         if (i == TETRIS_WIDTH - 1)
  1295.           return false;
  1296.         if (Pile[i + 1][j] != TETRIS_BLACK)
  1297.           return false;
  1298.       }
  1299.     }
  1300.   }
  1301.   return true;
  1302. }//end spaceRight()
  1303. boolean spaceRight2() {
  1304.   for (int j = TETRIS_HIGHT - 1; j >= 0; j--) {
  1305.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1306.       if (Block[i][j] != TETRIS_BLACK) {
  1307.         if (i == TETRIS_WIDTH - 1 || i == TETRIS_WIDTH - 2)
  1308.           return false;
  1309.         if (Pile[i + 1][j] != TETRIS_BLACK || Pile[i + 2][j] != TETRIS_BLACK)
  1310.           return false;
  1311.       }
  1312.     }
  1313.   }
  1314.   return true;
  1315. }//end spaceRight2()
  1316. boolean spaceRight3() {
  1317.   for (int j = TETRIS_HIGHT - 1; j >= 0; j--) {
  1318.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1319.       if (Block[i][j] != TETRIS_BLACK) {
  1320.         if (i == TETRIS_WIDTH - 1 || i == TETRIS_WIDTH - 2 || i == TETRIS_WIDTH - 3)
  1321.           return false;
  1322.         if (Pile[i + 1][j] != TETRIS_BLACK || Pile[i + 2][j] != TETRIS_BLACK || Pile[i + 3][j] != TETRIS_BLACK)
  1323.           return false;
  1324.       }
  1325.     }
  1326.   }
  1327.   return true;
  1328. }//end spaceRight3()
  1329. boolean checkOverlap() {
  1330.   for (int j = 0; j < TETRIS_HIGHT; j++) {
  1331.     for (int i = 0; i < TETRIS_WIDTH - 1; i++) {
  1332.       if (Block[i][j]) {
  1333.         if (Pile[i][j]) return false;
  1334.       }
  1335.     }
  1336.   }
  1337.   for (int j = TETRIS_HIGHT; j < TETRIS_HIGHT + 2; j++) {
  1338.     for (int i = 0; i < 7; i++) {
  1339.       if (Block[i][j]) return false;
  1340.     }
  1341.   }
  1342.   return true;
  1343. }//end checkOverlap()
  1344. void removeFullRow() {
  1345.   while (checkFullRow() != -1) {
  1346.     int fullrow = checkFullRow();
  1347.     playEffect(9);
  1348.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1349.       Pile[i][fullrow] = TETRIS_WHITE;
  1350.     }
  1351.     updatePlayfield();
  1352.     delay(100);
  1353.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1354.       Pile[i][fullrow] = TETRIS_BLACK;
  1355.     }
  1356.     updatePlayfield();
  1357.     delay(100);
  1358.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1359.       Pile[i][fullrow] = TETRIS_WHITE;
  1360.     }
  1361.     updatePlayfield();
  1362.     delay(100);
  1363.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1364.       Pile[i][fullrow] = TETRIS_BLACK;
  1365.     }
  1366.     for (int j = fullrow; j >= 0; j--) {
  1367.       for (int i = 0; i < TETRIS_WIDTH; i++) {
  1368.         Pile[i][j] = Pile[i][j - 1];
  1369.       }
  1370.     }
  1371.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1372.       Pile[i][0] = TETRIS_BLACK;
  1373.     }
  1374.   }
  1375. }//end removeFullRow()
  1376. int checkFullRow() {
  1377.   int cnt = 0;
  1378.   for (int j = 0; j < TETRIS_HIGHT; j++) {
  1379.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1380.       if (Pile[i][j] != TETRIS_BLACK) cnt++;
  1381.     }
  1382.     if (cnt == 10) return j;
  1383.     cnt = 0;
  1384.   }
  1385.   return -1;
  1386. }//end checkFullRow()
  1387. void GameOverTetris() {
  1388.   tetrisrun = false;
  1389.   FastLED.clear();
  1390.   playEffect(2);
  1391.   StringToTextBuffer("GAME OVER", 1);
  1392.   matrix_top.DrawFilledRectangle(0, 0, 9, 9, CRGB::Red);
  1393.   while (scrollingtext.UpdateText() != -1) {
  1394.     FastLED.show();
  1395.     delay(50);
  1396.   }
  1397.   resetDos();
  1398.   doNoise = true;
  1399. }//end GameOverTetris()
  1400. void drawBorder() {
  1401.   matrix_bottom.DrawLine(9, 0, 9, 9, CRGB::Red);
  1402.   matrix_bottom.DrawLine(20, 0, 20, 9, CRGB::Red);
  1403.   matrix_bottom.DrawLine(20, 9, 29, 9, CRGB::Red);
  1404.   matrix_bottom.DrawLine(0, 9, 9, 9, CRGB::Red);
  1405.   matrix_bottom.DrawLine(30, 9, 39, 9, CRGB::Red);
  1406. }//end drawPlayfield()
  1407. void updatePlayfield() {
  1408.   FastLED.clear();
  1409.   for (int i = 0; i < TETRIS_WIDTH; i++) {
  1410.     for (int j = 0; j < TETRIS_HIGHT; j++) {
  1411.       if (Block[i][j] != TETRIS_BLACK) setXYPixelTetris(i, j, TetrisColor[Block[i][j]]);
  1412.       if (Pile[i][j] != TETRIS_BLACK) setXYPixelTetris(i, j, TetrisColor[Pile[i][j]]);
  1413.     }
  1414.   }
  1415.   drawBorder();
  1416.   FastLED.show();
  1417.   //PrintToMonitor(); //debug
  1418. }//end renderTetris()
  1419. void setXYPixelTetris(byte x, byte y, CRGB c) {
  1420.   if (y < 10)
  1421.     matrix_top(x, y) = c;
  1422.   else
  1423.     matrix_bottom(x + 10, 19 - y) = c;
  1424. }//end setXYPixel()
  1425. void PrintToMonitor() {
  1426.   for (int j = 0; j < TETRIS_HIGHT; j++) {
  1427.     for (int i = 0; i < TETRIS_WIDTH; i++) {
  1428.       if (Block[i][j] != 0) Serial.print("x");
  1429.       else Serial.print("-");
  1430.       Serial.print("\t");
  1431.     }
  1432.     Serial.println();
  1433.   }
  1434.   Serial.println();
  1435. }
  1436. void Text() {
  1437.   if (millis() > t2 + wait) {
  1438.     if (scrollingtext.UpdateText() == -1) {
  1439.       scrollingtext.SetText(TextBuffer, charlength(TextBuffer, MAX_BUFFER));
  1440.       setTextColor(0);
  1441.     }
  1442.     else
  1443.       FastLED.show();
  1444.     t2 = millis();
  1445.   }
  1446. }//end Text()
  1447. int PrepareText(String &txt) {
  1448.   txt = "        " + txt;
  1449.   return txt.length();
  1450. }//end PrepareText()
  1451. int PrepareText2(String &txt) {
  1452.   txt = txt;
  1453.   return txt.length();
  1454. }//end PrepareText2()
  1455. void StringToTextBuffer(String str, int mode) {
  1456.   clearTextBuffer();
  1457.   str.toCharArray((char*) TextBuffer, PrepareText(str) + 1);
  1458.   scrollingtext.SetFont(MatriseFontData);
  1459.   scrollingtext.Init(&matrix_bottom, matrix_bottom.Width(), scrollingtext.FontHeight() + 1, 0, 1);
  1460.   scrollingtext.SetText((unsigned char*) TextBuffer, charlength(TextBuffer, MAX_BUFFER));
  1461.   scrollingtext.SetScrollDirection(SCROLL_LEFT);
  1462.   scrollingtext.SetTextDirection(CHAR_UP);
  1463.   setTextColor(mode);
  1464. }//end StringToTextBuffer()
  1465. int charlength(unsigned char *chararray, int nmax) {
  1466.   int x = 0;
  1467.   for (int i = 0; i < nmax; i++) {
  1468.     int c = chararray[i];
  1469.     if (c != '\0') x++;
  1470.   }
  1471.   return x;
  1472. }//end charlength()
  1473. void clearTextBuffer() {
  1474.   for (int i = 0; i < MAX_BUFFER; i++)
  1475.     TextBuffer[i] = (char) 0 ;
  1476. }//end clearTextBuffer()
  1477. void setTextColor(int mode) {
  1478.   if (mode == 0) scrollingtext.SetTextColrOptions(COLR_HSV | COLR_GRAD | COLR_CHAR | COLR_HORI, random(0, 255), 250, 200, random(0, 255), 250, 200);
  1479.   else if (mode == 1) scrollingtext.SetTextColrOptions(COLR_GRAD_CV, 250, 0, 0, 250, 250, 250);
  1480.   else scrollingtext.SetTextColrOptions(COLR_GRAD_CH, 0, 0, 200, 250, 250, 250);
  1481. }//end setTextColor()
  1482. void Snake() {
  1483.   if (millis() > movingtime + snakespeed && snakerun) {
  1484.     moveSnake();
  1485.     movingtime = millis();
  1486.   }
  1487. }//end Snake
  1488. void handleSnake(String incoming) {
  1489.   if (incoming.indexOf("GameRight") > -1) {
  1490.     setSnakeDirection(1);
  1491.   }
  1492.   if (incoming.indexOf("GameLeft") > -1) {
  1493.     setSnakeDirection(-1);
  1494.   }
  1495.   if (incoming.indexOf("GameStart") > -1) {
  1496.     snakerun = true;
  1497.   }
  1498.   if (incoming.indexOf("GamePause") > -1) {
  1499.     snakerun = false;
  1500.   }
  1501.   if (incoming.indexOf("GameReset") > -1) {
  1502.     EEPROMWritelong(0, ScoreEEPROMaddress);
  1503.   }
  1504. }//end handleSnake()
  1505. void setXYPixel(byte x, byte y, CRGB c) {
  1506.   if (x <= 40)
  1507.     matrix_bottom(x - 1, y - 1) = c;
  1508.   else
  1509.     matrix_top(x - 41, y - 1) = c;
  1510. }//end setXYPixel()
  1511. void NewGameSnake() {
  1512.   byte x, y;
  1513.   SnakeItems = 1;
  1514.   SnakeHeadID = 1;
  1515.   SnakeItemPosX[1] = 1;
  1516.   SnakeItemPosY[1] = 1;
  1517.   movingDirection = SNAKE_RIGHT;
  1518.   snakeDirection = SNAKE_RIGHT;
  1519.   snakeOntop = false;
  1520.   snakerun = false;
  1521.   SnakeScore = 0;
  1522.   snakespeed = SNAKESPEED_BEGIN;
  1523.   //Spielfeld ausschalten
  1524.   for (y = 1; y <= BaneGridYmax; y++) {
  1525.     for (x = 1; x <= BaneGridXmax; x++) {
  1526.       Playfield[x][y] = BLANK;
  1527.     }
  1528.   }
  1529.   AddSnakeItem = 0;
  1530.   AppleCount = 0;
  1531.   placeRandomApple();
  1532.   render();
  1533.   movingtime = millis();
  1534. }//end newgame
  1535. void render() {
  1536.   byte i, x, y;
  1537.   for (i = 1; i <= oldSnakeItems; i++) {
  1538.     if (oldSnakeItemPosX[i] > 0 && oldSnakeItemPosY[i] > 0 && oldSnakeItemPosX[i] <= BaneGridXmax && oldSnakeItemPosY[i] <= BaneGridYmax) {
  1539.       Playfield[oldSnakeItemPosX[i]][oldSnakeItemPosY[i]] = BLANK;
  1540.     }
  1541.   }
  1542.   for (i = 1; i <= SnakeItems; i++) {
  1543.     if (SnakeItemPosX[i] > 0 && SnakeItemPosY[i] > 0 && SnakeItemPosX[i] <= BaneGridXmax && SnakeItemPosY[i] <= BaneGridYmax) {
  1544.       Playfield[SnakeItemPosX[i]][SnakeItemPosY[i]] = SNAKE;
  1545.       oldSnakeItemPosX[i] = SnakeItemPosX[i];
  1546.       oldSnakeItemPosY[i] = SnakeItemPosY[i];
  1547.     }
  1548.   }
  1549.   oldSnakeItems = SnakeItems;
  1550.   //schalte die leds auf der matrix
  1551.   for (y = 1; y <= BaneGridYmax; y++) {
  1552.     for (x = 1; x <= BaneGridXmax; x++) {
  1553.       switch (Playfield[x][y]) {
  1554.         case BLANK:
  1555.           setXYPixel(x, y, CRGB::Black); //Blank
  1556.           break;
  1557.         case SNAKE:
  1558.           if (SnakeItemPosX[SnakeHeadID] == x && SnakeItemPosY[SnakeHeadID] == y)
  1559.             setXYPixel(x, y, CRGB::Yellow); // Yellow snake head
  1560.           else
  1561.             setXYPixel(x, y, CRGB::Green); // Green snake body
  1562.           break;
  1563.         case APPLE:
  1564.           setXYPixel(x, y, CRGB::Red);  //Red Apple
  1565.           break;
  1566.         default:
  1567.           setXYPixel(x, y, CRGB::Black);  //Blank
  1568.           break;
  1569.       }
  1570.     }
  1571.   }
  1572.   FastLED.show();
  1573. }//end render
  1574. void moveSnake() {
  1575.   byte i;
  1576.   movingDirection = snakeDirection;
  1577.   if (AddSnakeItem == 0) {
  1578.     //wenn er keinen apfel isst
  1579.     SnakeBackID = SnakeHeadID - 1;
  1580.     if (SnakeBackID == 0) SnakeBackID = SnakeItems;
  1581.     SnakeItemPosX[SnakeBackID] = SnakeItemPosX[SnakeHeadID];
  1582.     SnakeItemPosY[SnakeBackID] = SnakeItemPosY[SnakeHeadID];
  1583.     switch (movingDirection) {
  1584.       case SNAKE_RIGHT:
  1585.         SnakeItemPosX[SnakeBackID] += 1;
  1586.         //damit snake nicht am seitlichen x rand aufhört
  1587.         if (SnakeItemPosX[SnakeBackID] == 41 && !snakeOntop) {
  1588.           SnakeItemPosX[SnakeBackID] = 1;
  1589.         }
  1590.         //verhalten der schlange wenn sie von der oberen matrix auf die seite wechselt
  1591.         else if (SnakeItemPosX[SnakeBackID] == 51 && snakeOntop) {
  1592.           SnakeItemPosX[SnakeBackID] = 31 - SnakeItemPosY[SnakeBackID];
  1593.           SnakeItemPosY[SnakeBackID] = 10;
  1594.           snakeDirection = SNAKE_DOWN;
  1595.           snakeOntop = false;
  1596.         }
  1597.         break;
  1598.       case SNAKE_LEFT:
  1599.         SnakeItemPosX[SnakeBackID] -= 1;
  1600.         //damit snake nicht am seitlichen x rand aufhört
  1601.         if (SnakeItemPosX[SnakeBackID] < 1 && !snakeOntop) {
  1602.           SnakeItemPosX[SnakeBackID] = 40;
  1603.         }
  1604.         //verhalten der schlange wenn sie von der oberen matrix auf die seite wechselt
  1605.         else if (SnakeItemPosX[SnakeBackID] == 40 && snakeOntop) {
  1606.           SnakeItemPosX[SnakeBackID] = SnakeItemPosY[SnakeBackID];
  1607.           SnakeItemPosY[SnakeBackID] = 10;
  1608.           snakeDirection = SNAKE_DOWN;
  1609.           snakeOntop = false;
  1610.         }
  1611.         break;
  1612.       case SNAKE_DOWN:
  1613.         SnakeItemPosY[SnakeBackID] -= 1;
  1614.         //verhalten der schlange wenn sie von der oberen matrix auf die seite wechselt
  1615.         if (SnakeItemPosY[SnakeBackID] == 0 && SnakeItemPosX[SnakeBackID] > 40 && snakeOntop) {
  1616.           SnakeItemPosY[SnakeBackID] = 10;
  1617.           SnakeItemPosX[SnakeBackID] = 81 - SnakeItemPosX[SnakeBackID];
  1618.           snakeDirection = SNAKE_DOWN;
  1619.           snakeOntop = false;
  1620.         }
  1621.         break;
  1622.       case SNAKE_UP:
  1623.         SnakeItemPosY[SnakeBackID] += 1;
  1624.         //verhalten der schlange wenn sie von der oberen matrix auf die seite wechselt
  1625.         if (SnakeItemPosY[SnakeBackID] == 11 && SnakeItemPosX[SnakeBackID] > 40 && snakeOntop) {
  1626.           SnakeItemPosY[SnakeBackID] = 10;
  1627.           SnakeItemPosX[SnakeBackID] = SnakeItemPosX[SnakeBackID] - 30;
  1628.           snakeDirection = SNAKE_DOWN;
  1629.           snakeOntop = false;
  1630.         }
  1631.         //verhalten der schlange wenn sie von der seite auf die obere matrix wechselt
  1632.         else if (SnakeItemPosY[SnakeBackID] > 10 && !snakeOntop) {
  1633.           if (SnakeItemPosX[SnakeBackID] > 0 && SnakeItemPosX[SnakeBackID] <= 10) {
  1634.             SnakeItemPosY[SnakeBackID] = SnakeItemPosX[SnakeBackID];
  1635.             SnakeItemPosX[SnakeBackID] = 41;
  1636.             snakeDirection = SNAKE_RIGHT;
  1637.           }
  1638.           else if (SnakeItemPosX[SnakeBackID] > 10 && SnakeItemPosX[SnakeBackID] <= 20) {
  1639.             SnakeItemPosY[SnakeBackID] = 10;
  1640.             SnakeItemPosX[SnakeBackID] = SnakeItemPosX[SnakeBackID] + 30;
  1641.             snakeDirection = SNAKE_DOWN;
  1642.           }
  1643.           else if (SnakeItemPosX[SnakeBackID] > 20 && SnakeItemPosX[SnakeBackID] <= 30) {
  1644.             SnakeItemPosY[SnakeBackID] = 31 - SnakeItemPosX[SnakeBackID];
  1645.             SnakeItemPosX[SnakeBackID] = 50;
  1646.             snakeDirection = SNAKE_LEFT;
  1647.           }
  1648.           else if (SnakeItemPosX[SnakeBackID] > 30 && SnakeItemPosX[SnakeBackID] <= 40) {
  1649.             SnakeItemPosY[SnakeBackID] = 1;
  1650.             SnakeItemPosX[SnakeBackID] = 81 - SnakeItemPosX[SnakeBackID];
  1651.             snakeDirection = SNAKE_UP;
  1652.           }
  1653.           snakeOntop = true;
  1654.         }
  1655.         break;
  1656.     }
  1657.     SnakeHeadID = SnakeBackID;
  1658.   }
  1659.   else {
  1660.     //die schlange isst einen apfel
  1661.     for (i = SnakeItems; i >= SnakeHeadID; i--) {
  1662.       SnakeItemPosX[i + 1] = SnakeItemPosX[i];
  1663.       SnakeItemPosY[i + 1] = SnakeItemPosY[i];
  1664.     }
  1665.     SnakeItemPosX[SnakeHeadID] = SnakeItemPosX[SnakeHeadID + 1];
  1666.     SnakeItemPosY[SnakeHeadID] = SnakeItemPosY[SnakeHeadID + 1];
  1667.     switch (movingDirection) {
  1668.       case SNAKE_RIGHT:
  1669.         SnakeItemPosX[SnakeHeadID] += 1;
  1670.         //damit snake nicht am seitlichen x rand aufhört
  1671.         if (SnakeItemPosX[SnakeHeadID] == 41 && !snakeOntop) {
  1672.           SnakeItemPosX[SnakeHeadID] = 1;
  1673.         }
  1674.         //verhalten der schlange wenn sie von der oberen matrix auf die seite wechselt
  1675.         else if (SnakeItemPosX[SnakeHeadID] == 51 && snakeOntop) {
  1676.           SnakeItemPosX[SnakeHeadID] = 31 - SnakeItemPosY[SnakeHeadID];
  1677.           SnakeItemPosY[SnakeHeadID] = 10;
  1678.           snakeDirection = SNAKE_DOWN;
  1679.           snakeOntop = false;
  1680.         }
  1681.         break;
  1682.       case SNAKE_LEFT:
  1683.         SnakeItemPosX[SnakeHeadID] -= 1;
  1684.         //damit snake nicht am seitlichen x rand aufhört
  1685.         if (SnakeItemPosX[SnakeHeadID] < 1 && !snakeOntop) {
  1686.           SnakeItemPosX[SnakeHeadID] = 40;
  1687.         }
  1688.         //verhalten der schlange wenn sie von der oberen matrix auf die seite wechselt
  1689.         else if (SnakeItemPosX[SnakeHeadID] == 40 && snakeOntop) {
  1690.           SnakeItemPosX[SnakeHeadID] = SnakeItemPosY[SnakeHeadID];
  1691.           SnakeItemPosY[SnakeHeadID] = 10;
  1692.           snakeDirection = SNAKE_DOWN;
  1693.           snakeOntop = false;
  1694.         }
  1695.         break;
  1696.       case SNAKE_DOWN:
  1697.         SnakeItemPosY[SnakeHeadID] -= 1;
  1698.         //verhalten der schlange wenn sie von der oberen matrix auf die seite wechselt
  1699.         if (SnakeItemPosY[SnakeHeadID] == 0 && SnakeItemPosX[SnakeHeadID] > 40 && snakeOntop) {
  1700.           SnakeItemPosY[SnakeHeadID] = 10;
  1701.           SnakeItemPosX[SnakeHeadID] = 81 - SnakeItemPosX[SnakeHeadID];
  1702.           snakeDirection = SNAKE_DOWN;
  1703.           snakeOntop = false;
  1704.         }
  1705.         break;
  1706.       case SNAKE_UP:
  1707.         SnakeItemPosY[SnakeHeadID] += 1;
  1708.         //verhalten der schlange wenn sie von der oberen matrix auf die seite wechselt
  1709.         if (SnakeItemPosY[SnakeHeadID] == 11 && SnakeItemPosX[SnakeHeadID] > 40 && snakeOntop) {
  1710.           SnakeItemPosY[SnakeHeadID] = 10;
  1711.           SnakeItemPosX[SnakeHeadID] = SnakeItemPosX[SnakeHeadID] - 30;
  1712.           snakeDirection = SNAKE_DOWN;
  1713.           snakeOntop = false;
  1714.         }
  1715.         //verhalten der schlange wenn sie von der seite auf die obere matrix wechselt
  1716.         else if (SnakeItemPosY[SnakeHeadID] > 10 && !snakeOntop) {
  1717.           if (SnakeItemPosX[SnakeHeadID] > 0 && SnakeItemPosX[SnakeHeadID] <= 10) {
  1718.             SnakeItemPosY[SnakeHeadID] = SnakeItemPosX[SnakeHeadID];
  1719.             SnakeItemPosX[SnakeHeadID] = 41;
  1720.             snakeDirection = SNAKE_RIGHT;
  1721.           }
  1722.           else if (SnakeItemPosX[SnakeHeadID] > 10 && SnakeItemPosX[SnakeHeadID] <= 20) {
  1723.             SnakeItemPosY[SnakeHeadID] = 10;
  1724.             SnakeItemPosX[SnakeHeadID] = SnakeItemPosX[SnakeHeadID] + 30;
  1725.             snakeDirection = SNAKE_DOWN;
  1726.           }
  1727.           else if (SnakeItemPosX[SnakeHeadID] > 20 && SnakeItemPosX[SnakeHeadID] <= 30) {
  1728.             SnakeItemPosY[SnakeHeadID] = 31 - SnakeItemPosX[SnakeHeadID];
  1729.             SnakeItemPosX[SnakeHeadID] = 50;
  1730.             snakeDirection = SNAKE_LEFT;
  1731.           }
  1732.           else if (SnakeItemPosX[SnakeBackID] > 30 && SnakeItemPosX[SnakeHeadID] <= 40) {
  1733.             SnakeItemPosY[SnakeHeadID] = 1;
  1734.             SnakeItemPosX[SnakeHeadID] = 81 - SnakeItemPosX[SnakeHeadID];
  1735.             snakeDirection = SNAKE_UP;
  1736.           }
  1737.           snakeOntop = true;
  1738.         }
  1739.         break;
  1740.     }
  1741.     SnakeItems++;
  1742.     AddSnakeItem--;
  1743.   }
  1744.   //Abbruchbedingungen
  1745.   //Snake darf keine wand berühren
  1746.   //if (SnakeItemPosX[SnakeHeadID] > 0 && SnakeItemPosX[SnakeHeadID] <= BaneGridXmax && SnakeItemPosY[SnakeHeadID] > 0 && SnakeItemPosY[SnakeHeadID] <= BaneGridYmax) {
  1747.   //Snake darf nur oben und unten nicht berühren
  1748.   if (SnakeItemPosY[SnakeHeadID] > 0) {
  1749.     //Snake kommt nicht an den Rand
  1750.     if (Playfield[SnakeItemPosX[SnakeHeadID]][SnakeItemPosY[SnakeHeadID]] != SNAKE) {
  1751.       if (Playfield[SnakeItemPosX[SnakeHeadID]][SnakeItemPosY[SnakeHeadID]] == APPLE) {
  1752.         //Snake isst einen Apple
  1753.         playEffect(0);
  1754.         if (snakespeed > snakespeedmax + 10) snakespeed = snakespeed - 10;
  1755.         SnakeScore++;
  1756.         //add one
  1757.         AddSnakeItem += SnakeItemsToAddAtApple;
  1758.         AppleCount--;
  1759.       }
  1760.       if (AppleCount == 0) {
  1761.         //falls kein Apple mehr auf dem Spielfeld
  1762.         placeRandomApple();
  1763.       }
  1764.       render();
  1765.     }
  1766.     else {
  1767.       //Snake trifft sich selber
  1768.       GameOver();
  1769.     }
  1770.   }
  1771.   else {
  1772.     //Snake kommt an den Rand
  1773.     GameOver();
  1774.   }
  1775. }//end moveSnake()
  1776. void placeRandomApple() {
  1777.   byte x, y;
  1778.   x = random(1, BaneGridXmax);
  1779.   y = random(1, BaneGridYmax);
  1780.   //Suche eine Stelle an der noch keine led leuchtet
  1781.   while (Playfield[x][y] != BLANK) {
  1782.     x = random(1, BaneGridXmax);
  1783.     y = random(1, BaneGridYmax);
  1784.   }
  1785.   placeApple(x, y);
  1786. }//placeRandomApple()
  1787. void placeApple(byte x, byte y) {
  1788.   if (x > 0 && y > 0 && x <= BaneGridXmax && y <= BaneGridYmax) {
  1789.     //befinden sich x,y im Spielfeld
  1790.     Playfield[x][y] = APPLE;
  1791.     ApplePosX = x;
  1792.     ApplePosY = y;
  1793.     AppleCount++;
  1794.   }
  1795. }//end placeApple()
  1796. void removeApple() {
  1797.   Playfield[ApplePosX][ApplePosY] = BLANK;
  1798.   AppleCount = 0;
  1799. }//end removeapple()
  1800. void GameOver() {
  1801.   //if (SnakeItemPosX[SnakeHeadID] == 0) SnakeItemPosX[SnakeHeadID]++;
  1802.   //else if (SnakeItemPosX[SnakeHeadID] > BaneGridXmax) SnakeItemPosX[SnakeHeadID]--;
  1803.   if (SnakeItemPosY[SnakeHeadID] == 0) SnakeItemPosY[SnakeHeadID]++;
  1804.   else if (SnakeItemPosY[SnakeHeadID] > BaneGridYmax) SnakeItemPosY[SnakeHeadID]--;
  1805.   if (SnakeHeadID < SnakeItems)
  1806.     setXYPixel(SnakeItemPosX[SnakeHeadID + 1], SnakeItemPosY[SnakeHeadID + 1], CRGB::Green);
  1807.   else
  1808.     setXYPixel(SnakeItemPosX[1], SnakeItemPosY[1], CRGB::Green);
  1809.   setXYPixel(SnakeItemPosX[SnakeHeadID], SnakeItemPosY[SnakeHeadID], CRGB::Aqua);
  1810.   FastLED.show();
  1811.   snakerun = false;
  1812.   FastLED.clear();
  1813.   StringToTextBuffer("GAME OVER", 1);
  1814.   playEffect(2);
  1815.   matrix_top.DrawFilledRectangle(0, 0, 9, 9, CRGB::Red);
  1816.   while (scrollingtext.UpdateText() != -1) {
  1817.     FastLED.show();
  1818.     delay(50);
  1819.   }
  1820.   //  if (CheckHighscore(SnakeScore)) {
  1821.   //    //new Highscore
  1822.   //    InitText(Txt_NewHighscore);
  1823.   //    while (scrollingtext.UpdateText() != -1) {
  1824.   //      FastLED.show();
  1825.   //      delay(50);
  1826.   //    }
  1827.   //  }
  1828.   resetDos();
  1829.   doNoise = true;
  1830. }//end GameOver()
  1831. boolean CheckHighscore(long score) {
  1832.   boolean newhighscore = false;
  1833.   SnakeHighscore = EEPROMReadlong(ScoreEEPROMaddress);
  1834.   if (score > SnakeHighscore) {
  1835.     EEPROMWritelong(score, ScoreEEPROMaddress);
  1836.     newhighscore = true;
  1837.   }
  1838.   return newhighscore;
  1839. }//end CheckScore()
  1840. void setSnakeDirection(int dir) {
  1841.   if (dir == 1) {
  1842.     if (snakeOntop) {
  1843.       if (snakeDirection == SNAKE_RIGHT) snakeDirection = SNAKE_UP;
  1844.       else if (snakeDirection == SNAKE_DOWN) snakeDirection = SNAKE_RIGHT;
  1845.       else if (snakeDirection == SNAKE_LEFT) snakeDirection = SNAKE_DOWN;
  1846.       else if (snakeDirection == SNAKE_UP) snakeDirection = SNAKE_LEFT;
  1847.     } else {
  1848.       if (snakeDirection == SNAKE_RIGHT) snakeDirection = SNAKE_DOWN;
  1849.       else if (snakeDirection == SNAKE_DOWN) snakeDirection = SNAKE_LEFT;
  1850.       else if (snakeDirection == SNAKE_LEFT) snakeDirection = SNAKE_UP;
  1851.       else if (snakeDirection == SNAKE_UP) snakeDirection = SNAKE_RIGHT;
  1852.     }
  1853.   }
  1854.   else if (dir == -1) {
  1855.     if (snakeOntop) {
  1856.       if (snakeDirection == SNAKE_RIGHT) snakeDirection = SNAKE_DOWN;
  1857.       else if (snakeDirection == SNAKE_DOWN) snakeDirection = SNAKE_LEFT;
  1858.       else if (snakeDirection == SNAKE_LEFT) snakeDirection = SNAKE_UP;
  1859.       else if (snakeDirection == SNAKE_UP) snakeDirection = SNAKE_RIGHT;
  1860.     }
  1861.     else {
  1862.       if (snakeDirection == SNAKE_RIGHT) snakeDirection = SNAKE_UP;
  1863.       else if (snakeDirection == SNAKE_DOWN) snakeDirection = SNAKE_RIGHT;
  1864.       else if (snakeDirection == SNAKE_LEFT) snakeDirection = SNAKE_DOWN;
  1865.       else if (snakeDirection == SNAKE_UP) snakeDirection = SNAKE_LEFT;
  1866.     }
  1867.   }
  1868.   else {
  1869.     snakeDirection = -1;
  1870.   }
  1871. }//end setSnakeDirection()
复制代码


回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 16 小时前

【Arduino 动手做】100颗WS2812 LED 构建了一个 10x10 LED 魔方

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail