Project showcase
Interactive LED Table for 50€

Interactive LED Table for 50€ © CC BY-NC

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

  • 30,380 views
  • 14 comments
  • 102 respects

Components and supplies

LACK table - Ikea
×1
WS2812b 5m - 150 LEDs strip
×1
Ard nano
Arduino Nano R3
You can buy a cheap one in some eastern countries ;)
×1
61pby065esl  sx679  tnr8syww5d
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

Necessary tools and machines

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

About this project

To begin with, I would like to thank my parents and my grandfathers who taught me from an early age the worth and the power of creative work.

WARNING : This is a showcase, not a tutorial. The published code is for illustration and is not running without other files (containing application source codes). Since the games are not fully debugged, I do not want to share crappy code. Sorry about that :(

[04/01/2019 UPDATE] The code is now fully uploaded (Bluetooth and buttons mangement, animations, games fully operational) on this showcase but still verry buggy (RAM overflow)

0. Presentation

In this showcase, I'll explain you how I made a cheap interactive table that uses Bluetooth, physical controls and a LED matrix from a simple 7€ IKEA table. This table is able to display an audio spectrum visualizer, some games and animations.

You can see some of these features in the video below :

Video demonstration of the table :)

1. Preparing the project - Java emulation

After having the idea of a project, the first thing to be done is to define an exhaustive to-do list, a bill of materials and to have a strong idea of what your code will look like.

To cope with this challenge, I designed an emulator for my LED Matrix on Java. The purpose of this step was to set up the main functions and algorithms that I would need to make my device work properly. Doing this also allowed me to know more specifically what hardware (inputs especially) I would need to complete my project.

This program displays a 12x12 color grid and refreshes this grid with a method similar to the "FastLED.show();" function used by the Arduino FastLED library to control the matrix. The program displays the menu and is able to launch several modes : displaying images/animations/text, run Conway's game of life, Tetris, Snake, Pong (for 2 players), the Simon game, etc...

The first issue that I had was to define some static images in the program. The specifications of the Arduino didn't allow me to use .jpg nor .png images, I had to deal with 2D-arrays of 24bits pixels (defined in hexadecimal as 0xRRGGBB). To make things easier, I wrote another Java program that translates a 12x12 bitmap image into the desired 2D-Array.

Once the emulator was in accordance with my expectations, I bought all the necesary components for the project...

2. How to read an audio signal ?

One of the main feature of my table will be to display live an audio spectrum on the screen while I listen to music. In order to achieve that, I used an operational amplifier (the TL072) to center the voltage around 2.5V and to amplify it. The Arduino is now able to read and to analyze the provided audio signal. (thanks to a Fast Fourier Transformation algorithm). This circuit was soldered on a prototype board.

3. Time for some cheap woodwork, wiring and 3D printing !

The 150 LEDs strip that I bought was 5 meters (or 16.4ft) long. That means that my 12x12 screen would be at least (500/150*12)=40cm width and long. In order to have more flexibility in the future, I ordered a 405x405mm white acrylic glass and made a 410x410mm hole in the table with a drill and a saw.

The 7€ Ikea Lack table is so cheap that it is empty on the inside but this is beneficent for our purpose. (By the way, having a vacuum cleaner at your side prevents you from breathing wood dust)

Once I was done with the main hole, I drilled 4 round 40mm holes on the sides of the table to host 4 control buttons and a small hole on the bottom of the table to host the female powerplug. Afterwards, I made a square hole where the main control interface will fit. The buttons on this interface are very small and close to each other, drilling some holes would have led to disappointing results.

In order to solve this problem, I've designed my interface on Fusion360 and 3D printed it. Next, I applied a primer coat on it and was quite satisfied by this finish.

Then, it was time for some wiring. I've soldered and glued everything in place as it is shown in the schematic below.

4. Making the screen !

I think this is the part I'm the less proud of. I strongly advice you to use a very soft foam-board or even a laser cutter to make a decent grid. I cut the LED strip every 12 leds to make 12 small strips and glued them on a 410x410mm foam-board (with the wiring done). I then glued a foam-board grid made with a box cutter. Finally, I glued the acrylic glass on the top of the grid and powered the LED matrix for a test. The problem with the box cutter is that the grid isn't very flat on the top and that the pixels aren't perfectly aligned.

Once installed and wired in the table, the matrix was ready to execute the code that we provided to the Arduino through the USB port.

5. The Android controller

I used the MIT App Inventor software to make a very simple Android application to control my table through Bluetooth.

6. TO DO :

The work isn't finished yet. I need to debug some features, to improve some and even to program some others !

I also want to cut and install some L-profiled aluminium bar in the gap between the screen and the table to make it look nicer and cleaner but I need a hacksaw for that...

This is still work in progress ;)

SCHEMATICS COMING SOON

Code

Arduino main codeArduino
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 LifeArduino
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.hC/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.hC/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.hArduino
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.hArduino
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.hArduino
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.hArduino
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.hArduino
// 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 visualizerArduino
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;
  }
}

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

Comments

Similar projects you might like

Touch LED Table - Retrogaming And Ambiant Light

Project tutorial by Arbalet Project

  • 7,887 views
  • 1 comment
  • 38 respects

Automated Staircase RGB LED Lights

Project tutorial by Team NotEnoughTech.com

  • 27,609 views
  • 19 comments
  • 98 respects

Bluetooth control led with lcd led status display real time.

Project tutorial by Youssef Sabaa

  • 17,443 views
  • 4 comments
  • 34 respects

Interactive LED Name

Project tutorial by Pedro Mendez and Daniel Jablonski

  • 1,872 views
  • 1 comment
  • 5 respects

Otto DIY+ Arduino Bluetooth Robot Easy to 3D Print

Project tutorial by Team Otto builders

  • 48,195 views
  • 117 comments
  • 162 respects

Interactive 4x4x4 LED cube

Project tutorial by Tong Xin Hua

  • 1,781 views
  • 0 comments
  • 0 respects
Add projectSign up / Login