Antoine Rochebois
Published © CC BY-NC

Interactive LED Table for 50€

Make an interactive table that displays games, an audio spectrum, and animations on a 12x12 built-in LED matrix.

IntermediateShowcase (no instructions)20 hours57,843
Interactive LED Table for 50€

Things used in this project

Hardware components

LACK table - Ikea
×1
WS2812b 5m - 150 LEDs strip
×1
Arduino Nano R3
Arduino Nano R3
You can buy a cheap one in some eastern countries ;)
×1
HC-05 Bluetooth Module
HC-05 Bluetooth Module
×1
45mm illuminated round arcade push button
×4
19mm illuminated blue push button
×1
Illuminated blue rocker switch
×1
Stereo audio female plug
×2
TL072 based audio amplifier
Check the story for shematics
×1
Prototype PCB
×1
Too many cables...
×1
405x405mm white acrylic glass
×1
Black foam board (A2 - 5mm thick)
×1
5V - 50W power supply
×1
L profiled aluminium bar
×1

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Dagoma DiscoEasy200 (If you want a cheap french 3D printer you'd better buy a µDelta Rework from EmotionTech)
Soldering iron (generic)
Soldering iron (generic)
Hot glue gun (generic)
Hot glue gun (generic)
Box cutter
Wood rasp
Saw
Drill

Story

Read more

Custom parts and enclosures

Main interface panel

On this panel you'll find the main control buttons, audio IO and the USB port used to upload the code on the Arduino Nano.

Made on Fusion360

Code

Arduino main code

Arduino
This file is the main code that run on the Arduino, it uses several home-made methods & functions to run games and other features.
I'll upload them once the code is finished and bugless.
/*
 * 
 *        Code by Antoine ROCHEBOIS : CC-BY-NC 
 *
 *
 *        This file is the main code that run on the Arduino, it uses several
 *      home-made methods & functions to run games and other features, I'll   *      upload them once the code is finished and bugless.
 * 
 * 
 * 
 * */
#include "FastLED.h"
#include <avr/pgmspace.h>
#include <SoftwareSerial.h>

#include "imgMario.h"  //Import 2D array of 32bits int provinding the RGB 
#include "imgMenu.h"  //code for each image of the Menu and Images function

#define M_PIN 2       //Pin for the menuInterrupt
#define R1_PIN 8      //
#define L1_PIN 9      // Digital inputs used by arcade buttons (side)
#define R2_PIN 6      //
#define L2_PIN 7      //
#define DATA_PIN 5             //Data PIN for the led matrix

#define COLOR_ORDER GRB      // if colors are mismatched; change this
#define NUM_LEDS    144       // 12*12=144 leds in the strip
#define LED_TYPE    WS2812B
// this creates an LED array to hold the values for each led in your strip
CRGB leds[NUM_LEDS];

const PROGMEM byte ledsAdress[12][12] = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
  {23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12},
  {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35},
  {47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36},
  {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59},
  {71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60},
  {72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83},
  {95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84},
  {96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107},
  {119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108},
  {120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131},
  {143, 142, 141, 140, 139, 138, 137, 136, 135, 134, 133, 132}
};  //Adress of each led in the matrix (Progmem means stored in FLASH instead //of SRAM)

SoftwareSerial BT(10, 11); //Emulates an Serial BT terminal

boolean grid[12][12]; //(used in a game, ignore)
char message;   //char read on the BT terminal
char curControl; //current control ID
int menuPointer = 0; //Position in the menu
volatile bool menuValidation = 0; //Variable set to true when the menu switch
//is pushed

unsigned long curTime; 
unsigned long prevUpdateTime = 0; //Variables for timed events
unsigned long prevButtonTime = 0;

//Function executed when the user push the menuButton (set menuValidation to
//true + debounce input)
void menuInterrupt() { 
  static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  if (interrupt_time - last_interrupt_time > 200) {
    menuValidation = true;
  }
  last_interrupt_time = interrupt_time;
}

//Displays static images stocked in FLASH, if roll == 1, draws it whith a fancy red line
void setImg(const unsigned long frame[12][12], bool roll) {
  if (!roll) {
    for (byte i = 0; i < 12; i++) {
      for (byte j = 0; j < 12; j++) {
        leds[pgm_read_byte(&ledsAdress[i][j])] = pgm_read_dword(&frame[i][j]);
      }
    }
    FastLED.show();

  } else {
    for (uint8_t i = 11; i > 0; i--) {
      for (byte j = 0; j < 12; j++) {
        if (i > 0) {
          leds[pgm_read_byte(&ledsAdress[i][j])] = 0xe22c79;
        } else {
          leds[pgm_read_byte(&ledsAdress[i][j])] = pgm_read_dword(&frame[i][j]);
        }

        leds[pgm_read_byte(&ledsAdress[(i + 13) % 12][j])] = pgm_read_dword(&frame[(i + 13) % 12][j]);
      }
      FastLED.show();
      delay(70);
    }
  }


}

//Erase the screen with a red sweeping line
void resetImg() {
  for (byte i = 0; i < 12; i++) {
    for (byte j = 0; j < 12; j++) {
      leds[pgm_read_byte(&ledsAdress[i][j])] = 0xe22c79;
      if (i > 0) {
        leds[pgm_read_byte(&ledsAdress[(i + 11) % 12][j])] = 0x000000;
      }

    }
    FastLED.show();
    delay(70);
  }
}
//Erase the screen by fading it
void fadeImg() {
  for (uint8_t i = 128; i > 0; i = i - 4) {
    FastLED.setBrightness(i);
    FastLED.show();
    delay(24);
  }
  delay(250);
  FastLED.setBrightness(128);
}
//Instant erase of the screen
void clearImg() {
  for (byte dim1 = 0; dim1 < 12; dim1++) {
    for (byte dim2 = 0; dim2 < 12; dim2++) {
      leds[pgm_read_byte(&ledsAdress[dim1][dim2])] = 0x000000;
    }
  }
}


//Read inputs (BT + digital IO)
void readPointer() {

  //Read BT (hc-05 module)
  while (BT.available() > 0) {
    message = BT.read();
  }
  //Changes control variables
  if (message == 'q' ) {
    --menuPointer;
    curControl = 'q';
  } else if (message == 'd') {
    ++menuPointer;
    curControl = 'd';
  } else if (message == 'z') {
    curControl = 'z';
  } else if (message == 's') {
    curControl = 's';
  }  else if (message == 'a') {
    curControl = 'a';
  } else if (message == 'y') {
    menuValidation = true;
  }
  message = 'n'; //Reset message
  
  //Read digital IO pins
  if (!digitalRead(L1_PIN)) {
    if ( (millis() - prevButtonTime) > 150) {
      curControl = 'q';
      --menuPointer;
      prevButtonTime = millis();
    }
  } else if (!digitalRead(R1_PIN)) {
    if ( (millis() - prevButtonTime) > 150) {
      curControl = 'd';
      ++menuPointer;
      prevButtonTime = millis();
    }
  } else if (!digitalRead(L2_PIN)) {
    if ( (millis() - prevButtonTime) > 150) {
      curControl = 'z';
      --menuPointer;
      prevButtonTime = millis();
    }
  } else if (!digitalRead(l2_PIN)) {
    if ( (millis() - prevButtonTime) > 150) {
      curControl = 's';
      ++menuPointer;
      prevButtonTime = millis();
    }
  }

}

#include "gameOfLife.h" //Include features and games methods and functions
#include "snake.h"
#include "pong.h"
#include "tetris.h"
#include "bottle.h"
#include "decoration.h"
#include "font.h"







/*
*
*         RUN AT STARTUP
*
*/

void setup() {
  //Setup the BT connection
  BT.begin(9600);
  
  //Setup the IO pin (Inputs with a built-in pull-up resistor or interupt)
  pinMode(R1_PIN, INPUT_PULLUP);
  pinMode(L1_PIN, INPUT_PULLUP);
  pinMode(R2_PIN, INPUT_PULLUP);
  pinMode(L2_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(M_PIN), menuInterrupt, FALLING);
  
  //Setup the Led strip
  FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  
  //Intro animation (display a beer and load the menu)
  FastLED.setBrightness(0);
  setImg(beer, 0);
  delay(250);
  for (uint8_t i = 0; i <= 128; i = i + 4) {
    FastLED.setBrightness(i);
    FastLED.show();
    delay(42);
  }
  delay(2400);
  fadeImg();
  clearImg();
  FastLED.show();
  delay(250);
  setImg(menuTetris, 1);

}








/*
*
*         RUN PERPETUALY
*
*/
void loop() {

//Menu Interface and games/features launcher
  switch ((menuPointer + 12000) % 12) {
    case 0:
      setImg(menuTetris, 0);
      while ((menuPointer + 12000) % 12 == 0 && !menuValidation) {
        readPointer();
      }
      if (menuValidation) {
        menuValidation = false;
        while (!menuValidation) {
          launchTetris(); //Tetris game
        }
        menuValidation = false;
        menuPointer = 0;
        resetImg();
        setImg(menuTetris, 1);
      }
      break;
    case 1:
      setImg(menuSimon, 0);
      while ((menuPointer + 12000) % 12 == 1 && !menuValidation) {
        readPointer();
      }
      if (menuValidation) {
        menuValidation = false; //Displays a cross : feature not implemented yet
        setImg(menuError, 0);
        delay(1250);
        menuPointer = 1;
        resetImg();
        setImg(menuSimon, 1);
      }
      break;
    case 2:
      setImg(menuPong, 0);
      while ((menuPointer + 12000) % 12 == 2 && !menuValidation) {
        readPointer();
      }
      if (menuValidation) {
        menuValidation = false;
        while (!menuValidation) {
          launchPong(); //Pong game for 2 players
        }
        menuValidation = false;
        menuPointer = 2;
        resetImg();
        setImg(menuPong, 1);
      }
      break;
    case 3:
      setImg(menuSnake, 0);
      while ((menuPointer + 12000) % 12 == 3 && !menuValidation) {
        readPointer();
      }
      if (menuValidation) {
        menuValidation = false;
        while (!menuValidation) {
          launchSnake(); //Snake game
        }
        menuValidation = false;
        menuPointer = 3;
        resetImg();
        setImg(menuSnake, 1);
      }
      break;
    case 4:
      setImg(menuBottle, 0);
      while ((menuPointer + 12000) % 12 == 4 && !menuValidation) {
        readPointer();
      }
      if (menuValidation) {
        menuValidation = false;
        while (!menuValidation) {
          bottle(); //A wheel that stops on a random location on the screen
        }
        menuValidation = false;
        menuPointer = 4;
        resetImg();
        setImg(menuBottle, 1);
      }
      break;
    case 5:
      setImg(menuGameOfLife, 0);
      while ((menuPointer + 12000) % 12 == 5 && !menuValidation) {
        readPointer();
      }
      if (menuValidation) {
        menuValidation = false;
        while (!menuValidation) {
          gameOfLife(); //Run conway's game of life
        }
        menuValidation = false;
        menuPointer = 5;
        resetImg();
        setImg(menuGameOfLife, 1);
      }
      break;
    case 6:
      setImg(menuParty, 0);
      while ((menuPointer + 12000) % 12 == 6 && !menuValidation) {
        readPointer();
      }
      if (menuValidation) {
        menuValidation = false;
        while (!menuValidation) {
          launchManualAnimations(); //Visual animation (choice)
        }
        menuValidation = false;
        menuPointer = 6;
        resetImg();
        setImg(menuParty, 1);
      }
      break;
    case 7:
      setImg(menuParty, 0);
      while ((menuPointer + 12000) % 12 == 7 && !menuValidation) {
        readPointer();
      }
      if (menuValidation) {
        menuValidation = false;
        while (!menuValidation) {
          launchRandomAnimations(); //Launch random visual animation
        }
        menuValidation = false;
        menuPointer = 7;
        resetImg();
        setImg(menuParty, 1);
      }
      break;
    case 8:
      setImg(menuImage, 0);
      while ((menuPointer + 12000) % 12 == 8 && !menuValidation) {
        readPointer();
      }
      if (menuValidation) {
        menuValidation = false;
        while (!menuValidation) { 
          launchManualImage(); //Images manual selection
        }
        menuValidation = false;
        menuPointer = 8;
        resetImg();
        setImg(menuImage, 1);
      }
      break;
    case 9:
      setImg(menuImage, 0);
      while ((menuPointer + 12000) % 12 == 9 && !menuValidation) {
        readPointer();
      }
      if (menuValidation) {
        menuValidation = false;
        while (!menuValidation) {
          launchRandomImage(); //Images random selection
        }
        menuValidation = false;
        menuPointer = 9;
        resetImg();
        setImg(menuImage, 1);
      }
      break;
    case 10:
      setImg(menuText, 0);
      while ((menuPointer + 12000) % 12 == 10 && !menuValidation) {
        readPointer();
      }
      if (menuValidation) {
        menuValidation = false;
        while (!menuValidation) {
          scrollText("Hi!",3, 0xff0000); //Scrolls a defined text on the screen TODO: BT defined text & color
        }
        menuValidation = false;
        menuPointer = 10;
        resetImg();
        setImg(menuText, 1);
      }
      break;
    case 11:
      setImg(menuSpectrum, 0);
      while ((menuPointer + 12000) % 12 == 11 && !menuValidation) {
        readPointer();
      }
      if (menuValidation) { //Spectrum Analyzer code is too heavy for the FLASH/SRAM, needs a specific upload to run. Displays an error message
        menuValidation = false;
        setImg(menuError, 0);
        delay(1250);
        scrollText("Connect USB",11, 0xff0000);
        menuPointer = 11;
        resetImg();
        setImg(menuSpectrum, 1);
      }
      break;
  }

}

Game of Life

Arduino
The algorithm for the Conway's Game of life function
void gameOfLife(void) {
  //Génération d'une grille de départ aléatoire
  for (byte i = 0; i < 12; i++) {
    for (byte j = 0; j < 12; j++) {
      byte r = random(0, 100);
      if (r < 35) {
        grid[i][j] = true;
        leds[pgm_read_byte(&ledsAdress[i][j])] = 0x2CB697;
      } else {
        grid[i][j] = false;
        leds[pgm_read_byte(&ledsAdress[i][j])] = 0x11463A;
      }
    }
  }
  FastLED.show();

  //Tant que la grille n'est pas vide
  byte nbCases;
  byte iterations = 0;

  do {
    readPointer();
    if (menuValidation) {
      break;
    }
    ++iterations;
    if (iterations > 250) {
      break;
    }
    //Délai de 150ms
    delay(120);

    //Initialisation et comptage du nombre de voisins de chaque case
    nbCases = 0;
    byte voisins[12][12];
    for (byte i = 0; i < 12; i++) {
      for (byte j = 0; j < 12; j++) {
        voisins[i][j] = 0;
      }
    }

    for (byte i = 0; i < 12; i++) {
      for (byte j = 0; j < 12; j++) {
        for (int k = -1; k < 2; k++) {
          for (int m = -1; m < 2; m++) {
            if ((grid[(i + k + 12) % 12][(j + m + 12) % 12] == true) && !(k == 0 && m == 0)) {
              voisins[i][j]++;
            }
          }
        }
      }
    }

    //Détermination des cellules vivantes et mortes et mise à jour de l'affichage
    for (byte i = 0; i < 12; i++) {
      for (byte j = 0; j < 12; j++) {
        if (grid[i][j]) {
          nbCases++;
          //Cellule vivante reste vivante avec 2 ou 3 voisins
          if (voisins[i][j] == 2 || voisins[i][j] == 3) {
            grid[i][j] = true;
            leds[pgm_read_byte(&ledsAdress[i][j])] = 0x2CB697;
          } else {
            grid[i][j] = false;
            leds[pgm_read_byte(&ledsAdress[i][j])] = 0x11463A;
          }

        } else {
          if (voisins[i][j] == 3) {
            //Cellule morte devient vivante si elle a 3 voisins
            grid[i][j] = true;
            leds[pgm_read_byte(&ledsAdress[i][j])] = 0x2CB697;
          }
        }
      }
    }
    FastLED.show();
    if (nbCases <= 4) {
      for (byte i = 0; i < 12; i++) {
        for (byte j = 0; j < 12; j++) {
          if (i < 12 - 1) {
            leds[pgm_read_byte(&ledsAdress[i][j])] = 0xe22c79;
          } else {
            leds[pgm_read_byte(&ledsAdress[i][j])] = 0x11463A;
          }

              leds[pgm_read_byte(&ledsAdress[(i + 11) % 12][j])] = 0x11463A;
        }
        FastLED.show();
        delay(100);
      }
    }
  } while (nbCases > 4);
}

Random roulette mode (idk how to call it ^^' )

Arduino
const unsigned long PROGMEM img[12][12] = {{0x020EF4, 0x022FF4, 0x0159F3, 0x018AF5, 0x01BFF9, 0x01F5FB, 0x01FFD6, 0x01FDA5, 0x01FC7A, 0x01FB54, 0x01FA38, 0x02F91F},
  {0x0100F3, 0x010EF4, 0x0237F3, 0x0170F4, 0x01B1F9, 0x01F2FA, 0x01FFCB, 0x01FC8E, 0x01FB5D, 0x01FA37, 0x01F918, 0x00F905},
  {0x0001F2, 0x0000F2, 0x010FF3, 0x0247F4, 0x019BF7, 0x01EFF8, 0x01FFBA, 0x01FB6E, 0x01FA35, 0x00F812, 0x00F802, 0x01F900},
  {0x0201F2, 0x0001F2, 0x0000F3, 0x0010F3, 0x016CF6, 0x01E9F5, 0x00FF97, 0x00FA35, 0x00F908, 0x02F900, 0x0EFA00, 0x1FFB01},
  {0x2C00F3, 0x1D01F3, 0x0F01F2, 0x0100F2, 0x001BF9, 0x00CEE4, 0x00FF42, 0x08F901, 0x1DFB00, 0x33FC01, 0x41FE00, 0x4BFE00},
  {0x6701F3, 0x6501F3, 0x6501F4, 0x6402F5, 0x6700FE, 0x756F82, 0x7CFF00, 0x7BFF02, 0x7AFF02, 0x7BFF02, 0x7AFE02, 0x79FF01},
  {0x9F00F3, 0xAC01F3, 0xC301F7, 0xE001F1, 0xF900AE, 0xFF0C15, 0xFFAF00, 0xF3FD04, 0xD8FF03, 0xC0FF03, 0xB2FF04, 0xA9FE04},
  {0xD301F5, 0xE600F2, 0xF401DF, 0xF600AF, 0xF4004F, 0xF30001, 0xF95000, 0xFFC002, 0xFFF303, 0xF7FF03, 0xE6FF02, 0xD3FF03},
  {0xF301E9, 0xF501D0, 0xF301A9, 0xF30074, 0xF20026, 0xF10000, 0xF52A01, 0xFA8202, 0xFFC003, 0xFFEA04, 0xFFFD03, 0xF7FF03},
  {0xF400C5, 0xF301A8, 0xF20182, 0xF2014F, 0xF20113, 0xF20000, 0xF41801, 0xF75C02, 0xFD9502, 0xFEBF02, 0xFFE103, 0xFFF803},
  {0xF301A8, 0xF30089, 0xF20065, 0xF10137, 0xF20009, 0xF20000, 0xF30D01, 0xF64301, 0xF97601, 0xFDA001, 0xFEC002, 0xFFDB03},
  {0xF30090, 0xF20172, 0xF2014E, 0xF30126, 0xF20003, 0xF20000, 0xF30801, 0xF43201, 0xF75F02, 0xFC8602, 0xFEA601, 0xFEC102}
};

void bottleAnimation(byte t, byte stopb) {
  byte iterations = 0;

  while (!menuValidation) {
    for (byte k = 0; k < 4; k++) {
      switch (k) {
        case 0:
          for (byte i = 2; i < 12; i++) {
            iterations++;
            if (iterations == stopb) {
              goto mainLoopBottle;
            }
            clearImg();
            leds[pgm_read_byte(&ledsAdress[2][i - 2])] = pgm_read_dword(&img[1][i - 2]);
            leds[pgm_read_byte(&ledsAdress[2][i - 1])] = pgm_read_dword(&img[2][i - 1]);
            leds[pgm_read_byte(&ledsAdress[2][i])] = pgm_read_dword(&img[2][i]);
            leds[pgm_read_byte(&ledsAdress[1][i - 2])] = pgm_read_dword(&img[1][i - 2]);
            leds[pgm_read_byte(&ledsAdress[1][i - 1])] = pgm_read_dword(&img[1][i - 1]);
            leds[pgm_read_byte(&ledsAdress[1][i])] = pgm_read_dword(&img[1][i]);
            leds[pgm_read_byte(&ledsAdress[0][i - 2])] = pgm_read_dword(&img[1][i - 2]);
            leds[pgm_read_byte(&ledsAdress[0][i])] = pgm_read_dword(&img[0][i]);
            leds[pgm_read_byte(&ledsAdress[0][i - 1])] = pgm_read_dword(&img[1][i - 1]);
            FastLED.show();
            delay(t);
          }
          break;
        case 1:
          for (byte i = 2; i < 12; i++) {
            iterations++;
            if (iterations == stopb) {
              goto mainLoopBottle;
            }
            clearImg();
            leds[pgm_read_byte(&ledsAdress[i - 2][11])] = pgm_read_dword(&img[i - 2][11]);
            leds[pgm_read_byte(&ledsAdress[i - 1][11])] = pgm_read_dword(&img[i - 1][11]);
            leds[pgm_read_byte(&ledsAdress[i][11])] = pgm_read_dword(&img[i][11]);
            leds[pgm_read_byte(&ledsAdress[i - 2][10])] = pgm_read_dword(&img[i - 2][10]);
            leds[pgm_read_byte(&ledsAdress[i - 1][10])] = pgm_read_dword(&img[i - 1][10]);
            leds[pgm_read_byte(&ledsAdress[i][10])] = pgm_read_dword(&img[i][10]);
            leds[pgm_read_byte(&ledsAdress[i - 2][9])] = pgm_read_dword(&img[i - 2][9]);
            leds[pgm_read_byte(&ledsAdress[i - 1][9])] = pgm_read_dword(&img[i - 1][9]);
            leds[pgm_read_byte(&ledsAdress[i][9])] = pgm_read_dword(&img[i][9]);
            FastLED.show();
            delay(t);
          }
          break;
        case 2:
          for (byte i = 12 - 1; i > 1; i--) {
            iterations++;
            if (iterations == stopb) {
              goto mainLoopBottle;
            }
            clearImg();
            leds[pgm_read_byte(&ledsAdress[11][i - 2])] = pgm_read_dword(&img[11][i - 2]);
            leds[pgm_read_byte(&ledsAdress[11][i - 1])] = pgm_read_dword(&img[11][i - 1]);
            leds[pgm_read_byte(&ledsAdress[11][i])] = pgm_read_dword(&img[11][i]);
            leds[pgm_read_byte(&ledsAdress[10][i - 2])] = pgm_read_dword(&img[10][i - 2]);
            leds[pgm_read_byte(&ledsAdress[10][i - 1])] = pgm_read_dword(&img[10][i - 1]);
            leds[pgm_read_byte(&ledsAdress[10][i])] = pgm_read_dword(&img[10][i]);
            leds[pgm_read_byte(&ledsAdress[9][i - 2])] = pgm_read_dword(&img[9][i - 2]);
            leds[pgm_read_byte(&ledsAdress[9][i])] = pgm_read_dword(&img[9][i]);
            leds[pgm_read_byte(&ledsAdress[9][i - 1])] = pgm_read_dword(&img[9][i - 1]);
            FastLED.show();
            delay(t);
          }
          break;
        case 3:
          for (byte i = 12 - 1; i > 1; i--) {
            iterations++;
            if (iterations == stopb) {
              goto mainLoopBottle;
            }
            clearImg();
            leds[pgm_read_byte(&ledsAdress[i - 2][2])] = pgm_read_dword(&img[i - 2][2]);
            leds[pgm_read_byte(&ledsAdress[i - 1][2])] = pgm_read_dword(&img[i - 1][2]);
            leds[pgm_read_byte(&ledsAdress[i][2])] = pgm_read_dword(&img[i][2]);
            leds[pgm_read_byte(&ledsAdress[i - 2][1])] = pgm_read_dword(&img[i - 2][1]);
            leds[pgm_read_byte(&ledsAdress[i - 1][1])] = pgm_read_dword(&img[i - 1][1]);
            leds[pgm_read_byte(&ledsAdress[i][1])] = pgm_read_dword(&img[i][1]);
            leds[pgm_read_byte(&ledsAdress[i - 2][0])] = pgm_read_dword(&img[i - 2][0]);
            leds[pgm_read_byte(&ledsAdress[i - 1][0])] = pgm_read_dword(&img[i - 1][0]);
            leds[pgm_read_byte(&ledsAdress[i][0])] = pgm_read_dword(&img[i][0]);
            FastLED.show();
            delay(t);
          }
          break;

      }
    }
} mainLoopBottle:
delay(4);
}

void bottle() {
  bottleAnimation(60, random(100, 139));

  int prevPointer = menuPointer;
  while (!menuValidation && menuPointer == prevPointer) {
    readPointer();
    delay(4);
  }
}

decoration.h

C/C++
/*void fillingScreen() {
  int iterations = 0;
  while (!menuValidation && iterations < 400) {
    byte c[3];
    for (byte i = 0; i < 3; i++) {
      c[i] = random(0, 256);
    }
    for (byte i = 0; i < 12; i++) {
      for (byte j = 0; j < 12; j++) {
        leds[pgm_read_byte(&ledsAdress[i][j])] = (( (c[0])) << 16) + ( (c[1]) << 8) + ( (c[2]));
        FastLED.show();
        delay(25);
        iterations++;
      }
    }
  }
}

void fadingScreen() {
  int iterations = 0;
  while (!menuValidation && iterations < 400) {
    byte c[3];
    for (byte i = 0; i < 3; i++) {
      c[i] = random(0, 256);
    }
    for (byte k = 0; k < 128; k = k + 2) {
      for (byte i = 0; i < 12; i++) {
        for (byte j = 0; j < 12; j++) {
          leds[pgm_read_byte(&ledsAdress[i][j])] = (( (c[0])) << 16) + ( (c[1]) << 8) + ( (c[2]));
        }

      }
      iterations++;
      FastLED.setBrightness(k);
      FastLED.show();
      delay(50);
    }
    for (uint8_t k = 128; k > 0; k = k - 2) {
      for (byte i = 0; i < 12; i++) {
        for (byte j = 0; j < 12; j++) {
          leds[pgm_read_byte(&ledsAdress[i][j])] = (( (c[0])) << 16) + ( (c[1]) << 8) + ( (c[2]));
        }

      }
      iterations++;
      FastLED.setBrightness(k);
      FastLED.show();
      delay(50);
    }

  }
  clearImg();
  FastLED.setBrightness(128);
}*/
void crossingSnakes() {
  for (byte i = 0; i < 144; i++) {
    for (byte j = 0; j < 5; j++) {
      leds[(i + j + 0) % 144] = 0xff0000;
      leds[(i + j + 12) % 144] = 0xffff00;
      leds[(i + j + 23) % 144] = 0x00ff00;
      leds[(i + j + 36) % 144] = 0x00ffff;
      leds[(i + j + 48) % 144] = 0x0000ff;
      leds[(i + j + 59) % 144] = 0xff00ff;
      leds[(i + j + 72) % 144] = 0xff0000;
      leds[(i + j + 84) % 144] = 0xffff00;
      leds[(i + j + 95) % 144] = 0x00ff00;
      leds[(i + j + 107) % 144] = 0x00ffff;
      leds[(i + j + 120) % 144] = 0x0000ff;
      leds[(i + j + 134) % 144] = 0xff00ff;
    }
    FastLED.show();
    delay(85);
    clearImg();
    if (menuValidation) break;
  }
}


/*public void rainingStars(Panel p) {
  int iterations = 0;
  while (!TableLED.menuValidation && iterations < 400) {

    int img[][] = new int[12][12];
    int color[][] = new int[12][12];
    boolean stars[][] = new boolean[12][12];
    for (int i = 0; i < stars.length; i++) {
      for (int j = 0; j < stars[i].length; j++) {
        if (Math.random() < 0.035) {
          stars[i][j] = true;
          color[i][j] = (int) (Math.random() * 4);
        }
      }
    }
    for (int k = 0; k < 256; k = k + 4) {
      try {
        Thread.sleep(50);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      iterations++;
      for (int i = 0; i < stars.length; i++) {
        for (int j = 0; j < stars[i].length; j++) {
          if (stars[i][j]) {

            switch (color[i][j]) {
              case 0:
                img[i][j] = (((int) (0.2 * k)) << 16) + ((int) (0.32 * k) << 8) + ((int) (0.7 * k));
                break;
              case 1:
                img[i][j] = (((int) (0.28 * k)) << 16) + ((int) (0.463 * k) << 8) + k;
                break;
              case 2:
                img[i][j] = (k << 16) + ((int) (0.757 * k) << 8) + ((int) (0.146 * k));
                break;
              default:
                img[i][j] = (((int) (0.27 * k)) << 16) + ((int) (0.118 * k) << 8) + ((int) (0.44 * k));
                break;
            }

          }
        }
      }
      p.setFrame(img);
    }
  }
  }*/



/*void decoration() {

  byte r = random(0, 6);
  readPointer();
  switch (r) {
    case 0:
      crossingSnakes();
      break;
      case 1:
      glitchRainingStars();
      break;
      case 2:
      rainingStars();
      break;
    case 3:
      fadingScreen();
      break;
    case 4:
      bottleAnimation(60, 334);
    default:
      fillingScreen();
      break;
  }

}*/

font.h

C/C++
/* uTFT Font library
 * http://www.henningkarlsen.com/electronics/r_fonts.php
 * 
 */


const unsigned char PROGMEM font[760] =
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // <Space>
0x18,0x3C,0x3C,0x18,0x18,0x00,0x18,0x00, // !
0x66,0x66,0x24,0x00,0x00,0x00,0x00,0x00, // "
0x6C,0x6C,0xFE,0x6C,0xFE,0x6C,0x6C,0x00, // #
0x18,0x3E,0x60,0x3C,0x06,0x7C,0x18,0x00, // $
0x00,0xC6,0xCC,0x18,0x30,0x66,0xC6,0x00, // %
0x38,0x6C,0x38,0x76,0xDC,0xCC,0x76,0x00, // &
0x18,0x18,0x30,0x00,0x00,0x00,0x00,0x00, // '
0x0C,0x18,0x30,0x30,0x30,0x18,0x0C,0x00, // (
0x30,0x18,0x0C,0x0C,0x0C,0x18,0x30,0x00, // )
0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00, // *
0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00, // +
0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30, // ,
0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00, // -
0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00, // .
0x06,0x0C,0x18,0x30,0x60,0xC0,0x80,0x00, // /
0x7C,0xC6,0xCE,0xD6,0xE6,0xC6,0x7C,0x00, // 0
0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00, // 1
0x7C,0xC6,0x06,0x1C,0x30,0x66,0xFE,0x00, // 2
0x7C,0xC6,0x06,0x3C,0x06,0xC6,0x7C,0x00, // 3
0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x1E,0x00, // 4
0xFE,0xC0,0xC0,0xFC,0x06,0xC6,0x7C,0x00, // 5
0x38,0x60,0xC0,0xFC,0xC6,0xC6,0x7C,0x00, // 6
0xFE,0xC6,0x0C,0x18,0x30,0x30,0x30,0x00, // 7
0x7C,0xC6,0xC6,0x7C,0xC6,0xC6,0x7C,0x00, // 8
0x7C,0xC6,0xC6,0x7E,0x06,0x0C,0x78,0x00, // 9
0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x00, // :
0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x30, // ;
0x06,0x0C,0x18,0x30,0x18,0x0C,0x06,0x00, // <
0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00, // =
0x60,0x30,0x18,0x0C,0x18,0x30,0x60,0x00, // >
0x7C,0xC6,0x0C,0x18,0x18,0x00,0x18,0x00, // ?
0x7C,0xC6,0xDE,0xDE,0xDE,0xC0,0x78,0x00, // @
0x38,0x6C,0xC6,0xFE,0xC6,0xC6,0xC6,0x00, // A
0xFC,0x66,0x66,0x7C,0x66,0x66,0xFC,0x00, // B
0x3C,0x66,0xC0,0xC0,0xC0,0x66,0x3C,0x00, // C
0xF8,0x6C,0x66,0x66,0x66,0x6C,0xF8,0x00, // D
0xFE,0x62,0x68,0x78,0x68,0x62,0xFE,0x00, // E
0xFE,0x62,0x68,0x78,0x68,0x60,0xF0,0x00, // F
0x3C,0x66,0xC0,0xC0,0xCE,0x66,0x3A,0x00, // G
0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00, // H
0x3C,0x18,0x18,0x18,0x18,0x18,0x3C,0x00, // I
0x1E,0x0C,0x0C,0x0C,0xCC,0xCC,0x78,0x00, // J
0xE6,0x66,0x6C,0x78,0x6C,0x66,0xE6,0x00, // K
0xF0,0x60,0x60,0x60,0x62,0x66,0xFE,0x00, // L
0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0x00, // M
0xC6,0xE6,0xF6,0xDE,0xCE,0xC6,0xC6,0x00, // N
0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00, // O
0xFC,0x66,0x66,0x7C,0x60,0x60,0xF0,0x00, // P
0x7C,0xC6,0xC6,0xC6,0xC6,0xCE,0x7C,0x0E, // Q
0xFC,0x66,0x66,0x7C,0x6C,0x66,0xE6,0x00, // R
0x7C,0xC6,0x60,0x38,0x0C,0xC6,0x7C,0x00, // S
0x7E,0x7E,0x5A,0x18,0x18,0x18,0x3C,0x00, // T
0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00, // U
0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x00, // V
0xC6,0xC6,0xC6,0xD6,0xD6,0xFE,0x6C,0x00, // W
0xC6,0xC6,0x6C,0x38,0x6C,0xC6,0xC6,0x00, // X
0x66,0x66,0x66,0x3C,0x18,0x18,0x3C,0x00, // Y
0xFE,0xC6,0x8C,0x18,0x32,0x66,0xFE,0x00, // Z
0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00, // [
0xC0,0x60,0x30,0x18,0x0C,0x06,0x02,0x00, // <Backslash>
0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00, // ]
0x10,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00, // ^
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF, // _
0x30,0x18,0x0C,0x00,0x00,0x00,0x00,0x00, // '
0x00,0x00,0x78,0x0C,0x7C,0xCC,0x76,0x00, // a
0xE0,0x60,0x7C,0x66,0x66,0x66,0xDC,0x00, // b
0x00,0x00,0x7C,0xC6,0xC0,0xC6,0x7C,0x00, // c
0x1C,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00, // d
0x00,0x00,0x7C,0xC6,0xFE,0xC0,0x7C,0x00, // e
0x3C,0x66,0x60,0xF8,0x60,0x60,0xF0,0x00, // f
0x00,0x00,0x76,0xCC,0xCC,0x7C,0x0C,0xF8, // g
0xE0,0x60,0x6C,0x76,0x66,0x66,0xE6,0x00, // h
0x18,0x00,0x38,0x18,0x18,0x18,0x3C,0x00, // i
0x06,0x00,0x06,0x06,0x06,0x66,0x66,0x3C, // j
0xE0,0x60,0x66,0x6C,0x78,0x6C,0xE6,0x00, // k
0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00, // l
0x00,0x00,0xEC,0xFE,0xD6,0xD6,0xD6,0x00, // m
0x00,0x00,0xDC,0x66,0x66,0x66,0x66,0x00, // n
0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7C,0x00, // o
0x00,0x00,0xDC,0x66,0x66,0x7C,0x60,0xF0, // p
0x00,0x00,0x76,0xCC,0xCC,0x7C,0x0C,0x1E, // q
0x00,0x00,0xDC,0x76,0x60,0x60,0xF0,0x00, // r
0x00,0x00,0x7E,0xC0,0x7C,0x06,0xFC,0x00, // s
0x30,0x30,0xFC,0x30,0x30,0x36,0x1C,0x00, // t
0x00,0x00,0xCC,0xCC,0xCC,0xCC,0x76,0x00, // u
0x00,0x00,0xC6,0xC6,0xC6,0x6C,0x38,0x00, // v
0x00,0x00,0xC6,0xD6,0xD6,0xFE,0x6C,0x00, // w
0x00,0x00,0xC6,0x6C,0x38,0x6C,0xC6,0x00, // x
0x00,0x00,0xC6,0xC6,0xC6,0x7E,0x06,0xFC, // y
0x00,0x00,0x7E,0x4C,0x18,0x32,0x7E,0x00, // z
0x0E,0x18,0x18,0x70,0x18,0x18,0x0E,0x00, // {
0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00, // |
0x70,0x18,0x18,0x0E,0x18,0x18,0x70,0x00, // }
0x76,0xDC,0x00,0x00,0x00,0x00,0x00,0x00, // ~
};


uint8_t charBuffer[8][8];

//Load char in buffer and return width in pixels
uint8_t loadCharInBuffer(char letter) {
  uint8_t* tmpCharPix;
  uint8_t tmpCharWidth;

  int letterIdx = (letter - 32) * 8;

  int x = 0; int y = 0;
  for (int idx = letterIdx; idx < letterIdx + 8; idx++) {
    for (int x = 0; x < 8; x++) {
      charBuffer[x][y] = ((pgm_read_byte(&font[idx])) & (1 << (7 - x))) > 0;
    }
    y++;
  }

  tmpCharWidth = 8;
  return tmpCharWidth;
}

void printText(char* text, unsigned int textLength, int xoffset, int yoffset, unsigned long color) {
  uint8_t curLetterWidth = 0;
  int curX = xoffset;
  clearImg();

  //Loop over all the letters in the string
  for (int i = 0; i < textLength; i++) {
    //Determine width of current letter and load its pixels in a buffer
    curLetterWidth = loadCharInBuffer(text[i]);
    //Loop until width of letter is reached
    for (int lx = 0; lx < curLetterWidth; lx++) {
      //Now copy column per column to field (as long as within the field
      if (curX >= 12) { //If we are to far to the right, stop loop entirely
        break;
      } else if (curX >= 0) { //Draw pixels as soon as we are "inside" the drawing area
        for (int ly = 0; ly < 8; ly++) { //Finally copy column
          leds[pgm_read_byte(&ledsAdress[yoffset + ly][curX])] = charBuffer[lx][ly] * color;

        }
      }
      curX++;
    }
  }
  FastLED.show();
}
//Scroll current selection text from right to left;
void scrollText(char* curSelectionText, int curSelectionTextLength, unsigned long color ) {
  for (int x = 12; x > -(curSelectionTextLength * 8); x--) {
    printText(curSelectionText, curSelectionTextLength, x, 2, color);
    delay(80);
  }
}

imgMario.h

Arduino
const unsigned long mushroom[12][12] PROGMEM = {{0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0x000000, 0x000000, 0x000000},
  {0x000000, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0x000000},
  {0x000000, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0x000000},
  {0xDF0202, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xDF0202},
  {0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202},
  {0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202, 0xDF0202},
  {0xDF0202, 0xDF0202, 0xFFFFFF, 0x000000, 0x000000, 0xFFFFFF, 0xFFFFFF, 0x000000, 0x000000, 0xFFFFFF, 0xDF0202, 0xDF0202},
  {0xDF0202, 0xFFFFFF, 0xFFFFFF, 0x000000, 0x000000, 0xFFFFFF, 0xFFFFFF, 0x000000, 0x000000, 0xFFFFFF, 0xFFFFFF, 0xDF0202},
  {0x000000, 0xFFFFFF, 0xFFFFFF, 0x000000, 0x000000, 0xFFFFFF, 0xFFFFFF, 0x000000, 0x000000, 0xFFFFFF, 0xFFFFFF, 0x000000},
  {0x000000, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0x000000},
  {0x000000, 0x000000, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0x000000, 0x000000}
};

const unsigned long flower[12][12] PROGMEM = {{0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0xFC3205, 0xFC3205, 0xFC3205, 0xFC3205, 0xFC3205, 0xFC3205, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0xFC3205, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFC3205, 0x000000, 0x000000},
  {0x000000, 0xFC3205, 0xFAFE13, 0xFFFFFF, 0x000000, 0xFFFFFF, 0xFFFFFF, 0x000000, 0xFFFFFF, 0xFAFE13, 0xFC3205, 0x000000},
  {0x000000, 0xFC3205, 0xFAFE13, 0xFFFFFF, 0x000000, 0xFFFFFF, 0xFFFFFF, 0x000000, 0xFFFFFF, 0xFAFE13, 0xFC3205, 0x000000},
  {0x000000, 0x000000, 0xFC3205, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFC3205, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0xFC3205, 0xFC3205, 0xFC3205, 0xFC3205, 0xFC3205, 0xFC3205, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x0DFC03, 0x0DFC03, 0x000000, 0x000000, 0x0DFC03, 0x0DFC03, 0x000000, 0x000000, 0x0DFC03, 0x0DFC03, 0x000000},
  {0x000000, 0x0DFC03, 0x0DFC03, 0x0DFC03, 0x000000, 0x0DFC03, 0x0DFC03, 0x000000, 0x0DFC03, 0x0DFC03, 0x0DFC03, 0x000000},
  {0x000000, 0x000000, 0x0DFC03, 0x0DFC03, 0x0DFC03, 0x0DFC03, 0x0DFC03, 0x0DFC03, 0x0DFC03, 0x0DFC03, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x0DFC03, 0x0DFC03, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000}
};

const unsigned long star[12][12] PROGMEM = {{0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0xFAFE13, 0xFAFE13, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0x000000},
  {0x000000, 0x000000, 0xFAFE13, 0xFAFE13, 0x000000, 0xFAFE13, 0xFAFE13, 0x000000, 0xFAFE13, 0xFAFE13, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0xFAFE13, 0x000000, 0xFAFE13, 0xFAFE13, 0x000000, 0xFAFE13, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0x000000, 0x000000, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0x000000, 0x000000},
  {0x000000, 0xFAFE13, 0xFAFE13, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0xFAFE13, 0xFAFE13, 0x000000}
};


const unsigned long beer[12][12] PROGMEM = {{0x020202, 0x020202, 0x020202, 0x020202, 0x020202, 0x020202, 0x020202, 0x020202, 0x020202, 0x020202, 0x020202, 0x020202},
  {0x000000, 0x000000, 0x000000, 0xBEBEBD, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xBEBEBD, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0xBEBEBD, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xBEBEBD, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0xB49C04, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xB49C04, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0x000000},
  {0x000000, 0x000000, 0x000000, 0xB49C04, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xB49C04, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0x000000},
  {0x000000, 0x000000, 0x000000, 0xB49C04, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xB49C04, 0x000000, 0x000000, 0xFFFFFF, 0x000000},
  {0x000000, 0x000000, 0x000000, 0xB49C04, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xB49C04, 0x000000, 0x000000, 0xFFFFFF, 0x000000},
  {0x000000, 0x000000, 0x000000, 0xB49C04, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xB49C04, 0x000000, 0x000000, 0xFFFFFF, 0x000000},
  {0x000000, 0x000000, 0x000000, 0xB49C04, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xB49C04, 0x000000, 0x000000, 0xFFFFFF, 0x000000},
  {0x000000, 0x000000, 0x000000, 0xB49C04, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xB49C04, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0x000000},
  {0x000000, 0x000000, 0x000000, 0xB49C04, 0xFAFE13, 0xFAFE13, 0xFAFE13, 0xA48E01, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0xB49C04, 0xB49C04, 0xB49C04, 0xB49C04, 0xB49C04, 0x000000, 0x000000, 0x000000, 0x000000}
};

imgMenu.h

Arduino
const unsigned long PROGMEM menuError[12][12] = {{0xEF3101, 0xED2D01, 0x040100, 0x040100, 0x000000, 0x000000, 0x000000, 0x000000, 0x040100, 0x040100, 0xEB2801, 0xED2C01},
  {0xED2C01, 0xEB2701, 0xE92202, 0x040100, 0x040100, 0x000000, 0x000000, 0x040100, 0x040100, 0xE81F02, 0xEA2301, 0xEB2701},
  {0x040100, 0xE92202, 0xE81E01, 0xE61A01, 0x040000, 0x040000, 0x040000, 0x040000, 0xE61601, 0xE61A02, 0xE71D02, 0x040100},
  {0x040100, 0x040100, 0xE61901, 0xE51501, 0xE31101, 0x040000, 0x040000, 0xE20F01, 0xE41202, 0xE51501, 0x040100, 0x040100},
  {0x000000, 0x040000, 0x040000, 0xE31102, 0xE20D02, 0xE10A02, 0xE00902, 0xE10B02, 0xE20D02, 0x040000, 0x040000, 0x000000},
  {0x000000, 0x000000, 0x040000, 0x040000, 0xE10B01, 0xDF0701, 0xDF0602, 0xE00702, 0x040000, 0x040000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x040000, 0x040000, 0xE10902, 0xDF0502, 0xDE0202, 0xDF0501, 0x040000, 0x040000, 0x000000, 0x000000},
  {0x000000, 0x040000, 0x040000, 0xE30F02, 0xE10A02, 0xE00702, 0xE00502, 0xE00701, 0xE10A02, 0x040000, 0x040000, 0x000000},
  {0x040100, 0x040000, 0xE61601, 0xE41102, 0xE20D02, 0x040000, 0x040000, 0xE10B02, 0xE20D02, 0xE41101, 0x040000, 0x040100},
  {0x040100, 0xE81F01, 0xE71A01, 0xE51501, 0x040000, 0x040000, 0x040000, 0x040000, 0xE31202, 0xE51502, 0xE61A01, 0x040100},
  {0xEB2801, 0xEA2301, 0xE81E01, 0x040000, 0x040000, 0x000000, 0x000000, 0x040000, 0x040000, 0xE71A01, 0xE81D01, 0xEA2301},
  {0xED2C01, 0xEB2801, 0x040100, 0x040000, 0x000000, 0x000000, 0x000000, 0x000000, 0x040000, 0x040000, 0xE92202, 0xEB2701}
};

const unsigned long PROGMEM menuGameOfLife[12][12] = {{0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x00FF00, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},

  {0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3},
  {0xb2a24, 0x2cb194, 0xe281f, 0xc2720, 0xb261f, 0xb241e, 0x8231e, 0xa231f, 0x258d76, 0x8211d, 0x91e19, 0x91e19},
  {0xb2823, 0xb2823, 0xa2723, 0x92621, 0x27a285, 0x7251d, 0x25977e, 0x7221d, 0x9201a, 0x9201a, 0x1e7f6b, 0x81b17},
  {0x2bb292, 0x2aac90, 0x7281f, 0x27a486, 0xa231f, 0xc231b, 0xa2117, 0x238a75, 0x91f1c, 0x81d18, 0x91c1a, 0x1d775e},
  {0x2aad8d, 0xc261d, 0xc251f, 0x9241d, 0x92320, 0xb211e, 0x228c76, 0x23876f, 0x91e19, 0x1e7b68, 0x81b17, 0x71a18},
  {0x29aa8e, 0x27a387, 0x8261c, 0xc221f, 0x21967b, 0x248e77, 0x228a73, 0x1f826e, 0x1e7d67, 0x71c15, 0x71a16, 0x1d6c59},
  {0xc2521, 0x26a286, 0x269b81, 0x28967d, 0x8211b, 0xa211b, 0x218470, 0xa1d19, 0x91c1a, 0x71c17, 0x81815, 0x61915},
  {0xa2520, 0xa231f, 0x25977d, 0x9221c, 0x248a75, 0x1f8770, 0xa1f1a, 0x71c17, 0x81b17, 0x1d6e5b, 0x1a6956, 0x196251},
  {0xa2520, 0x25977d, 0x8231c, 0x7221b, 0x92018, 0x22816d, 0x1f7c69, 0x1c7563, 0x91916, 0x61915, 0x196352, 0x71613},
  {0x259a7f, 0x8231e, 0x8211b, 0x91f1c, 0x20836e, 0x91e17, 0x71c17, 0x91916, 0x1a6b5a, 0x1a6455, 0x81712, 0x61512},
  {0xb211e, 0x82219, 0x228974, 0x22836f, 0x1e7f6b, 0x1d7962, 0x71b19, 0x1b6c59, 0x1a6755, 0x186150, 0x175b4c, 0x514f}
};

const unsigned long PROGMEM menuPong[12][12] = {{0x000000, 0x000000, 0xFF0000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},

  {0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3},
  {0x000000, 0x000000, 0xFF0000, 0xFF0000, 0xFF0000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0xFF0000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0xFF0000, 0xFF0000, 0xFF0000, 0x000000, 0x000000}
};


const unsigned long PROGMEM menuTetris[12][12] = {{0x00a651, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},

  {0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0xFF0000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0xFF0000, 0xFF0000, 0xFF0000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0xFF0000, 0xFF0000, 0x000000, 0x000000},
  {0xFF0000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0xFF0000, 0xFF0000, 0x4DBB2E, 0x000000},
  {0xFF0000, 0xFF0000, 0x4DBB2E, 0x4DBB2E, 0x000000, 0x000000, 0xD4BD0D, 0xD4BD0D, 0xD4BD0D, 0x4DBB2E, 0x4DBB2E, 0xFF0000},
  {0xFF0000, 0x0DA5D4, 0x4DBB2E, 0x4DBB2E, 0xFF0000, 0x000000, 0x4DBB2E, 0xD4BD0D, 0x0DA5D4, 0x0DA5D4, 0x4DBB2E, 0xFF0000},
  {0x0DA5D4, 0x0DA5D4, 0x0DA5D4, 0x000000, 0xFF0000, 0x4DBB2E, 0x4DBB2E, 0x000000, 0x0DA5D4, 0xD4BD0D, 0xD4BD0D, 0xFF0000},
  {0xD4BD0D, 0xD4BD0D, 0xD4BD0D, 0xD4BD0D, 0xFF0000, 0xFF0000, 0x4DBB2E, 0x000000, 0x0DA5D4, 0xD4BD0D, 0xD4BD0D, 0xFF0000}
};


const unsigned long PROGMEM menuSnake[12][12] = {{0x000000, 0x000000, 0x000000, 0xF26522, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},

  {0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x388FEC, 0x388FEC, 0x000000, 0xFF0000, 0x000000, 0x388FEC, 0x388FEC, 0x388FEC, 0x000000, 0x000000, 0x388FEC, 0x388FEC},
  {0x000000, 0x388FEC, 0x000000, 0x000000, 0x000000, 0x388FEC, 0x000000, 0x388FEC, 0x000000, 0x000000, 0x388FEC, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x388FEC, 0x000000, 0x388FEC, 0x388FEC, 0x388FEC, 0x388FEC, 0x000000},
  {0x000000, 0x000000, 0x388FEC, 0x000000, 0x000000, 0x388FEC, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x388FEC, 0x000000, 0x000000, 0x388FEC, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x388FEC, 0x388FEC, 0x388FEC, 0x388FEC, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000}
};

const unsigned long PROGMEM menuSimon[12][12] = 
{{0x000000, 0x388FEC, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3},
  {0xFFF202, 0xFFF202, 0xFFF20C, 0xFFF202, 0xFFF202, 0x000000, 0x000000, 0xFF0202, 0xFF191A, 0xFF2727, 0xFF1A1A, 0xFF0202},
  {0xFFF202, 0xFFF234, 0xFFF257, 0xFFF235, 0xFFF202, 0x000000, 0x000000, 0xFF191A, 0xFF4C4C, 0xFF6767, 0xFF4C4C, 0xFF1A19},
  {0xFFF20C, 0xFFF257, 0xFFF898, 0xFFF257, 0xFFF20C, 0x000000, 0x000000, 0xFF2727, 0xFF6767, 0xFF9797, 0xFF6767, 0xFF2726},
  {0xFFF202, 0xFFF235, 0xFFF257, 0xFFF234, 0xFFF202, 0x000000, 0x000000, 0xFF1919, 0xFF4C4C, 0xFF6767, 0xFF4C4C, 0xFF1A19},
  {0xFFF202, 0xFFF202, 0xFFF20C, 0xFFF202, 0xFFF202, 0x000000, 0x000000, 0xFF0202, 0xFF1A19, 0xFF2626, 0xFF1A1A, 0xFF0202},
  {0x34BA20, 0x34BA20, 0x34BA20, 0x34BA20, 0x34BA20, 0x000000, 0x000000, 0x2760B9, 0x4360B9, 0x4F68B9, 0x4360B9, 0x2760B9},
  {0x34BA20, 0x48BA40, 0x6BBA63, 0x48BA41, 0x34BA20, 0x000000, 0x000000, 0x4360B9, 0x6D87B9, 0x819ABE, 0x6D86B9, 0x4360B9},
  {0x34BA20, 0x6BBA63, 0xACE2A4, 0x6BBA63, 0x34BA20, 0x000000, 0x000000, 0x4F68B9, 0x819ABE, 0xA4BEE1, 0x819BBE, 0x4F68B9},
  {0x34BA20, 0x49BA40, 0x6BBA63, 0x48BA41, 0x34BA20, 0x000000, 0x000000, 0x4360B9, 0x6D86B9, 0x819BBE, 0x6D86B9, 0x4360B9},
  {0x34BA20, 0x34BA20, 0x34BA20, 0x34BA20, 0x34BA20, 0x000000, 0x000000, 0x2760B9, 0x4360B9, 0x4F68B9, 0x4360B9, 0x2760B9}
};


const unsigned long PROGMEM menuBottle[12][12] = 
{{0x000000, 0x000000, 0x000000, 0x000000, 0xFFF200, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x02F900, 0x0EFA00, 0x1FFB01},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x33FC01, 0x41FE00, 0x4BFE00},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x7BFF02, 0x7AFE02, 0x79FF01},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000}
};



const unsigned long PROGMEM  menuParty[12][12] = {{0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x960055, 0x960055, 0x000000, 0x000000, 0x000000, 0x000000},

  {0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3},
  {0x00A651, 0x00A651, 0x00A651, 0x00A651, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x00A651, 0x00A651},
  {0xEC008C, 0xEC008C, 0xEC008C, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0xEC008C, 0xEC008C, 0xEC008C},
  {0xF7941D, 0xF7941D, 0xF7941D, 0xF7941D, 0xF7941D, 0xF7941D, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x00AEEF, 0x00AEEF, 0x00AEEF, 0x00AEEF, 0x00AEEF, 0x00AEEF, 0x000000},
  {0xFFF200, 0xFFF200, 0xFFF200, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0xFFF200, 0xFFF200, 0xFFF200},
  {0x000000, 0x000000, 0xED1C24, 0xED1C24, 0xED1C24, 0xED1C24, 0xED1C24, 0xED1C24, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x2E3192, 0x2E3192, 0x2E3192, 0x2E3192, 0x2E3192, 0x2E3192, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x00A651, 0x00A651, 0x00A651, 0x00A651, 0x00A651, 0x00A651},
  {0x000000, 0x000000, 0x000000, 0xF7941D, 0xF7941D, 0xF7941D, 0xF7941D, 0xF7941D, 0xF7941D, 0x000000, 0x000000, 0x000000},
  {0xEC008C, 0xEC008C, 0xEC008C, 0xEC008C, 0xEC008C, 0xEC008C, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000}
};

const unsigned long PROGMEM menuImage[12][12] = {{0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0xFF0000, 0xFF0000, 0x000000, 0x000000},

  {0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0xAC8255, 0xFBBD7B, 0xE8AF72, 0xE8AF72, 0xE8AF72, 0xE8AF72, 0xE8AF72, 0xEAB276, 0xE7A45D, 0x9E6C36, 0x000000},
  {0x000000, 0xAD7F4F, 0xF1ECCE, 0xD6FFFF, 0xD4FFFF, 0xDCFFFF, 0xFAFFF9, 0xFFFF3E, 0xFFD529, 0xF4A05C, 0x9D6B36, 0x000000},
  {0x000000, 0xAD7F4F, 0xF1ECCE, 0xD6FFFF, 0xD4FFFF, 0xDCFFFF, 0xFAFFFA, 0xFFFF42, 0xFFFF00, 0xF4D31A, 0x9D683A, 0x000000},
  {0x000000, 0xAB8959, 0xFF5638, 0xFF0000, 0xFF0000, 0xFF2020, 0xFFDFDF, 0xFFFFFF, 0xFFFFFF, 0xF4D3B0, 0x9D6830, 0x000000},
  {0x000000, 0xBD8B4E, 0xFF5731, 0xFF0000, 0xFF0000, 0xFF0000, 0xFE0505, 0xE1BDBE, 0xD5FFFF, 0xDDD3B0, 0x9E6830, 0x000000},
  {0x000000, 0xB68959, 0x734338, 0x007100, 0x00BF00, 0x001920, 0x0066DF, 0x0088FF, 0x0080FF, 0x5F89B0, 0xA76E30, 0x000000},
  {0x000000, 0xB67F59, 0x73EC38, 0x00FF00, 0x00FF00, 0x00FF00, 0x00FD05, 0x00A1BE, 0x0076FF, 0x5F89B0, 0xA76E30, 0x000000},
  {0x000000, 0x9F6D38, 0xD38B3D, 0xC7843C, 0xC7843C, 0xC7843C, 0xC7843C, 0xC7843C, 0xC7843C, 0xD38B3D, 0x9F6D38, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000}
};

const unsigned long PROGMEM menuSpectrum[12][12] = {{0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0xffff00},

  {0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x531A8D, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x5E2598, 0x000000, 0x2585A9, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x3696B9, 0x591F93, 0x2585A9, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x642B9E, 0x3696B9, 0x3389AA, 0x2585A9, 0x000000, 0x490F83, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x6F36A9, 0x000000, 0x3E9EC1, 0x3C92B3, 0x3389AA, 0x2585A9, 0x4E1488, 0x16769A, 0x000000, 0x000000, 0x000000},
  {0x743BAE, 0x4EAED2, 0x000000, 0x3E9DC1, 0x3C92B3, 0x3289AA, 0x2A80A1, 0x217898, 0x167699, 0x000000, 0x000000, 0x000000},
  {0x60B7D7, 0x4EAED2, 0x000000, 0x3E9DC1, 0x3C92B3, 0x3289AA, 0x2A80A0, 0x217798, 0x16769A, 0x000000, 0x40077A, 0x000000},
  {0x60B7D7, 0x4EAED2, 0x6A30A4, 0x3E9EC1, 0x3C92B3, 0x3289AA, 0x2A80A1, 0x217898, 0x167699, 0x000000, 0x0A698D, 0x3D0376},
  {0x60B7D7, 0x57AECE, 0x4FA5C5, 0x459CBC, 0x3C92B3, 0x3389A9, 0x2A80A1, 0x217898, 0x167699, 0x440B7E, 0x09698D, 0x065C7C}
};


const unsigned long PROGMEM menuText[12][12] = {{0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0xFFF200, 0x000000},

  {0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3, 0xFDCAD3},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0xFF0000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0xFF0000, 0x000000, 0x000000, 0x000000, 0x000000, 0xFF0000, 0xFF0000, 0xFF0000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0xFF0000, 0x000000, 0x000000, 0x000000, 0xFF0000, 0x000000, 0x000000, 0x000000, 0xFF0000, 0x000000},
  {0x000000, 0x000000, 0xFF0000, 0x000000, 0x000000, 0x000000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000, 0x000000},
  {0x000000, 0x000000, 0xFF0000, 0x000000, 0x000000, 0x000000, 0xFF0000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0xFF0000, 0x000000, 0x000000, 0x000000, 0xFF0000, 0x000000, 0x000000, 0x000000, 0xFF0000, 0x000000},
  {0x000000, 0x000000, 0xFF0000, 0x000000, 0x000000, 0x000000, 0x000000, 0xFF0000, 0xFF0000, 0xFF0000, 0x000000, 0x000000},
  {0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000}
};

pong.h

Arduino
byte scorePlayerLeft = 0;
byte scorePlayerRight = 0;

byte positionPlayerLeft;
byte positionPlayerRight;

uint8_t ballx;
uint8_t previousBallx;
uint8_t bally;
uint8_t previousBally;
uint8_t velocityx;
uint8_t velocityy;
byte ballBounces;

byte gameSpeed;

//Arduino : Unsigned long !!!!!
unsigned long lastAutoPlayerMoveTime;
unsigned long rumbleUntil;
unsigned long waitUntil;

void setPosition() {
  if (!digitalRead(R1_PIN)) {
    if (positionPlayerLeft + (3 - 1) / 2 < 12 - 1) {
      ++positionPlayerLeft;
    }
  }
  else if (!digitalRead(L1_PIN)) {
    if (positionPlayerLeft - 3 / 2 > 0) {
      --positionPlayerLeft;
    }
  }
  if (!digitalRead(L2_PIN)) {
    if (positionPlayerRight - 3 / 2 > 0) {
      --positionPlayerRight;
    }
  }
  else if (!digitalRead(R2_PIN)) {
    if (positionPlayerRight + (3 - 1) / 2 < 12 - 1) {
      ++positionPlayerRight;
    }

  }
  curControl = 'n';
}

void checkBallHitByPlayer() {
  if (ballx == 1)
  {
    if (bally == positionPlayerLeft)
    {
      velocityx = 1;
      ballx = 1;
      ++ballBounces;
      rumbleUntil = curTime + 200;
    }
    else if (bally < positionPlayerLeft && bally >= positionPlayerLeft - 3 / 2)
    {
      velocityx = 1;
      velocityy = max(-1, velocityy - 1);
      ballx = 1;
      bally = positionPlayerLeft - 3 / 2 - 1;
      ++ballBounces;
      rumbleUntil = curTime + 200;
    }
    else if (bally > positionPlayerLeft && bally <= positionPlayerLeft + (3 - 1) / 2)
    {
      velocityx = 1;
      velocityy = min(1, velocityy + 1);
      ballx = 1;
      bally = positionPlayerLeft + (3 - 1) / 2 + 1;
      ++ballBounces;
      rumbleUntil = curTime + 200;
    }
  }
  else if (ballx == 12 - 2)
  {
    if (bally == positionPlayerRight)
    {
      velocityx = -1;
      ballx = 12 - 2;
      ++ballBounces;
    }
    else if (bally < positionPlayerRight && bally >= positionPlayerRight - 3 / 2)
    {
      velocityx = -1;
      velocityy = max(-1, velocityy - 1);
      ballx = 12 - 2;
      bally = positionPlayerRight - 3 / 2 - 1;
      ++ballBounces;
    }
    else if (bally > positionPlayerRight && bally <= positionPlayerRight + (3 - 1) / 2)
    {
      velocityx = -1;
      velocityy = min(1, velocityy + 1);
      ballx = 12 - 2;
      bally = positionPlayerRight + (3 - 1) / 2 + 1;
      ++ballBounces;
    }
  }
}

void checkBallOutOfBounds() {
  if (bally < 1)
  {
    velocityy = - velocityy;
    bally = 0;
    //bally = 1;
  } else if (bally > 12 -1)
  {
    velocityy = - velocityy;
    bally = 12 - 2;
    //bally = 12-1;
  }
  if (ballx < 0)
  {
    velocityx = - velocityx;
    velocityy = 0;
    ballx = 12 / 2;
    bally = 12 / 2;
    ++scorePlayerRight;
    ballBounces = 0;
    waitUntil = curTime + 2000;
    fadeImg();
  }
  else if (ballx > 12 - 1)
  {
    velocityx = - velocityx;
    velocityy = 0;
    ballx = 12 / 2;
    bally = 12 / 2;
    ++scorePlayerLeft;
    ballBounces = 0;
    waitUntil = curTime + 2000;
    fadeImg();
  }
}

/*boolean moveAutoPlayer()
  {
  if (bally < positionPlayerRight)
  {
    if (positionPlayerRight - 3 / 2 > 0)
    {
      --positionPlayerRight;
      return true;
    }
  }
  else if (bally > positionPlayerRight)
  {
    if (positionPlayerRight + (3 - 1) / 2 < 12 - 1)
    {
      ++positionPlayerRight;
      return true;
    }
  }
  return false;
  }*/

void pongInit() {
  scorePlayerLeft  = 0;
  scorePlayerRight = 0;
  positionPlayerLeft  = 12 / 2;
  positionPlayerRight = 12 / 2;
  ballx = 12 / 2;
  bally = 12 / 2;
  velocityx = 1;
  velocityy = 0;
  ballBounces = 0;
  gameSpeed = 180;
  lastAutoPlayerMoveTime = 0;
  rumbleUntil = 0;
  waitUntil = 0;
}

void runPong() {
  pongInit();

  boolean pongRunning = true;
  while (pongRunning) {

    if (scorePlayerLeft == 5) {
      pongRunning = false;
      scrollText("Game over", 9, 0xff0000);
      scorePlayerLeft = 0;
      scorePlayerRight = 0;
      break;
    }  if (scorePlayerRight == 5) {
      pongRunning = false;
      scrollText("Game over", 9, 0xff0000);
      scorePlayerLeft = 0;
      scorePlayerRight = 0;
      break;
    }

    checkBallHitByPlayer();

    /* if ((curTime - lastAutoPlayerMoveTime) > 200) {
       if (moveAutoPlayer()) {
         lastAutoPlayerMoveTime = curTime;
       }
      }*/

    ballx += velocityx;
    bally += velocityy;

    checkBallOutOfBounds();
    for (byte i = 0; i < 12; i++) {
      for (byte j = 0; j < 12; j++) {
        leds[pgm_read_byte(&ledsAdress[i][j])] = 0x000000;
      }
    }


    // Draw ball
    leds[pgm_read_byte(&ledsAdress[ballx][bally])] = 0xffffff;
    // Draw player left
    for (int y = positionPlayerLeft - 3 / 2; y <= positionPlayerLeft + 3 / 2; ++y) {
      leds[pgm_read_byte(&ledsAdress[0][y])] = 0x0000ff;
    }
    // Draw player right
    for (int y = positionPlayerRight - 3 / 2; y <= positionPlayerRight + 3 / 2; ++y) {
      leds[pgm_read_byte(&ledsAdress[12 - 1][y])] = 0xffff00;
    }

    FastLED.show();
    boolean dirChanged = false;
    do {
      if (menuValidation) {
        pongRunning = false;
        break;
      }
      readPointer();
      if (curControl != 'n' && !dirChanged) { //Can only change direction once per loop
        dirChanged = true;
        setPosition();
      }
      curTime = millis();
    }
    while ((curTime - prevUpdateTime) < 140); //Once enough time  has passed, proceed. The lower this number, the faster the game is
    prevUpdateTime = curTime;
  }
}

snake.h

Arduino
byte curLength;//Curren length of snake
int xs[127];//Array containing all snake segments,
int ys[127];// max snake length is array length
char dir;//Current Direction of snake

boolean snakeGameOver;

byte ax = 0;//Apple x position
byte ay = 0;//Apple y position

void snakeInit() {
  //Snake start position and direction & initialise variables
  curLength = 3;
  xs[0] = 3;
  xs[1] = 2;
  xs[2] = 1;
  ys[0] = 12 / 2;
  ys[1] = 12 / 2;
  ys[2] = 12 / 2;
  dir = 'd';
  //Generate random apple position
  ax = random(0, 11);
  ay = random(0, 11);

  snakeGameOver = false;
}
/* Set direction from current button state */
void setDirection() {
  readPointer();
  switch (curControl) {
    case 'q':
      dir = 'z';
      break;
    case 'd':
      dir = 's';
      break;
    case 's':
      dir = 'd';
      break;
    case 'z':
      dir = 'q';
      break;
  }
}

/* Ending, show score */
void die() {
  snakeGameOver = true;
  fadeImg();
}

/* Collision detection function */
boolean collide(int x1, int x2, int y1, int y2, int w1, int w2, int h1, int h2) {
  if ((x1 + w1 > x2) && (x1 < x2 + w2) && (y1 + h1 > y2) && (y1 < y2 + h2)) {
    return true;
  } else {
    return false;
  }
}

void runSnake() {
  snakeInit();
  prevUpdateTime = 0;
  boolean snakeRunning = true;
  while (snakeRunning) {
    //Check self-collision with snake
    int i = curLength - 1;
    while (i >= 2) {
      if (collide(xs[0], xs[i], ys[0], ys[i], 1, 1, 1, 1)) {
        die();
      }
      i = i - 1;
    }

    if (snakeGameOver) {
      snakeRunning = false;
      break;
    }

    //Check collision of snake head with apple
    if (collide(xs[0], ax, ys[0], ay, 1, 1, 1, 1)) {
      //Increase score and snake length;
      curLength = curLength + 1;
      //Add snake segment with temporary position of new segments
      xs[curLength - 1] = 255;
      ys[curLength - 1] = 255;

      //Generate new apple position
      ax = random(0, 11);
      ay = random(0, 11);

      for (int j = 0; j < curLength; j++) {
        if (collide(ax, xs[j], ay, ys[j], 1, 1, 1, 1)) {
          ax = random(0, 11);
          ay = random(0, 11);
          j = 0; //ICI
        }
      }
    }

    //Shift snake position array by one
    i = curLength - 1;
    while (i >= 1) {
      xs[i] = xs[i - 1];
      ys[i] = ys[i - 1];
      i = i - 1;
    }
    //Determine new position of head of snake
    if (dir == 'd') {
      xs[0] = xs[0] + 1;
    } else if (dir == 'q') {
      xs[0] = xs[0] - 1;
    } else if (dir == 'z') {
      ys[0] = ys[0] - 1;
    } else {//DOWN
      ys[0] = ys[0] + 1;
    }

    //Check if outside playing field
    if ((xs[0] < 0) || (xs[0] >= 12) || (ys[0] < 0) || (ys[0] >= 12)) {
      if (xs[0] < 0) {
        xs[0] = 12 - 1;
      } else if (xs[0] >= 12) {
        xs[0] = 0;
      } else if (ys[0] < 0) {
        ys[0] = 12 - 1;
      } else if (ys[0] >= 12) {
        ys[0] = 0;
      }
    }

    for (int j = 0; j < 144; j++) {
      leds[j] = 0x000000;

    }

    //Draw apple
    leds[pgm_read_byte(&ledsAdress[ax][ay])] = 0xFF0000;

    //Draw snake
    for (int j = 0; j < curLength; j++) {
      leds[pgm_read_byte(&ledsAdress[xs[j]] [ys[j]])] = 0x388FEC;
    }

    FastLED.show();

    //Check buttons and set snake movement direction while we are waiting to draw the next move
    curTime = 0;
    boolean dirChanged = false;
    do {
      if (menuValidation) {
        snakeRunning = false;
        break;
      }
      if (curControl != 'n' && !dirChanged) {//Can only change direction once per loop
        dirChanged = true;
        setDirection();
      }
      curTime = millis();
    } while ((curTime - prevUpdateTime) < 200);//Once enough time  has passed, proceed. The lower this number, the faster the game is
    prevUpdateTime = curTime;
  }


}

tetris.h

Arduino
// Playing field
byte selectedColor = 0;
const unsigned long PROGMEM colorLib[5] = {0x000000, 0xff0000, 0xffff00, 0x00ff00, 0x03cdff};
struct Field {
  boolean pix[12][12 + 1]; //Make field one larger so that collision detection with bottom of field can be done in a uniform way
  byte color[12][12];
};
Field field;

//Structure to represent active brick on screen
struct Brick {
  boolean enabled;//Brick is disabled when it has landed
  byte xpos, ypos;
  byte yOffset;//Y-offset to use when placing brick at top of field
  byte siz;
  boolean pix[4][4];

  byte color;
};
Brick activeBrick;

//Struct to contain the different choices of blocks
struct AbstractBrick {
  byte yOffset;//Y-offset to use when placing brick at top of field
  byte siz;
  byte pix[4][4];
};

//Brick "library"
AbstractBrick brickLib[7] = {
  {
    1,//yoffset when adding brick to field
    4,
    { {0, 0, 0, 0},
      {0, 1, 1, 0},
      {0, 1, 1, 0},
      {0, 0, 0, 0}
    }
  },
  {
    0,
    4,
    { {0, 1, 0, 0},
      {0, 1, 0, 0},
      {0, 1, 0, 0},
      {0, 1, 0, 0}
    }
  },
  {
    1,
    3,
    { {0, 0, 0, 0},
      {1, 1, 1, 0},
      {0, 0, 1, 0},
      {0, 0, 0, 0}
    }
  },
  {
    1,
    3,
    { {0, 0, 1, 0},
      {1, 1, 1, 0},
      {0, 0, 0, 0},
      {0, 0, 0, 0}
    }
  },
  {
    1,
    3,
    { {0, 0, 0, 0},
      {1, 1, 1, 0},
      {0, 1, 0, 0},
      {0, 0, 0, 0}
    }
  },
  {
    1,
    3,
    { {0, 1, 1, 0},
      {1, 1, 0, 0},
      {0, 0, 0, 0},
      {0, 0, 0, 0}
    }
  },
  {
    1,
    3,
    { {1, 1, 0, 0},
      {0, 1, 1, 0},
      {0, 0, 0, 0},
      {0, 0, 0, 0}
    }
  }
};


uint16_t brickSpeed;
byte nbRowsThisLevel;
uint16_t nbRowsTotal;
Brick tmpBrick;
boolean tetrisRunning = false;
boolean tetrisGameOver;

/* *** Game functions *** */
//Check collision between bricks in the field and the specified brick
boolean checkFieldCollision(struct Brick* brick) {
  byte bx, by;
  byte fx, fy;
  for (by = 0; by < 4; by++) {
    for (bx = 0; bx < 4; bx++) {
      fx = (*brick).xpos + bx;
      fy = (*brick).ypos + by;
      if (( (*brick).pix[bx][by] == 1)
          && ( field.pix[fx][fy] == 1)) {
        return true;
      }
    }
  }
  return false;
}

void newActiveBrick() {
  //  byte selectedBrick = 3;
  byte selectedBrick = random(7);
  ++selectedColor;
  if (selectedColor > 3) {
    selectedColor = 0;
  }
  //Set properties of brick
  activeBrick.siz = brickLib[selectedBrick].siz;
  activeBrick.yOffset = brickLib[selectedBrick].yOffset;
  activeBrick.xpos = 12 / 2 - activeBrick.siz / 2;
  activeBrick.ypos = -1 - activeBrick.yOffset;
  activeBrick.enabled = true;

  //Set color of brick
  switch (selectedColor) {
    case 0 :
      activeBrick.color = 1;
      break;
    case 1 :
      activeBrick.color = 2;
      break;
    case 2 :
      activeBrick.color = 3;
      break;
    case 3 :
      activeBrick.color = 4;
      break;
  }
  //activeBrick.color = colorLib[1];

  //Copy pix array of selected Brick
  for (byte y = 0; y < 4; y++) {
    for (byte x = 0; x < 4; x++) {
      activeBrick.pix[x][y] = (brickLib[selectedBrick]).pix[x][y];
    }
  }

  //Check collision, if already, then game is over
  if (checkFieldCollision(&activeBrick)) {
    tetrisGameOver = true;
  }
}

//Check collision between specified brick and all sides of the playing field
boolean checkSidesCollision(struct Brick* brick) {
  //Check vertical collision with sides of field
  byte fx, fy;
  for (byte by = 0; by < 4; by++) {
    for (byte bx = 0; bx < 4; bx++) {
      if ( (*brick).pix[bx][by] == 1) {
        fx = (*brick).xpos + bx;//Determine actual position in the field of the current pix of the brick
        fy = (*brick).ypos + by;
        if (fx < 0 || fx >= 12) {
          return true;
        }
      }
    }
  }
  return false;
}


void printField() {
  for (byte x = 0; x < 12; x++) {
    for (byte y = 0; y < 12; y++) {
      boolean activeBrickPix = 0;
      if (activeBrick.enabled) { //Only draw brick if it is enabled
        //Now check if brick is "in view"
        if ((x >= activeBrick.xpos) && (x < (activeBrick.xpos + (activeBrick.siz)))
            && (y >= activeBrick.ypos) && (y < (activeBrick.ypos + (activeBrick.siz)))) {
          activeBrickPix = (activeBrick.pix)[x - activeBrick.xpos][y - activeBrick.ypos];
        }
      }
      if (field.pix[x][y] == 1) {
        leds[pgm_read_byte(&ledsAdress[y][x])] = pgm_read_dword(&colorLib[field.color[x][y]]);
      } else if (activeBrickPix == 1) {
        leds[pgm_read_byte(&ledsAdress[y][x])] = pgm_read_dword(&colorLib[activeBrick.color]);
      } else {
        leds[pgm_read_byte(&ledsAdress[y][x])] = 0x000000;
      }
    }
  }
  FastLED.show();
}

void rotateActiveBrick() {
  //Copy active brick pix array to temporary pix array
  for (byte y = 0; y < 4; y++) {
    for (byte x = 0; x < 4; x++) {
      tmpBrick.pix[x][y] = activeBrick.pix[x][y];
    }
  }
  tmpBrick.xpos = activeBrick.xpos;
  tmpBrick.ypos = activeBrick.ypos;
  tmpBrick.siz = activeBrick.siz;

  //Depending on size of the active brick, we will rotate differently
  if (activeBrick.siz == 3) {
    //Perform rotation around center pix
    tmpBrick.pix[0][0] = activeBrick.pix[0][2];
    tmpBrick.pix[0][1] = activeBrick.pix[1][2];
    tmpBrick.pix[0][2] = activeBrick.pix[2][2];
    tmpBrick.pix[1][0] = activeBrick.pix[0][1];
    tmpBrick.pix[1][1] = activeBrick.pix[1][1];
    tmpBrick.pix[1][2] = activeBrick.pix[2][1];
    tmpBrick.pix[2][0] = activeBrick.pix[0][0];
    tmpBrick.pix[2][1] = activeBrick.pix[1][0];
    tmpBrick.pix[2][2] = activeBrick.pix[2][0];
    //Keep other parts of temporary block clear
    tmpBrick.pix[0][3] = 0;
    tmpBrick.pix[1][3] = 0;
    tmpBrick.pix[2][3] = 0;
    tmpBrick.pix[3][3] = 0;
    tmpBrick.pix[3][2] = 0;
    tmpBrick.pix[3][1] = 0;
    tmpBrick.pix[3][0] = 0;

  } else if (activeBrick.siz == 4) {
    //Perform rotation around center "cross"
    tmpBrick.pix[0][0] = activeBrick.pix[0][3];
    tmpBrick.pix[0][1] = activeBrick.pix[1][3];
    tmpBrick.pix[0][2] = activeBrick.pix[2][3];
    tmpBrick.pix[0][3] = activeBrick.pix[3][3];
    tmpBrick.pix[1][0] = activeBrick.pix[0][2];
    tmpBrick.pix[1][1] = activeBrick.pix[1][2];
    tmpBrick.pix[1][2] = activeBrick.pix[2][2];
    tmpBrick.pix[1][3] = activeBrick.pix[3][2];
    tmpBrick.pix[2][0] = activeBrick.pix[0][1];
    tmpBrick.pix[2][1] = activeBrick.pix[1][1];
    tmpBrick.pix[2][2] = activeBrick.pix[2][1];
    tmpBrick.pix[2][3] = activeBrick.pix[3][1];
    tmpBrick.pix[3][0] = activeBrick.pix[0][0];
    tmpBrick.pix[3][1] = activeBrick.pix[1][0];
    tmpBrick.pix[3][2] = activeBrick.pix[2][0];
    tmpBrick.pix[3][3] = activeBrick.pix[3][0];
  }

  //Now validate by checking collision.
  //Collision possibilities:
  //      -Brick now sticks outside field
  //      -Brick now sticks inside fixed bricks of field
  //In case of collision, we just discard the rotated temporary brick
  if ((!checkSidesCollision(&tmpBrick)) && (!checkFieldCollision(&tmpBrick))) {
    //Copy temporary brick pix array to active pix array
    for (byte y = 0; y < 4; y++) {
      for (byte x = 0; x < 4; x++) {
        activeBrick.pix[x][y] = tmpBrick.pix[x][y];
      }
    }
  }
}

//Copy active pixels to field, including color
void addActiveBrickToField() {
  byte fx, fy;
  for (byte by = 0; by < 4; by++) {
    for (byte bx = 0; bx < 4; bx++) {
      fx = activeBrick.xpos + bx;
      fy = activeBrick.ypos + by;

      if (fx >= 0 && fy >= 0 && fx < 12 && fy < 12 && activeBrick.pix[bx][by]) { //Check if inside playing field
        //field.pix[fx][fy] = field.pix[fx][fy] || activeBrick.pix[bx][by];
        field.pix[fx][fy] = activeBrick.pix[bx][by];
        field.color[fx][fy] = activeBrick.color;
      }
    }
  }
}
//Shift brick left/right/down by one if possible
void shiftActiveBrick(char dir) {
  //Change position of active brick (no copy to temporary needed)
  if (dir == 'q') {
    activeBrick.xpos--;
  } else if (dir == 'd') {
    activeBrick.xpos++;
  } else if (dir == 's') {
    activeBrick.ypos++;
  }

  //Check position of active brick
  //Two possibilities when collision is detected:
  //    -Direction was LEFT/RIGHT, just revert position back
  //    -Direction was DOWN, revert position and fix block to field on collision
  //When no collision, keep activeBrick coordinates
  if ((checkSidesCollision(&activeBrick)) || (checkFieldCollision(&activeBrick))) {
    //Serial.println("coll");
    if (dir == 'q') {
      activeBrick.xpos++;
    } else if (dir == 'd') {
      activeBrick.xpos--;
    } else if (dir == 's') {
      activeBrick.ypos--;//Go back up one
      addActiveBrickToField();
      activeBrick.enabled = false;//Disable brick, it is no longer moving
    }
  }
}



//Move all pix from te field above startRow down by one. startRow is overwritten
void moveFieldDownOne(byte startRow) {
  if (startRow == 0) { //Topmost row has nothing on top to move...
    return;
  }
  byte x, y;
  for (y = startRow - 1; y > 0; y--) {
    for (x = 0; x < 12; x++) {
      field.pix[x][y + 1] = field.pix[x][y];
      field.color[x][y + 1] = field.color[x][y];
    }
  }
}

void checkFullLines() {
  int x, y;
  int minY = 0;
  for (y = (12 - 1); y >= minY; y--) {
    byte rowSum = 0;
    for (x = 0; x < 12; x++) {
      rowSum = rowSum + (field.pix[x][y]);
    }
    if (rowSum >= 12) {
      //Found full row, animate its removal
      for (x = 0; x < 12; x++) {
        field.pix[x][y] = 0;
        printField();
        delay(100);
      }
      //Move all upper rows down by one
      moveFieldDownOne(y);
      y++; minY++;
      printField();
      delay(100);

      nbRowsThisLevel++; nbRowsTotal++;
      if (nbRowsThisLevel >= 2) {
        nbRowsThisLevel = 0;
        brickSpeed = brickSpeed - 100;
        if (brickSpeed < 200) {
          brickSpeed = 200;
        }
      }
    }
  }
}

void clearField() {
  for (byte y = 0; y < 12; y++) {
    for (byte x = 0; x < 12; x++) {
      field.pix[x][y] = 0;
      field.color[x][y] = 0;
    }
  }
  for (byte x = 0; x < 12; x++) { //This last row is invisible to the player and only used for the collision detection routine
    field.pix[x][12] = 1;
  }
}

void playerControlActiveBrick() {
  switch (curControl) {
    case 'd':
      shiftActiveBrick('d');
      break;
    case 'q':
      shiftActiveBrick('q');
      break;
    case 's':
      shiftActiveBrick('s');
      break;
    case 'a':
      rotateActiveBrick();
      break;
    case 'y':
      tetrisRunning = false;
      break;
  }
}


void tetrisInit() {
  clearField();
  brickSpeed = 1000;
  nbRowsThisLevel = 0;
  nbRowsTotal = 0;
  tetrisGameOver = false;

  newActiveBrick();
}


void runTetris(void) {
  tetrisInit();

  prevUpdateTime = 0;

  tetrisRunning = true;
  while (tetrisRunning) {
    curTime = 0;

    do {
      readPointer(); //ATTENTION SOURCE BUG ?!
      if (menuValidation) {
        tetrisRunning = false;
        break;
      }
      if (curControl != 'n') {
        playerControlActiveBrick();
        printField();
        curControl = 'n';
      }
      if (tetrisGameOver) break;

      curTime = millis();
    } while ((curTime - prevUpdateTime) < brickSpeed);//Once enough time  has passed, proceed. The lower this number, the faster the game is
    prevUpdateTime = curTime;

    if (tetrisGameOver) {
      fadeImg();

      //Disable loop and exit to main menu of led table
      tetrisRunning = false;
      break;
    }

    //If brick is still "on the loose", then move it down by one
    if (activeBrick.enabled) {
      shiftActiveBrick('s');
    } else {
      //Active brick has "crashed", check for full lines
      //and create new brick at top of field
      checkFullLines();
      newActiveBrick();
      prevUpdateTime = millis();//Reset update time to avoid brick dropping two spaces
    }
    printField();
  }


}

Audio spectrum visualizer

Arduino
FischiMc's code adapted to my LED matrix
/*
  Written by FischiMc and SupaStefe, modified by Antoine Rochebois (scaling to 12*12)

  This sketch uses a 10x10 RGB LED-Matrix as a spectrum analyzer
  It uses a FTT Library to analyze an audio signal connected to the
  pin A7 of an Arduino nano. Everytime a column gets higher than
  10 pixels the color of each column changes.
*/

#define LOG_OUT 0         //set output of FFT library to linear not logarithmical
#define LIN_OUT 1
#define FFT_N 256         //set to 256 point fft

#include <FFT.h>          //include the FFT library
#include <FastLED.h>      //include the FastLED Library
#include <math.h>         //include library for mathematic funcions
#define DATA_PIN 5        //DATA PIN WHERE YOUR LEDS ARE CONNECTED
#define NUM_LEDS 144     //amount of LEDs in your matrix
CRGB leds[NUM_LEDS];
float faktoren[12] = {1, 1.1, 1.15, 1.25, 1.35, 1.45, 1.55, 1.65 , 1.75, 1.8, 2, 3};       //factors to increase the height of each column
unsigned char hs[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0};                       //height of each column
float hue = 0;                                                                //hue value of the colors

void setBalken(unsigned char column, unsigned char height) {                  //calculation of the height of each column
  unsigned char h = (unsigned char)map(height, 0, 255, 0, 12);
  h = (unsigned char)(h * faktoren[column]);
  if (h < hs[column]) {
    hs[column]--;
  }
  else if (h > hs[column]) {
    hs[column] = h;
  }
  if (height > 250) {
    hue += 2;                   //CHANGE THIS VALUE IF YOU WANT THE DIFFERENCE BETWEEN THE COLORS TO BE BIGGER
    if (hue > 25) hue = 0;
  }

  for (unsigned char y = 0; y < 12; y++) {                        //set colors of pixels according to column and hue
    if (hs[column] > y) {
      if (column % 2 == 0) {
        leds[y + (column * 12)] = CHSV((hue * 12) + (column * 12), 255, 200);
      } else {
        leds[12 - y + (column * 12)] = CHSV((hue * 12) + (column * 12), 255, 200);
      }

    } else {
      if (column % 2 == 0) {
        leds[y + (column * 12)] = CRGB::Black;
      } else {
        leds[12 - y + (column * 12)] = CRGB::Black;
      }
    }
  }
}

unsigned char grenzen[13] = {0, 3, 5, 7, 9, 11, 13, 15, 17, 20, 24, 32, 69}; //borders of the frequency areas

void setup() {
  FastLED.addLeds<WS2812B, DATA_PIN, GRB> (leds, NUM_LEDS);
  TIMSK0 = 0;                                                       //turn off timer0 for lower jitter
  ADCSRA = 0xe5;                                                    //set the adc to free running mode
  ADMUX = 0x40;                                               //use pin A0
  DIDR0 = 0x01;                                                     //turn off the digital input for
  analogReference(EXTERNAL);                                        //set aref to external
}

void loop() {
  while (1) {                                                       //reduces jitter
    cli();                                                          //UDRE interrupt slows this way down on arduino1.0
    for (int i = 0 ; i < 512 ; i += 2) {                            //save 256 samples
      while (!(ADCSRA & 0x10));                                     //wait for adc to be ready
      ADCSRA = 0xf5;                                                //restart adc
      byte m = ADCL;                                                //fetch adc data
      byte j = ADCH;
      int k = (j << 8) | m;                                         //form into an int
      k -= 0x0200;                                                  //form into a signed int
      k <<= 6;                                                      //form into a 16b signed int
      fft_input[i] = k;                                             //put real data into even bins
    }

    fft_window();                                                   // window the data for better frequency response
    fft_reorder();                                                  // reorder the data before doing the fft
    fft_run();                                                      // process the data in the fft
    fft_mag_lin();                                                  // take the output of the fft
    sei();

    fft_lin_out[0] = 0;
    fft_lin_out[1] = 0;

    for (unsigned char i = 0; i < 13; i++) {
      unsigned char maxW = 0;
      for (unsigned char x = grenzen[i]; x < grenzen[i + 1]; x++) {

        if ((unsigned char)fft_lin_out[x] > maxW) {
          maxW = (unsigned char)fft_lin_out[x];
        }
      }

      setBalken(i, maxW);

    }
    TIMSK0 = 1;
    FastLED.show();
    TIMSK0 = 0;
  }
}

Credits

Antoine Rochebois

Antoine Rochebois

0 projects • 31 followers
17yo electronic and 3D printing enthusiast | French engineering student at INSA Lyon (National Institute of Applied Sciences)
Thanks to FischiMc.

Comments