Project showcase

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.

  • 10,210 views
  • 0 comments
  • 37 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
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.

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

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 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);
  }
}
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;
  }
}

Comments

Similar projects you might like

IR Home Automation on DFRobot's Relay Shield

Project tutorial by Techduino

  • 442 views
  • 0 comments
  • 5 respects

Sesame

Project showcase by gibatronic

  • 5,110 views
  • 7 comments
  • 12 respects

Aurdino Radar With Processing

Project in progress by Akshay6766

  • 1,569 views
  • 1 comment
  • 10 respects

Laundry IFTTT Alert

Project tutorial by danvanf

  • 909 views
  • 0 comments
  • 5 respects

Workspace Environment Monitor - enVMon

Project showcase by Team comcrazy

  • 733 views
  • 2 comments
  • 9 respects

Butterfly Alarm Clock

Project tutorial by Patrick Prescott

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