├── Breakout.h ├── ESP32Matrix.ino ├── LICENSE ├── LedControlWithBluetooth.aia ├── LedControlWithBluetooth.apk ├── Menu.h ├── Pixel.h ├── README.md ├── Snake.h ├── Tetris.h └── sprites ├── Smileys ├── Smiley1.png ├── Smiley2.png ├── Smiley3.png ├── Smiley4.png ├── Smiley5.png ├── Smiley6.png ├── Smiley7.png └── Smiley8.png ├── SpaceInvaders ├── invader1.gif ├── invader2.gif ├── invader3.gif └── invader4.gif └── alien └── alien.gif /Breakout.h: -------------------------------------------------------------------------------- 1 | class Breakout { 2 | 3 | int8_t sliderPosition; 4 | uint8_t ballRate; 5 | uint8_t level; 6 | uint8_t blocksRemaining; 7 | cLEDText ScrollingMsg; 8 | unsigned char txtBreakout[20]; 9 | 10 | const uint8_t paddleData[1] = { B8_1BIT(01111000) }; 11 | const uint8_t paddleMask[1] = { B8_1BIT(11111100) }; 12 | const struct CRGB paddleColorTable[1] = {CRGB(200,200,200)}; 13 | uint8_t paddleWidth = 6; 14 | uint8_t paddleHeight = 1; 15 | 16 | const uint8_t ballData[1] = { B8_1BIT(10000000) }; 17 | const uint8_t ballMask[1] = { B8_1BIT(10000000) }; 18 | const struct CRGB ballColorTable[1] = {CRGB(255,255,0)}; 19 | 20 | uint8_t plasmaLowHue = 100; 21 | uint8_t plasmaHighHue = 150; 22 | 23 | #define NUM_BLOCKS 11 24 | const uint8_t blockData[30] = { 25 | // Frame 1 26 | B8_3BIT(11110000), 27 | B8_3BIT(11110000), 28 | // Frame 2 29 | B8_3BIT(22220000), 30 | B8_3BIT(22220000), 31 | // Frame 3 32 | B8_3BIT(33330000), 33 | B8_3BIT(33330000), 34 | // Frame 4 35 | B8_3BIT(44440000), 36 | B8_3BIT(44440000), 37 | // Frame 5 38 | B8_3BIT(55550000), 39 | B8_3BIT(55550000), 40 | }; 41 | const uint8_t blockMask[10] = { 42 | // Frame 1 43 | B8_1BIT(11110000), 44 | B8_1BIT(11110000), 45 | // Frame 2 46 | B8_1BIT(11110000), 47 | B8_1BIT(11110000), 48 | // Frame 3 49 | B8_1BIT(11110000), 50 | B8_1BIT(11110000), 51 | // Frame 4 52 | B8_1BIT(11110000), 53 | B8_1BIT(11110000), 54 | // Frame 5 55 | B8_1BIT(11110000), 56 | B8_1BIT(11110000), 57 | }; 58 | const struct CRGB blockColors1[5] = { CRGB(13, 150, 255), CRGB(12, 227, 232), CRGB(0, 255, 149), CRGB(12, 232, 43), CRGB(93, 255, 2) }; 59 | const struct CRGB blockColors2[5] = { CRGB(255, 229, 3), CRGB(232, 172, 2), CRGB(255, 154, 11), CRGB(232, 84, 2), CRGB(255, 44, 13) }; 60 | const struct CRGB blockColors3[5] = { CRGB(255, 48, 13), CRGB(232, 12, 74), CRGB(247, 0, 255), CRGB(131, 12, 232), CRGB(32, 3, 255) }; 61 | const uint8_t blockWidth = 4; 62 | const uint8_t blockHeight = 2; 63 | 64 | cLEDSprites Sprites; 65 | cSprite sprPaddle; 66 | cSprite sprBall; 67 | cSprite Blocks[NUM_BLOCKS]; 68 | 69 | boolean startUp; 70 | 71 | uint16_t PlasmaTime, PlasmaShift; 72 | 73 | public: 74 | Breakout():Sprites(&leds){}; 75 | 76 | void setup(){ 77 | // Create paddle 78 | sprPaddle.Setup(paddleWidth, paddleHeight, paddleData, 1, _1BIT, paddleColorTable, paddleMask); 79 | sprPaddle.SetPosition(sliderPosition,0); 80 | sprPaddle.SetFrame(0); 81 | sprPaddle.SetMotion(0,0,0,0); 82 | sprPaddle.SetOptions(SPRITE_DETECT_COLLISION); 83 | Sprites.AddSprite(&sprPaddle); 84 | 85 | // Create ball 86 | sprBall.Setup(1, 1, ballData, 1, _1BIT, ballColorTable, ballMask); 87 | sprBall.SetPosition(0,7); 88 | sprBall.SetFrame(0); 89 | sprBall.SetMotion(0,0,0,0); 90 | sprBall.SetOptions(SPRITE_DETECT_EDGE | SPRITE_X_KEEPIN | SPRITE_Y_KEEPIN | SPRITE_DETECT_COLLISION); 91 | Sprites.AddSprite(&sprBall); 92 | 93 | level = 1; 94 | setupLevel(level); 95 | 96 | PlasmaShift = (random8(0, 5) * 32) + 64; 97 | PlasmaTime = 0; 98 | } 99 | 100 | boolean loop(){ 101 | 102 | // Fill background with dim plasma 103 | #define PLASMA_X_FACTOR 24 104 | #define PLASMA_Y_FACTOR 24 105 | for (int16_t x=0; x PlasmaTime) 120 | PlasmaShift = (random8(0, 5) * 32) + 64; 121 | 122 | if(startUp) { 123 | sprBall.SetPosition(sliderPosition+1,1); 124 | } 125 | 126 | if (SerialBT.available()) { 127 | byte keyPress = SerialBT.read(); 128 | // If it's greater than 20 we sent a character instead 129 | if(keyPress > 20) { 130 | if(startUp) { 131 | sprBall.SetMotion(1, ballRate, 1, ballRate); 132 | startUp = false; 133 | } 134 | switch((char)keyPress) { 135 | case 'm': 136 | currentApp = -1; 137 | return false; 138 | } 139 | } else { 140 | sliderPosition = (int8_t)keyPress - 1; 141 | } 142 | } 143 | 144 | //FastLED.clear(); 145 | 146 | sprPaddle.SetPosition(sliderPosition,0); 147 | 148 | Sprites.UpdateSprites(); 149 | Sprites.DetectCollisions(); 150 | 151 | if(sprBall.GetFlags() & SPRITE_COLLISION) { 152 | 153 | if(sprBall.m_Y > 1) { // If y < 1, ball is hitting paddle instead of a block 154 | 155 | for (uint8_t i = 0; i < NUM_BLOCKS; i++) { 156 | if(isSpriteAtXY(sprBall.m_X, sprBall.m_Y, blockWidth, blockHeight, &Blocks[i])) { 157 | 158 | if(Sprites.IsSprite(&Blocks[i])) { 159 | Sprites.RemoveSprite(&Blocks[i]); 160 | blocksRemaining--; 161 | 162 | if(blocksRemaining) { 163 | 164 | int8_t ballYChange = sprBall.GetYChange(); 165 | int8_t ballXChange = sprBall.GetXChange(); 166 | 167 | // Hits top row of pixels on a block and downwards, or bottom row and upwards 168 | if ((sprBall.m_Y % 2 != 0 && ballYChange == -1) || (sprBall.m_Y % 2 == 0 && ballYChange == 1)){ 169 | sprBall.SetMotion(ballXChange, ballRate, ballYChange * -1, ballRate); 170 | // If hitting straight on need to jump down two pixels instead of one 171 | if(ballXChange == 0) sprBall.m_Y += ballYChange * -2; 172 | else sprBall.m_Y += ballYChange * -1; 173 | } 174 | 175 | // Hits top row of pixels on a block and upwards, or bottom row and downwards 176 | else { 177 | sprBall.SetMotion(ballXChange * -1, ballRate, ballYChange, ballRate); 178 | sprBall.m_X += ballXChange * -1; 179 | } 180 | } 181 | 182 | else { 183 | level = ++level % 3; 184 | setupLevel(level); 185 | } 186 | } 187 | } 188 | } 189 | } 190 | } 191 | 192 | if(sprPaddle.GetFlags() & SPRITE_COLLISION) { 193 | // Check which pixel of the paddle it has hit 194 | uint16_t paddleHitPixel = sprBall.m_X - sprPaddle.m_X; 195 | 196 | // Remember current XChange for later 197 | int8_t lastXChange = sprBall.GetXChange(); 198 | 199 | // Adjust bounce up according to which pixel is hit on the paddle 200 | switch(paddleHitPixel) { 201 | case 0: 202 | case 1: 203 | sprBall.SetMotion(-1, ballRate, 1, ballRate); 204 | break; 205 | case 2: 206 | case 3: 207 | sprBall.SetMotion(0, ballRate, 1, ballRate); 208 | break; 209 | case 4: 210 | case 5: 211 | sprBall.SetMotion(1, ballRate, 1, ballRate); 212 | break; 213 | } 214 | 215 | // Pop ball up from paddle 216 | sprBall.m_Y += 2; 217 | if (lastXChange > 0) sprBall.m_X--; 218 | if (lastXChange < 0) sprBall.m_X++; 219 | sprBall.m_X += sprBall.GetXChange(); 220 | 221 | } 222 | else { 223 | // If hit bottom of screen and NOT collided with paddle, reset game 224 | if(sprBall.GetFlags() & SPRITE_EDGE_Y_MIN) { 225 | return false; 226 | } 227 | } 228 | 229 | Sprites.RenderSprites(); 230 | FastLED.show(); 231 | 232 | return true; 233 | } 234 | 235 | private: 236 | 237 | void setupLevel(uint8_t levelNo) { 238 | sliderPosition = 6; 239 | startUp = true; 240 | blocksRemaining = NUM_BLOCKS; 241 | sprBall.SetMotion(0,0,0,0); 242 | 243 | for (uint8_t i = 0; i < NUM_BLOCKS; i++){ 244 | 245 | uint8_t xPos, yPos; 246 | if(i < 4 ) xPos = i*4; 247 | else if (i > 6) xPos = (i-7)*4; 248 | else xPos = ((i-4)*4)+2; 249 | 250 | if(i < 4) yPos = 14; 251 | else if (i > 6) yPos = 10; 252 | else yPos = 12; 253 | 254 | switch (levelNo){ 255 | case 1: 256 | Blocks[i].Setup(blockWidth, blockHeight, blockData, 5, _3BIT, blockColors1, blockMask); 257 | ballRate = 15; // Lower is faster 258 | plasmaLowHue = 100; 259 | plasmaHighHue = 150; 260 | break; 261 | case 2: 262 | Blocks[i].Setup(blockWidth, blockHeight, blockData, 5, _3BIT, blockColors2, blockMask); 263 | ballRate = 12; 264 | plasmaLowHue = 10; 265 | plasmaHighHue = 64; 266 | break; 267 | case 3: 268 | Blocks[i].Setup(blockWidth, blockHeight, blockData, 5, _3BIT, blockColors3, blockMask); 269 | ballRate = 9; 270 | plasmaLowHue = 205; 271 | plasmaHighHue = 255; 272 | break; 273 | } 274 | 275 | Blocks[i].SetPosition(xPos, yPos); 276 | Blocks[i].SetFrame(i % 5); // Loop colours back around after 5 277 | Blocks[i].SetMotion(0,0,0,0); 278 | Blocks[i].SetOptions(SPRITE_DETECT_COLLISION); 279 | Sprites.AddSprite(&Blocks[i]); 280 | } 281 | } 282 | 283 | boolean isSpriteAtXY (uint8_t x, uint8_t y, uint8_t w, uint8_t h, cSprite *sprite) { 284 | if ( x >= sprite->m_X && x <= sprite->m_X + w - 1) { 285 | if (y >= sprite->m_Y && y <= sprite->m_Y + h - 1) { 286 | return true; 287 | } 288 | return false; 289 | } 290 | return false; 291 | } 292 | }; 293 | -------------------------------------------------------------------------------- /ESP32Matrix.ino: -------------------------------------------------------------------------------- 1 | // TETRIS 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "BluetoothSerial.h" 8 | 9 | #define LED_PIN 5 10 | #define COLOR_ORDER GRB 11 | #define CHIPSET WS2812B 12 | #define MATRIX_WIDTH 16 13 | #define MATRIX_HEIGHT 16 14 | #define MATRIX_TYPE HORIZONTAL_ZIGZAG_MATRIX 15 | 16 | #define APP_MENU -1 17 | #define APP_TETRIS 0 18 | #define APP_SNAKE 1 19 | #define APP_BREAKOUT 2 20 | #define APP_PIXEL 3 21 | 22 | int currentApp = APP_MENU; 23 | 24 | cLEDMatrix leds; 25 | BluetoothSerial SerialBT; 26 | 27 | // Bluetooth input 28 | enum btnInput {NONE, UP, DOWN, LEFT, RIGHT}; 29 | btnInput currentInput = NONE; 30 | 31 | #include "Tetris.h" 32 | #include "Menu.h" 33 | #include "Snake.h" 34 | #include "Breakout.h" 35 | #include "Pixel.h" 36 | 37 | void setup() 38 | { 39 | Serial.begin(115200); 40 | SerialBT.begin("ESP32Matrix"); 41 | 42 | FastLED.addLeds(leds[0], leds.Size()); 43 | FastLED.setMaxPowerInVoltsAndMilliamps(5,1000); 44 | FastLED.clear(true); 45 | FastLED.show(); 46 | } 47 | 48 | void loop() 49 | { 50 | switch (currentApp) { 51 | case APP_MENU : runMenu(); 52 | break; 53 | case APP_TETRIS : runTetris(); 54 | break; 55 | case APP_SNAKE : runSnake(); 56 | break; 57 | case APP_BREAKOUT : runBreakout(); 58 | break; 59 | case APP_PIXEL : runPixel(); 60 | } 61 | } 62 | 63 | void runMenu(){ 64 | bool isRunning = true; 65 | Menu menu = Menu(); 66 | menu.setup(); 67 | while (isRunning) { 68 | isRunning = menu.loop(); 69 | } 70 | } 71 | 72 | void runTetris(){ 73 | bool isRunning = true; 74 | Tetris tetris = Tetris(); 75 | tetris.setup(); 76 | while (isRunning) { 77 | isRunning = tetris.loop(); 78 | } 79 | } 80 | 81 | void runSnake(){ 82 | bool isRunning = true; 83 | Snake snake = Snake(); 84 | snake.setup(); 85 | while (isRunning) { 86 | isRunning = snake.loop(); 87 | } 88 | } 89 | 90 | void runBreakout(){ 91 | bool isRunning = true; 92 | Breakout breakout = Breakout(); 93 | breakout.setup(); 94 | while (isRunning) { 95 | isRunning = breakout.loop(); 96 | } 97 | } 98 | 99 | void runPixel(){ 100 | bool isRunning = true; 101 | Pixel pixel = Pixel(); 102 | pixel.setup(); 103 | while (isRunning) { 104 | isRunning = pixel.loop(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 s-marley 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LedControlWithBluetooth.aia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-marley/ESP32Matrix/9f119ebde22e3ec025dcf5a318590e6b1c0e4e1f/LedControlWithBluetooth.aia -------------------------------------------------------------------------------- /LedControlWithBluetooth.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-marley/ESP32Matrix/9f119ebde22e3ec025dcf5a318590e6b1c0e4e1f/LedControlWithBluetooth.apk -------------------------------------------------------------------------------- /Menu.h: -------------------------------------------------------------------------------- 1 | class Menu { 2 | cLEDText ScrollingMsg; 3 | unsigned char txtMenu[12]; 4 | int menuItem; 5 | 6 | public: 7 | Menu(){ 8 | menuItem = 0; 9 | }; 10 | 11 | void setup() { 12 | sprintf((char *)txtMenu, " "); 13 | ScrollingMsg.SetFont(MatriseFontData); 14 | ScrollingMsg.Init(&leds, leds.Width(), ScrollingMsg.FontHeight() + 1, 0, 5); 15 | ScrollingMsg.SetBackgroundMode(BACKGND_ERASE); 16 | ScrollingMsg.SetScrollDirection(SCROLL_LEFT); 17 | ScrollingMsg.SetFrameRate(5); 18 | ScrollingMsg.SetTextColrOptions(COLR_RGB | COLR_SINGLE, 0xff, 0x00, 0xff); 19 | FastLED.clear(); 20 | 21 | menuChanged(0); 22 | } 23 | 24 | boolean loop() { 25 | // Rainbow background 26 | uint32_t ms = millis(); 27 | int32_t yHueDelta32 = ((int32_t)cos16( ms * 27 ) * (350 / leds.Width())); 28 | int32_t xHueDelta32 = ((int32_t)cos16( ms * 39 ) * (310 / leds.Height())); 29 | DrawOneFrame( ms / 65536, yHueDelta32 / 32768, xHueDelta32 / 32768); 30 | 31 | //Black bar behind text 32 | for (int i = 48; i<191; i++) { 33 | leds(i) = CRGB::Black; 34 | } 35 | 36 | displayMenu(); 37 | 38 | if(SerialBT.available()){ 39 | char keyPress = (char)SerialBT.read(); 40 | switch(keyPress) { 41 | case 'a': 42 | if(--menuItem < 0 ) menuItem = 2; 43 | menuChanged(menuItem); 44 | break; 45 | case 'd': 46 | menuItem = ++menuItem % 4; 47 | menuChanged(menuItem); 48 | break; 49 | case 'r': 50 | case 'y': 51 | case 'g': 52 | case 'b': 53 | currentApp = menuItem; 54 | return false; 55 | } 56 | } 57 | return true; 58 | } 59 | 60 | void DrawOneFrame( byte startHue8, int8_t yHueDelta8, int8_t xHueDelta8) 61 | { 62 | byte lineStartHue = startHue8; 63 | for( byte y = 0; y < leds.Height(); y++) { 64 | lineStartHue += yHueDelta8; 65 | byte pixelHue = lineStartHue; 66 | for( byte x = 0; x < leds.Width(); x++) { 67 | pixelHue += xHueDelta8; 68 | leds(x, y) = CHSV( pixelHue, 255, 255); 69 | } 70 | } 71 | } 72 | 73 | private: 74 | void menuChanged(int menuItem) { 75 | switch (menuItem) { 76 | case 0: 77 | //Tetris highlighted 78 | sprintf((char *)txtMenu, " TETRIS "); 79 | break; 80 | case 1: 81 | //Snake highlighted 82 | sprintf((char *)txtMenu, " SNAKE "); 83 | break; 84 | case 2: 85 | //Breakout highlighted 86 | sprintf((char *)txtMenu, " BREAKOUT "); 87 | break; 88 | case 3: 89 | //Animation highlighted 90 | sprintf((char *)txtMenu, " PIXEL ART "); 91 | break; 92 | } 93 | ScrollingMsg.SetText((unsigned char *)txtMenu, sizeof(txtMenu)-1); 94 | } 95 | 96 | void displayMenu() { 97 | if(ScrollingMsg.UpdateText() == -1) 98 | ScrollingMsg.SetText((unsigned char *)txtMenu, sizeof(txtMenu)-1); 99 | else 100 | FastLED.show(); 101 | } 102 | 103 | }; 104 | -------------------------------------------------------------------------------- /Pixel.h: -------------------------------------------------------------------------------- 1 | class Pixel{ 2 | 3 | const uint8_t SpritealienData[96] = { 4 | B8_2BIT(33333111),B8_2BIT(11133333), 5 | B8_2BIT(33311111),B8_2BIT(11111333), 6 | B8_2BIT(33111111),B8_2BIT(11111133), 7 | B8_2BIT(31111111),B8_2BIT(11111113), 8 | B8_2BIT(31111111),B8_2BIT(11111113), 9 | B8_2BIT(10000111),B8_2BIT(11100001), 10 | B8_2BIT(10000011),B8_2BIT(11000001), 11 | B8_2BIT(10000001),B8_2BIT(10000001), 12 | B8_2BIT(10000001),B8_2BIT(10000001), 13 | B8_2BIT(31000001),B8_2BIT(10000013), 14 | B8_2BIT(31100001),B8_2BIT(10000113), 15 | B8_2BIT(33111111),B8_2BIT(11111133), 16 | B8_2BIT(33111111),B8_2BIT(11111133), 17 | B8_2BIT(33311111),B8_2BIT(11111333), 18 | B8_2BIT(33331111),B8_2BIT(11113333), 19 | B8_2BIT(33333311),B8_2BIT(11333333) }; 20 | const uint8_t SpritealienMask[32] = { 21 | B8_1BIT(00000111),B8_1BIT(11100000), 22 | B8_1BIT(00011111),B8_1BIT(11111000), 23 | B8_1BIT(00111111),B8_1BIT(11111100), 24 | B8_1BIT(01111111),B8_1BIT(11111110), 25 | B8_1BIT(01111111),B8_1BIT(11111110), 26 | B8_1BIT(10000111),B8_1BIT(11100001), 27 | B8_1BIT(10110011),B8_1BIT(11001101), 28 | B8_1BIT(10100001),B8_1BIT(10000101), 29 | B8_1BIT(10000101),B8_1BIT(10100001), 30 | B8_1BIT(01000001),B8_1BIT(10000010), 31 | B8_1BIT(01100001),B8_1BIT(10000110), 32 | B8_1BIT(00111111),B8_1BIT(11111100), 33 | B8_1BIT(00111111),B8_1BIT(11111100), 34 | B8_1BIT(00011111),B8_1BIT(11111000), 35 | B8_1BIT(00001111),B8_1BIT(11110000), 36 | B8_1BIT(00000011),B8_1BIT(11000000) }; 37 | 38 | const struct CRGB SpritealienCols[4] = { CRGB(2,103,2), CRGB(200,200,200), CRGB(0,0,0)}; 39 | 40 | const uint8_t SpriteSmileyData[480] = { 41 | // Frame 1 done 42 | B8_2BIT(00000111),B8_2BIT(11000000), 43 | B8_2BIT(00011111),B8_2BIT(11110000), 44 | B8_2BIT(00111111),B8_2BIT(11111000), 45 | B8_2BIT(01111211),B8_2BIT(12111100), 46 | B8_2BIT(01111211),B8_2BIT(12111100), 47 | B8_2BIT(11111211),B8_2BIT(12111110), 48 | B8_2BIT(11111111),B8_2BIT(11111110), 49 | B8_2BIT(11111111),B8_2BIT(11111110), 50 | B8_2BIT(11211111),B8_2BIT(11112110), 51 | B8_2BIT(11211111),B8_2BIT(11112110), 52 | B8_2BIT(01121111),B8_2BIT(11121100), 53 | B8_2BIT(01112111),B8_2BIT(11211100), 54 | B8_2BIT(00111222),B8_2BIT(22111000), 55 | B8_2BIT(00011111),B8_2BIT(11110000), 56 | B8_2BIT(00000111),B8_2BIT(11000000), 57 | // Frame 2 done 58 | B8_2BIT(00000111),B8_2BIT(11000000), 59 | B8_2BIT(00011111),B8_2BIT(11110000), 60 | B8_2BIT(00111111),B8_2BIT(12111000), 61 | B8_2BIT(01111111),B8_2BIT(21111100), 62 | B8_2BIT(01121112),B8_2BIT(11111100), 63 | B8_2BIT(11211111),B8_2BIT(11112110), 64 | B8_2BIT(11211111),B8_2BIT(11121110), 65 | B8_2BIT(11211111),B8_2BIT(11211110), 66 | B8_2BIT(11211111),B8_2BIT(11111110), 67 | B8_2BIT(11121111),B8_2BIT(11111110), 68 | B8_2BIT(01121111),B8_2BIT(11111100), 69 | B8_2BIT(01112211),B8_2BIT(11211100), 70 | B8_2BIT(00111122),B8_2BIT(22111000), 71 | B8_2BIT(00011111),B8_2BIT(11110000), 72 | B8_2BIT(00000111),B8_2BIT(11000000), 73 | // Frame 3 done 74 | B8_2BIT(00000111),B8_2BIT(11000000), 75 | B8_2BIT(00011111),B8_2BIT(11110000), 76 | B8_2BIT(00111221),B8_2BIT(11111000), 77 | B8_2BIT(01112111),B8_2BIT(11111100), 78 | B8_2BIT(01121111),B8_2BIT(11111100), 79 | B8_2BIT(11211111),B8_2BIT(12221110), 80 | B8_2BIT(11211111),B8_2BIT(11111110), 81 | B8_2BIT(11211111),B8_2BIT(11111110), 82 | B8_2BIT(11211111),B8_2BIT(11111110), 83 | B8_2BIT(11211111),B8_2BIT(12221110), 84 | B8_2BIT(01121111),B8_2BIT(11111100), 85 | B8_2BIT(01112111),B8_2BIT(11111100), 86 | B8_2BIT(00111221),B8_2BIT(11111000), 87 | B8_2BIT(00011111),B8_2BIT(11110000), 88 | B8_2BIT(00000111),B8_2BIT(11000000), 89 | // Frame 4 done 90 | B8_2BIT(00000111),B8_2BIT(11000000), 91 | B8_2BIT(00011111),B8_2BIT(11110000), 92 | B8_2BIT(00111122),B8_2BIT(22111000), 93 | B8_2BIT(01112211),B8_2BIT(11211100), 94 | B8_2BIT(01121111),B8_2BIT(11111100), 95 | B8_2BIT(11121111),B8_2BIT(11111110), 96 | B8_2BIT(11211111),B8_2BIT(11111110), 97 | B8_2BIT(11211111),B8_2BIT(11211110), 98 | B8_2BIT(11211111),B8_2BIT(11121110), 99 | B8_2BIT(11211111),B8_2BIT(11112110), 100 | B8_2BIT(01121112),B8_2BIT(11111100), 101 | B8_2BIT(01111111),B8_2BIT(21111100), 102 | B8_2BIT(00111111),B8_2BIT(12111000), 103 | B8_2BIT(00011111),B8_2BIT(11110000), 104 | B8_2BIT(00000111),B8_2BIT(11000000), 105 | // Frame 5 done 106 | B8_2BIT(00000111),B8_2BIT(11000000), 107 | B8_2BIT(00011111),B8_2BIT(11110000), 108 | B8_2BIT(00111222),B8_2BIT(22111000), 109 | B8_2BIT(01112111),B8_2BIT(11211100), 110 | B8_2BIT(01121111),B8_2BIT(11121100), 111 | B8_2BIT(11211111),B8_2BIT(11112110), 112 | B8_2BIT(11211111),B8_2BIT(11112110), 113 | B8_2BIT(11111111),B8_2BIT(11111110), 114 | B8_2BIT(11111111),B8_2BIT(11111110), 115 | B8_2BIT(11111211),B8_2BIT(12111110), 116 | B8_2BIT(01111211),B8_2BIT(12111100), 117 | B8_2BIT(01111211),B8_2BIT(12111100), 118 | B8_2BIT(00111111),B8_2BIT(11111000), 119 | B8_2BIT(00011111),B8_2BIT(11110000), 120 | B8_2BIT(00000111),B8_2BIT(11000000), 121 | // Frame 6 done 122 | B8_2BIT(00000111),B8_2BIT(11000000), 123 | B8_2BIT(00011111),B8_2BIT(11110000), 124 | B8_2BIT(00111222),B8_2BIT(21111000), 125 | B8_2BIT(01112111),B8_2BIT(12211100), 126 | B8_2BIT(01111111),B8_2BIT(11121100), 127 | B8_2BIT(11111111),B8_2BIT(11121110), 128 | B8_2BIT(11111111),B8_2BIT(11112110), 129 | B8_2BIT(11112111),B8_2BIT(11112110), 130 | B8_2BIT(11121111),B8_2BIT(11112110), 131 | B8_2BIT(11211111),B8_2BIT(11112110), 132 | B8_2BIT(01111112),B8_2BIT(11121100), 133 | B8_2BIT(01111121),B8_2BIT(11111100), 134 | B8_2BIT(00111211),B8_2BIT(11111000), 135 | B8_2BIT(00011111),B8_2BIT(11110000), 136 | B8_2BIT(00000111),B8_2BIT(11000000), 137 | // Frame 7 done 138 | B8_2BIT(00000111),B8_2BIT(11000000), 139 | B8_2BIT(00011111),B8_2BIT(11110000), 140 | B8_2BIT(00111111),B8_2BIT(22111000), 141 | B8_2BIT(01111111),B8_2BIT(11211100), 142 | B8_2BIT(01111111),B8_2BIT(11121100), 143 | B8_2BIT(11122211),B8_2BIT(11112110), 144 | B8_2BIT(11111111),B8_2BIT(11112110), 145 | B8_2BIT(11111111),B8_2BIT(11112110), 146 | B8_2BIT(11111111),B8_2BIT(11112110), 147 | B8_2BIT(11122211),B8_2BIT(11112110), 148 | B8_2BIT(01111111),B8_2BIT(11121100), 149 | B8_2BIT(01111111),B8_2BIT(11211100), 150 | B8_2BIT(00111111),B8_2BIT(22111000), 151 | B8_2BIT(00011111),B8_2BIT(11110000), 152 | B8_2BIT(00000111),B8_2BIT(11000000), 153 | // Frame 8 done 154 | B8_2BIT(00000111),B8_2BIT(11000000), 155 | B8_2BIT(00011111),B8_2BIT(11110000), 156 | B8_2BIT(00111211),B8_2BIT(11111000), 157 | B8_2BIT(01111121),B8_2BIT(11111100), 158 | B8_2BIT(01111112),B8_2BIT(11121100), 159 | B8_2BIT(11211111),B8_2BIT(11112110), 160 | B8_2BIT(11121111),B8_2BIT(11112110), 161 | B8_2BIT(11112111),B8_2BIT(11112110), 162 | B8_2BIT(11111111),B8_2BIT(11112110), 163 | B8_2BIT(11111111),B8_2BIT(11121110), 164 | B8_2BIT(01111111),B8_2BIT(11121100), 165 | B8_2BIT(01112111),B8_2BIT(12211100), 166 | B8_2BIT(00111222),B8_2BIT(21111000), 167 | B8_2BIT(00011111),B8_2BIT(11110000), 168 | B8_2BIT(00000111),B8_2BIT(11000000)}; 169 | 170 | const uint8_t SpriteSmileyMask[30] = { 171 | B8_1BIT(00000111),B8_1BIT(11000000), 172 | B8_1BIT(00011111),B8_1BIT(11110000), 173 | B8_1BIT(00111111),B8_1BIT(11111000), 174 | B8_1BIT(11111111),B8_1BIT(11111110), 175 | B8_1BIT(11111111),B8_1BIT(11111110), 176 | B8_1BIT(11111111),B8_1BIT(11111110), 177 | B8_1BIT(11111111),B8_1BIT(11111110), 178 | B8_1BIT(11111111),B8_1BIT(11111110), 179 | B8_1BIT(11111111),B8_1BIT(11111110), 180 | B8_1BIT(11111111),B8_1BIT(11111110), 181 | B8_1BIT(11111111),B8_1BIT(11111110), 182 | B8_1BIT(11111111),B8_1BIT(11111110), 183 | B8_1BIT(11111111),B8_1BIT(11111110), 184 | B8_1BIT(00011111),B8_1BIT(11110000), 185 | B8_1BIT(00000111),B8_1BIT(11000000) }; 186 | 187 | const struct CRGB SpriteSmileyCols[2] = { CRGB(255,255,0), CRGB(0,0,0) }; 188 | 189 | const uint8_t SpriteInvaderData[32] = { 190 | // Frame 1 191 | B8_1BIT(00100000),B8_1BIT(10000000), 192 | B8_1BIT(00010001),B8_1BIT(00000000), 193 | B8_1BIT(00111111),B8_1BIT(10000000), 194 | B8_1BIT(01101110),B8_1BIT(11000000), 195 | B8_1BIT(11111111),B8_1BIT(11100000), 196 | B8_1BIT(10111111),B8_1BIT(10100000), 197 | B8_1BIT(10100000),B8_1BIT(10100000), 198 | B8_1BIT(00011011),B8_1BIT(00000000), 199 | // Frame 2 200 | B8_1BIT(00100000),B8_1BIT(10000000), 201 | B8_1BIT(00010001),B8_1BIT(00000000), 202 | B8_1BIT(10111111),B8_1BIT(10100000), 203 | B8_1BIT(11101110),B8_1BIT(11100000), 204 | B8_1BIT(11111111),B8_1BIT(11100000), 205 | B8_1BIT(01111111),B8_1BIT(11000000), 206 | B8_1BIT(00100000),B8_1BIT(10000000), 207 | B8_1BIT(01000000),B8_1BIT(01000000) }; 208 | const uint8_t SpriteInvaderMask[32] = { 209 | B8_1BIT(00100000),B8_1BIT(10000000), 210 | B8_1BIT(00010001),B8_1BIT(00000000), 211 | B8_1BIT(00111111),B8_1BIT(10000000), 212 | B8_1BIT(01101110),B8_1BIT(11000000), 213 | B8_1BIT(11111111),B8_1BIT(11100000), 214 | B8_1BIT(10111111),B8_1BIT(10100000), 215 | B8_1BIT(10100000),B8_1BIT(10100000), 216 | B8_1BIT(00011011),B8_1BIT(00000000), 217 | // Frame 2 218 | B8_1BIT(00100000),B8_1BIT(10000000), 219 | B8_1BIT(00010001),B8_1BIT(00000000), 220 | B8_1BIT(10111111),B8_1BIT(10100000), 221 | B8_1BIT(11101110),B8_1BIT(11100000), 222 | B8_1BIT(11111111),B8_1BIT(11100000), 223 | B8_1BIT(01111111),B8_1BIT(11000000), 224 | B8_1BIT(00100000),B8_1BIT(10000000), 225 | B8_1BIT(01000000),B8_1BIT(01000000) }; 226 | 227 | struct CRGB SpriteInvaderCols[1] = { CRGB(0,0,0) }; 228 | 229 | const uint8_t Spriteinvader3Data[32] = { 230 | // Frame 2 231 | B8_1BIT(00001111),B8_1BIT(00000000), 232 | B8_1BIT(01111111),B8_1BIT(11100000), 233 | B8_1BIT(11111111),B8_1BIT(11110000), 234 | B8_1BIT(11100110),B8_1BIT(01110000), 235 | B8_1BIT(11111111),B8_1BIT(11110000), 236 | B8_1BIT(00011001),B8_1BIT(10000000), 237 | B8_1BIT(00110110),B8_1BIT(11000000), 238 | B8_1BIT(11000000),B8_1BIT(00110000), 239 | // Frame 2 240 | B8_1BIT(00001111),B8_1BIT(00000000), 241 | B8_1BIT(01111111),B8_1BIT(11100000), 242 | B8_1BIT(11111111),B8_1BIT(11110000), 243 | B8_1BIT(11100110),B8_1BIT(01110000), 244 | B8_1BIT(11111111),B8_1BIT(11110000), 245 | B8_1BIT(00111001),B8_1BIT(11000000), 246 | B8_1BIT(01100110),B8_1BIT(01100000), 247 | B8_1BIT(00110000),B8_1BIT(11000000) }; 248 | const uint8_t Spriteinvader3Mask[32] = { 249 | B8_1BIT(00100000),B8_1BIT(10000000), 250 | B8_1BIT(00010001),B8_1BIT(00000000), 251 | B8_1BIT(00111111),B8_1BIT(10000000), 252 | B8_1BIT(01101110),B8_1BIT(11000000), 253 | B8_1BIT(11111111),B8_1BIT(11100000), 254 | B8_1BIT(10111111),B8_1BIT(10100000), 255 | B8_1BIT(10100000),B8_1BIT(10100000), 256 | B8_1BIT(00011011),B8_1BIT(00000000), 257 | // Frame 2 258 | B8_1BIT(00100000),B8_1BIT(10000000), 259 | B8_1BIT(00010001),B8_1BIT(00000000), 260 | B8_1BIT(10111111),B8_1BIT(10100000), 261 | B8_1BIT(11101110),B8_1BIT(11100000), 262 | B8_1BIT(11111111),B8_1BIT(11100000), 263 | B8_1BIT(01111111),B8_1BIT(11000000), 264 | B8_1BIT(00100000),B8_1BIT(10000000), 265 | B8_1BIT(01000000),B8_1BIT(01000000) }; 266 | 267 | struct CRGB Spriteinvader3Cols[1] = { CRGB(255,255,255) }; 268 | 269 | uint8_t rectHue = 0; 270 | uint8_t seconds = 0; 271 | uint8_t secondsTrigger = 0; 272 | 273 | cLEDSprites Sprites; 274 | cSprite sprSmiley; 275 | cSprite sprInvader; 276 | cSprite sprInvader3; 277 | cSprite sprAlien; 278 | 279 | uint16_t steper = random8(2, 8); 280 | uint8_t h = random(64); 281 | uint8_t sprite = 4; 282 | uint8_t background = 0; 283 | 284 | uint16_t PlasmaTime, PlasmaShift; 285 | uint8_t plasmaLowHue, plasmaHighHue, plasmaBrightness; 286 | 287 | public: 288 | Pixel():Sprites(&leds){}; 289 | 290 | void setup(){ 291 | PlasmaShift = (random8(0, 5) * 32) + 64; 292 | PlasmaTime = 0; 293 | plasmaLowHue = 100; 294 | plasmaHighHue = 150; 295 | plasmaBrightness = 200; 296 | //setupSmiley(); 297 | setupAlien(); 298 | FastLED.clear(); 299 | } 300 | 301 | boolean loop(){ 302 | 303 | 304 | EVERY_N_MILLISECONDS(100) { 305 | // Cycle plasma colour 306 | plasmaLowHue++; 307 | plasmaHighHue++; 308 | } 309 | 310 | EVERY_N_SECONDS(1) { 311 | seconds++; 312 | } 313 | 314 | if(seconds % 13 == 0 && secondsTrigger != seconds) { 315 | secondsTrigger = seconds; 316 | background = ++background % 4; 317 | } 318 | 319 | switch (background) { 320 | case 0: 321 | corner(); 322 | break; 323 | case 1: 324 | square(); 325 | break; 326 | case 2: 327 | squares(); 328 | break; 329 | case 3: 330 | plasma(); 331 | break; 332 | } 333 | 334 | if(seconds % 5 == 0 && secondsTrigger != seconds) { 335 | secondsTrigger = seconds; 336 | Sprites.RemoveAllSprites(); 337 | sprite = ++sprite % 6; 338 | switch (sprite){ 339 | case 0: 340 | setupSmiley(); 341 | break; 342 | case 1: 343 | // Do nothing 344 | break; 345 | case 2: 346 | setupInvader(); 347 | setupInvader3(); 348 | break; 349 | case 3: 350 | // Do nothing 351 | break; 352 | case 4: 353 | setupAlien(); 354 | break; 355 | case 5: 356 | // Do nothing 357 | break; 358 | } 359 | } 360 | 361 | if (SerialBT.available()) { 362 | char keyPress = (char)SerialBT.read(); 363 | switch(keyPress) { 364 | case 'm': 365 | currentApp = -1; 366 | return false; 367 | } 368 | } 369 | 370 | plasmaBrightness = 200; 371 | 372 | switch (sprite) { 373 | case 0: // Rolling acid smiley 374 | if(sprSmiley.m_X > leds.Width()) { 375 | sprSmiley.m_X = -15; 376 | } 377 | break; 378 | case 2: //Invaders 379 | background = 3; 380 | plasmaBrightness = 60; 381 | break; 382 | case 4: // Alien face 383 | background = 2; 384 | break; 385 | } 386 | 387 | Sprites.UpdateSprites(); 388 | Sprites.RenderSprites(); 389 | 390 | FastLED.show(); 391 | 392 | return true; 393 | } 394 | 395 | private: 396 | 397 | void setupAlien() { 398 | sprAlien.Setup(16, 16, SpritealienData, 1, _2BIT, SpritealienCols, SpritealienMask); 399 | sprAlien.SetPosition(0,0); 400 | sprAlien.SetFrame(0,0); 401 | sprAlien.SetMotion(0,15,0,0); 402 | Sprites.AddSprite(&sprAlien); 403 | } 404 | 405 | void setupSmiley() { 406 | sprSmiley.Setup(15, 15, SpriteSmileyData, 8, _2BIT, SpriteSmileyCols, SpriteSmileyMask); 407 | sprSmiley.SetPosition(-15,0); 408 | sprSmiley.SetFrame(0,30); 409 | sprSmiley.SetMotion(1,15,0,0); 410 | Sprites.AddSprite(&sprSmiley); 411 | } 412 | 413 | void setupInvader() { 414 | SpriteInvaderCols[0] = CRGB(random8(),random8(),random8()); 415 | sprInvader.Setup(11, 8, SpriteInvaderData, 2, _1BIT, SpriteInvaderCols, SpriteInvaderMask); 416 | sprInvader.SetPosition(random8(0,5),random8(0,8)); 417 | sprInvader.SetFrame(0,20); 418 | sprInvader.SetMotion(1,15,1,15); 419 | sprInvader.SetOptions(SPRITE_DETECT_EDGE | SPRITE_X_KEEPIN | SPRITE_Y_KEEPIN ); 420 | Sprites.AddSprite(&sprInvader); 421 | } 422 | 423 | void setupInvader3() { 424 | Spriteinvader3Cols[0] = CRGB(random8(),random8(),random8()); 425 | sprInvader3.Setup(12, 8, Spriteinvader3Data, 1, _1BIT, Spriteinvader3Cols, Spriteinvader3Mask); 426 | sprInvader3.SetPosition(random8(0,5),random8(0,8)); 427 | sprInvader3.SetFrame(0,15); 428 | sprInvader3.SetMotion(-1,15,-1,15); 429 | sprInvader3.SetOptions(SPRITE_DETECT_EDGE | SPRITE_X_KEEPIN | SPRITE_Y_KEEPIN ); 430 | Sprites.AddSprite(&sprInvader3); 431 | } 432 | 433 | void corner() { 434 | leds.DrawFilledRectangle(0 , 0, leds.Width(), leds.Height(), CHSV(h, 255, 255)); 435 | h += steper; 436 | 437 | for (uint16_t y = 0; y <= leds.Width() / 2 - 1 ; y++) 438 | { 439 | leds.DrawFilledCircle(leds.Width() / 2, leds.Height() / 2, (leds.Width() / 2 + 1 - y) / 3, CHSV(h + y * steper, 255, 255)); 440 | leds.DrawFilledCircle(0, 0, leds.Width() / 2 - y, CHSV(64 + h + y * steper, 255, 255)); 441 | leds.DrawFilledCircle(0, leds.Height() - 1, leds.Width() / 2 - y, CHSV(h - 64 + y * steper, 255, 255)); 442 | leds.DrawFilledCircle(leds.Width() - 1, 0, leds.Width() / 2 - y, CHSV(h - 64 + y * steper, 255, 255)); 443 | leds.DrawFilledCircle(leds.Width() - 1, leds.Height() - 1, leds.Width() / 2 - y, CHSV(h + 64 + y * steper, 255, 255)); 444 | } 445 | } 446 | 447 | void square() { 448 | for (uint8_t i = 0; i <= leds.Width(); i++) { 449 | leds.DrawRectangle(i,i,leds.Width() - 1 - i, leds.Height() - 1 - i, CHSV(rectHue + (i*30),255,255)); 450 | } 451 | rectHue++; 452 | } 453 | 454 | void squares() { 455 | //Bottom left 456 | for (uint8_t i = 0; i <= 7; i++) { 457 | leds.DrawRectangle(i,i, 6 - i, 6 - i, CHSV(rectHue + (i*15),255,255)); 458 | } 459 | //Bottom right 460 | for (uint8_t i = 0; i <= 7; i++) { 461 | leds.DrawRectangle(i+9,i, 15 - i, 6 - i, CHSV(rectHue + (i*15),255,255)); 462 | } 463 | // Top left 464 | for (uint8_t i = 0; i <= 7; i++) { 465 | leds.DrawRectangle(i,i+9, 6 - i, 15 - i, CHSV(rectHue + (i*15),255,255)); 466 | } 467 | // Top right 468 | for (uint8_t i = 0; i <= 7; i++) { 469 | leds.DrawRectangle(i+9,i+9, 15 - i, 15 - i, CHSV(rectHue + (i*15),255,255)); 470 | } 471 | rectHue++; 472 | } 473 | 474 | void plasma() { 475 | // Fill background with dim plasma 476 | #define PLASMA_X_FACTOR 24 477 | #define PLASMA_Y_FACTOR 24 478 | for (int16_t x=0; x PlasmaTime) 490 | PlasmaShift = (random8(0, 5) * 32) + 64; 491 | } 492 | 493 | }; 494 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32Matrix 2 | ESP32 code to run on a 16x16 WS2812B matrix with Tetris, Snake, Breakout and Bluetooth control via an Android app. 3 | See this youtube video for more information: https://www.youtube.com/watch?v=apmOSQmeKJA 4 | -------------------------------------------------------------------------------- /Snake.h: -------------------------------------------------------------------------------- 1 | class Snake{ 2 | 3 | #define MAX_SNAKE_LENGTH 255 4 | 5 | cLEDText ScrollingMsg; 6 | CRGBPalette16 snakePalette; 7 | unsigned char txtSnake[17]; 8 | unsigned long previousMillis; 9 | uint8_t interval, startIndex, fruitHue; 10 | uint32_t count10ms; 11 | uint8_t snakeLength, snakeX[MAX_SNAKE_LENGTH], snakeY[MAX_SNAKE_LENGTH]; 12 | uint8_t fruitX, fruitY; 13 | struct Explode{ 14 | uint8_t x, y, r, hue; 15 | } ex; 16 | boolean paused; 17 | uint16_t PlasmaTime, PlasmaShift; 18 | const uint8_t PlasmaLowHue = 100; 19 | const uint8_t PlasmaHighHue = 150; 20 | 21 | public: 22 | Snake(){ 23 | previousMillis = 0; 24 | interval = 250; 25 | snakeLength = 1; 26 | snakePalette = RainbowColors_p; 27 | startIndex = 0; 28 | fruitHue = 0; 29 | ex.r = 10; 30 | count10ms = 0; 31 | paused = false; 32 | }; 33 | 34 | void setup() { 35 | sprintf((char *)txtSnake, " "); 36 | ScrollingMsg.SetFont(MatriseFontData); 37 | ScrollingMsg.Init(&leds, leds.Width(), ScrollingMsg.FontHeight() + 1, 0, 5); 38 | ScrollingMsg.SetBackgroundMode(BACKGND_ERASE); 39 | ScrollingMsg.SetScrollDirection(SCROLL_LEFT); 40 | ScrollingMsg.SetFrameRate(1); 41 | ScrollingMsg.SetTextColrOptions(COLR_RGB | COLR_SINGLE, 0x00, 0x99, 0x33); 42 | 43 | currentInput = NONE; 44 | 45 | PlasmaShift = (random8(0, 5) * 32) + 64; 46 | PlasmaTime = 0; 47 | 48 | // Start snake in the middle 49 | snakeX[0] = leds.Width() / 2; 50 | snakeY[0] = leds.Height() / 2; 51 | 52 | makeFruit(); 53 | } 54 | 55 | boolean loop() { 56 | // Fill background with dim plasma 57 | #define PLASMA_X_FACTOR 48 58 | #define PLASMA_Y_FACTOR 48 59 | for (int16_t x=0; x PlasmaTime) { 72 | PlasmaShift = (random8(0, 5) * 32) + 64; 73 | } 74 | 75 | EVERY_N_MILLISECONDS(10){ 76 | count10ms++; 77 | } 78 | 79 | unsigned long currentMillis = millis(); 80 | 81 | if(paused) { 82 | //Black bar behind text 83 | for (int i = 48; i<191; i++) { 84 | leds(i) = CRGB::Black; 85 | } 86 | if(ScrollingMsg.UpdateText() == -1) { 87 | ScrollingMsg.SetText((unsigned char *)txtSnake, sizeof(txtSnake)-1); 88 | } 89 | if (SerialBT.available()) { 90 | reset(); 91 | } 92 | } 93 | else { 94 | //FastLED.clear(); 95 | explodeFruit(); 96 | drawFruit(); 97 | FillSnakeWithColour( startIndex ); 98 | if(currentMillis - previousMillis >= interval) { 99 | previousMillis = currentMillis; 100 | nextStep(); 101 | } 102 | 103 | if (SerialBT.available()) { 104 | char keyPress = (char)SerialBT.read(); 105 | switch(keyPress) { 106 | case 'w': 107 | currentInput = UP; 108 | break; 109 | case 'a': 110 | currentInput = LEFT; 111 | break; 112 | case 's': 113 | currentInput = DOWN; 114 | break; 115 | case 'd': 116 | currentInput = RIGHT; 117 | break; 118 | case 'm': 119 | currentApp = -1; 120 | return false; 121 | } 122 | } 123 | } 124 | 125 | FastLED.show(); 126 | delay(10); 127 | 128 | return true; 129 | } 130 | 131 | private: 132 | void drawFruit(){ 133 | leds(fruitX, fruitY) = CHSV(fruitHue, 255, 255); 134 | } 135 | 136 | void nextStep(){ 137 | for(int i=snakeLength-1; i>0; i--){ 138 | snakeX[i] = snakeX[i-1]; 139 | snakeY[i] = snakeY[i-1]; 140 | } 141 | switch(currentInput){ 142 | case UP: 143 | snakeY[0] = snakeY[0]+1; 144 | break; 145 | case RIGHT: 146 | snakeX[0] = snakeX[0]+1; 147 | break; 148 | case DOWN: 149 | snakeY[0] = snakeY[0]-1; 150 | break; 151 | case LEFT: 152 | snakeX[0] = snakeX[0]-1; 153 | break; 154 | } 155 | 156 | // Check if head has hit fruit 157 | if((snakeX[0] == fruitX) && (snakeY[0] == fruitY)){ 158 | snakeLength++; 159 | interval -= 5; 160 | if(interval < 100) interval = 100; 161 | 162 | //Store current fruit values to make explosion 163 | ex.x = snakeX[0]; 164 | ex.y = snakeY[0]; 165 | ex.r = 1; 166 | ex.hue = fruitHue; 167 | 168 | if(snakeLength < MAX_SNAKE_LENGTH){ 169 | makeFruit(); 170 | } 171 | } 172 | 173 | // Check if head has hit body or left play area 174 | if(isPartOfSnakeBody(snakeX[0], snakeY[0]) || !inPlayField(snakeX[0], snakeY[0])){ 175 | currentInput = NONE; 176 | FastLED.clear(); 177 | paused = true; 178 | sprintf((char *)txtSnake, " SCORE %u ", snakeLength-1); 179 | ScrollingMsg.SetText(txtSnake, strlen((const char *)txtSnake)); 180 | } 181 | } 182 | 183 | void reset() { 184 | snakeLength = 1; 185 | currentInput = UP; 186 | interval = 250; 187 | snakeX[0] = leds.Width() / 2; 188 | snakeY[0] = leds.Height() / 2; 189 | for(int i=1; i=0) && (x=0) && (y= LoopDelayMS) 389 | { 390 | LastLoop = millis(); 391 | FastLED.clear(); 392 | 393 | // Fill background with dim plasma 394 | #define PLASMA_X_FACTOR 24 395 | #define PLASMA_Y_FACTOR 24 396 | for (int16_t x=0; x PlasmaTime) 408 | PlasmaShift = (random8(0, 5) * 32) + 64; 409 | 410 | if (AttractMode) 411 | { 412 | if (currentInput != NONE) 413 | { 414 | AttractMode = false; 415 | memset(PlayfieldData, 0, sizeof(PlayfieldData)); 416 | memset(PlayfieldMask, 0, sizeof(PlayfieldMask)); 417 | Sprites.RemoveSprite(&CurrentBlock); 418 | LastScore = 0; 419 | TotalLines = 0; 420 | DropDelay = INITIAL_DROP_FRAMES; 421 | CurrentBlock.SetXChange(-1); 422 | NextBlock = true; 423 | currentInput = NONE; 424 | } 425 | } 426 | else 427 | { 428 | if (Sprites.IsSprite(&CompletedLines)) // We have highlighted complete lines, delay for visual effect 429 | { 430 | if (CompletedLines.GetXCounter() > 0) 431 | CompletedLines.SetXCounter(CompletedLines.GetXCounter() - 1); 432 | else 433 | { 434 | Sprites.RemoveSprite(&CompletedLines); 435 | // Remove completed lines from playfield sprite 436 | uint8_t *Data = PlayfieldData; 437 | uint8_t *Mask = PlayfieldMask; 438 | uint16_t Mbpl = (MATRIX_WIDTH + 7) / 8; 439 | uint16_t Dbpl = Mbpl * _3BIT; 440 | int16_t k; 441 | for (int16_t i=(MATRIX_HEIGHT-1)*Dbpl,j=(MATRIX_HEIGHT-1)*Mbpl; i>=0; i-=Dbpl,j-=Mbpl) 442 | { 443 | for (k=0; k> _min(MATRIX_WIDTH - k, 8)) != Mask[j + (k / 8)]) 446 | break; 447 | } 448 | if (k >= MATRIX_WIDTH) 449 | { 450 | memmove(&Data[Dbpl], &Data[0], i); 451 | memset(&Data[0], 0, Dbpl); 452 | memmove(&Mask[Mbpl], &Mask[0], j); 453 | memset(&Mask[0], 0, Mbpl); 454 | i+=Dbpl; 455 | j+=Mbpl; 456 | } 457 | } 458 | } 459 | } 460 | else 461 | { 462 | if (CurrentBlock.GetXChange() >= 0) // We have a current block 463 | { 464 | // Check for user input 465 | if ( currentInput == UP ) 466 | { 467 | currentInput = NONE; 468 | if ((CurrentBlock.GetCurrentFrame() % 2) == 1) 469 | { 470 | if (CurrentBlock.GetXChange() == 0) 471 | CurrentBlock.m_X = _min(CurrentBlock.m_X, MATRIX_WIDTH - TETRIS_SPR_WIDTH); 472 | else if ((CurrentBlock.GetXChange() != 3) && (CurrentBlock.GetFlags() & SPRITE_EDGE_X_MAX)) 473 | --CurrentBlock.m_X; 474 | } 475 | CurrentBlock.IncreaseFrame(); 476 | Sprites.DetectCollisions(&CurrentBlock); 477 | if (CurrentBlock.GetFlags() & SPRITE_COLLISION) 478 | CurrentBlock.DecreaseFrame(); 479 | } 480 | 481 | if ( currentInput == LEFT && (! (CurrentBlock.GetFlags() & SPRITE_EDGE_X_MIN)) ) 482 | { 483 | currentInput = NONE; 484 | CurrentBlock.m_X--; 485 | Sprites.DetectCollisions(&CurrentBlock); 486 | if (CurrentBlock.GetFlags() & SPRITE_COLLISION) 487 | CurrentBlock.m_X++; 488 | } 489 | 490 | else if ( currentInput == RIGHT && (! (CurrentBlock.GetFlags() & SPRITE_EDGE_X_MAX)) ) 491 | { 492 | currentInput = NONE; 493 | CurrentBlock.m_X++; 494 | Sprites.DetectCollisions(&CurrentBlock); 495 | if (CurrentBlock.GetFlags() & SPRITE_COLLISION) 496 | CurrentBlock.m_X--; 497 | } 498 | 499 | if ( currentInput == DOWN ) 500 | { 501 | currentInput = NONE; 502 | CurrentBlock.SetYCounter(1); 503 | } 504 | 505 | // Do block checks for bottom or collision 506 | if (CurrentBlock.GetYCounter() <= 1) 507 | { 508 | if (CurrentBlock.GetFlags() & SPRITE_EDGE_Y_MIN) 509 | NextBlock = true; 510 | else 511 | { 512 | --CurrentBlock.m_Y; 513 | Sprites.DetectCollisions(&CurrentBlock); 514 | ++CurrentBlock.m_Y; 515 | if (CurrentBlock.GetFlags() & SPRITE_COLLISION) 516 | { 517 | // Block has collided check for game over 518 | int16_t MaxY = MATRIX_HEIGHT - 2; 519 | if ((CurrentBlock.GetCurrentFrame() % 2) == 1) 520 | { 521 | if (CurrentBlock.GetXChange() == 0) 522 | MaxY -= 2; 523 | else if (CurrentBlock.GetXChange() != 3) 524 | MaxY -= 1; 525 | } 526 | else if (CurrentBlock.GetXChange() == 0) 527 | ++MaxY; 528 | if (CurrentBlock.m_Y < MaxY) 529 | NextBlock = true; 530 | else 531 | { 532 | // Game over 533 | CurrentBlock.SetYCounter(2); // Stop last block moving down! 534 | AttractMode = true; 535 | if (LastScore > HighScore) 536 | { 537 | HighScore = LastScore; 538 | sprintf((char *)GameOverMsg, "%sGAME OVER%sNEW HIGH SCORE %u%s", BlankMsg, BlankMsg, LastScore, BlankMsg); 539 | } 540 | else 541 | sprintf((char *)GameOverMsg, "%sGAME OVER%sSCORE %u%s", BlankMsg, BlankMsg, LastScore, BlankMsg); 542 | sprintf((char *)AttractMsg, "%sTETRIS%sSCORE %u%sHIGH %u%sANY BUTTON TO START%s", BlankMsg, BlankMsg, LastScore, BlankMsg, HighScore, BlankMsg, BlankMsg); 543 | TetrisMsg.SetText(GameOverMsg, strlen((char *)GameOverMsg)); 544 | TetrisMsg.SetBackgroundMode(BACKGND_DIMMING, 0x40); 545 | } 546 | } 547 | } 548 | } 549 | } 550 | if (NextBlock) // Start new block 551 | { 552 | if (CurrentBlock.GetXChange() >= 0) // We have a current block so add to playfield before creating new block 553 | { 554 | Playfield.Combine(CurrentBlock.m_X, CurrentBlock.m_Y, &CurrentBlock); 555 | Sprites.RemoveSprite(&CurrentBlock); 556 | // Make completed lines highlight sprite & score 557 | memset(CompletedLinesData, 0, sizeof(CompletedLinesData)); 558 | CompletedLines.m_Y = -1; 559 | uint8_t *Mask = PlayfieldMask; 560 | uint16_t Mbpl = (MATRIX_WIDTH + 7) / 8; 561 | int16_t j, numlines = 0; 562 | for (int16_t i=(MATRIX_HEIGHT-1)*Mbpl, y=0; i>=0; i-=Mbpl,++y) 563 | { 564 | for (j=0; j> _min(MATRIX_WIDTH - j, 8)) != Mask[i + (j / 8)]) 567 | break; 568 | } 569 | if (j >= MATRIX_WIDTH) 570 | { 571 | if (CompletedLines.m_Y == -1) 572 | CompletedLines.m_Y = y; 573 | memset(&CompletedLinesData[((TETRIS_SPR_HEIGHT - 1) - (y - CompletedLines.m_Y)) * Mbpl], 0xff, Mbpl); 574 | numlines++; 575 | } 576 | } 577 | if (numlines > 0) 578 | { 579 | CompletedLines.SetXCounter(15); // Set delay for highlight display to 15 loops 580 | Sprites.AddSprite(&CompletedLines); 581 | } 582 | LastScore += 1; 583 | if (numlines == 1) 584 | LastScore += 4; 585 | else if (numlines == 2) 586 | LastScore += 12; 587 | else if (numlines == 3) 588 | LastScore += 20; 589 | else if (numlines == 4) 590 | LastScore += 40; 591 | TotalLines += numlines; 592 | DropDelay = _max(1, INITIAL_DROP_FRAMES - (TotalLines / 5)); 593 | } 594 | // Start new block 595 | uint8_t j = random8(sizeof(TetrisSprData) / sizeof(TetrisSprData[0])); 596 | CurrentBlock.Setup(TETRIS_SPR_WIDTH, TETRIS_SPR_WIDTH, TetrisSprData[j], 4, _3BIT, TetrisColours, TetrisSprMask[j]); 597 | CurrentBlock.SetPositionFrameMotionOptions((MATRIX_WIDTH/2)-1, MATRIX_HEIGHT, 0, 0, 0, 0, -1, DropDelay, SPRITE_DETECT_COLLISION | SPRITE_DETECT_EDGE); 598 | CurrentBlock.SetXChange(j); 599 | Sprites.AddSprite(&CurrentBlock); 600 | NextBlock = false; 601 | } 602 | Sprites.UpdateSprites(); 603 | } 604 | } 605 | Sprites.RenderSprites(); 606 | if (AttractMode) 607 | { 608 | if (TetrisMsg.UpdateText() == -1) 609 | { 610 | TetrisMsg.SetText(AttractMsg, strlen((char *)AttractMsg)); 611 | TetrisMsg.SetBackgroundMode(BACKGND_LEAVE); 612 | Sprites.RemoveSprite(&CurrentBlock); 613 | memset(PlayfieldData, 0, sizeof(PlayfieldData)); 614 | memset(PlayfieldMask, 0, sizeof(PlayfieldMask)); 615 | } 616 | } 617 | FastLED.show(); 618 | } 619 | if(SerialBT.available()){ 620 | char keyPress = (char)SerialBT.read(); 621 | switch(keyPress) { 622 | case 'w': 623 | currentInput = UP; 624 | break; 625 | case 'a': 626 | currentInput = LEFT; 627 | break; 628 | case 's': 629 | currentInput = DOWN; 630 | break; 631 | case 'd': 632 | currentInput = RIGHT; 633 | break; 634 | case 'm': 635 | currentApp = -1; 636 | return false; 637 | } 638 | } 639 | return true; 640 | }; 641 | }; 642 | -------------------------------------------------------------------------------- /sprites/Smileys/Smiley1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-marley/ESP32Matrix/9f119ebde22e3ec025dcf5a318590e6b1c0e4e1f/sprites/Smileys/Smiley1.png -------------------------------------------------------------------------------- /sprites/Smileys/Smiley2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-marley/ESP32Matrix/9f119ebde22e3ec025dcf5a318590e6b1c0e4e1f/sprites/Smileys/Smiley2.png -------------------------------------------------------------------------------- /sprites/Smileys/Smiley3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-marley/ESP32Matrix/9f119ebde22e3ec025dcf5a318590e6b1c0e4e1f/sprites/Smileys/Smiley3.png -------------------------------------------------------------------------------- /sprites/Smileys/Smiley4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-marley/ESP32Matrix/9f119ebde22e3ec025dcf5a318590e6b1c0e4e1f/sprites/Smileys/Smiley4.png -------------------------------------------------------------------------------- /sprites/Smileys/Smiley5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-marley/ESP32Matrix/9f119ebde22e3ec025dcf5a318590e6b1c0e4e1f/sprites/Smileys/Smiley5.png -------------------------------------------------------------------------------- /sprites/Smileys/Smiley6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-marley/ESP32Matrix/9f119ebde22e3ec025dcf5a318590e6b1c0e4e1f/sprites/Smileys/Smiley6.png -------------------------------------------------------------------------------- /sprites/Smileys/Smiley7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-marley/ESP32Matrix/9f119ebde22e3ec025dcf5a318590e6b1c0e4e1f/sprites/Smileys/Smiley7.png -------------------------------------------------------------------------------- /sprites/Smileys/Smiley8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-marley/ESP32Matrix/9f119ebde22e3ec025dcf5a318590e6b1c0e4e1f/sprites/Smileys/Smiley8.png -------------------------------------------------------------------------------- /sprites/SpaceInvaders/invader1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-marley/ESP32Matrix/9f119ebde22e3ec025dcf5a318590e6b1c0e4e1f/sprites/SpaceInvaders/invader1.gif -------------------------------------------------------------------------------- /sprites/SpaceInvaders/invader2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-marley/ESP32Matrix/9f119ebde22e3ec025dcf5a318590e6b1c0e4e1f/sprites/SpaceInvaders/invader2.gif -------------------------------------------------------------------------------- /sprites/SpaceInvaders/invader3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-marley/ESP32Matrix/9f119ebde22e3ec025dcf5a318590e6b1c0e4e1f/sprites/SpaceInvaders/invader3.gif -------------------------------------------------------------------------------- /sprites/SpaceInvaders/invader4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-marley/ESP32Matrix/9f119ebde22e3ec025dcf5a318590e6b1c0e4e1f/sprites/SpaceInvaders/invader4.gif -------------------------------------------------------------------------------- /sprites/alien/alien.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-marley/ESP32Matrix/9f119ebde22e3ec025dcf5a318590e6b1c0e4e1f/sprites/alien/alien.gif --------------------------------------------------------------------------------