Introduction: The Somewhat Different Birthday Card - Small Eiffel Tower Lightshow

You're as bad as I am at writing birthday cards? I have found a time intensive way not to have to write one!

I'm gonna go on a city trip to Paris with my girlfriend. As a birthday present I have reserved a table in the Eiffel Tower Restaurant for us two. I found a printed reservation confirmation too boring and I didn't want to write a card either, so I decided to give her a show that was a little bit like the lightshow of the real Eiffel Tower. So here is a little guide for all who might want to do something similar.

Step 1: What Do You Need?

Here is a list of things I used or things I should have used. I had no time to order some of the parts I should have used because they either had too long delivery times and reasonable prices or reasonable delivery times and outrageous prices , so I just improvised with the parts I had lying around.

  • Soldering iron, wire & flux
  • Drill (or some sharp screw driver and a round file. I did it this way, because I had no drill)
  • Utility knife (or jig saw)
  • Hot glue gun and/or superglue
  • Wood glue
  • Spray glue
  • LEDs (in my case 80x 3mm LEDs)
  • Voltage regulator like LM317 (I used a resistor to drop the voltage from 5 to 3.3, but this is not how it should be done)
  • Resistors (around 90Ω)
  • Capacitors would be a smart addition
  • Shift register (I used 9x 74HC595 which I had lying around)
  • Push button
  • Piezo Buzzer
  • Stepper Motor (in my case 28BYJ-48)
  • Motor Driver (in my case ULN2003)
  • Some cardboard
  • Acryl paint
  • Grass mat (which they use for model landscapes)
  • Wires (many wires, I used over 20 meter)
  • Arduino Nano (or any other)
  • Eiffel Tower Kit (cheapest option, if you don't have access to a laser cutter/cnc mill)
  • Plywood for the base
  • hole grid board
  • 5V power supply (the usual 2.1A should be enough)

Step 2: Building and Wiring the Lower Part

I started by building the lower part of the Eiffel Tower. This part is pretty "big", you have enough room to glue and solder the LEDs after assembling and glueing the wooden parts together. I'm not gonna explain how to assemble the tower, because the kit is very intuitive and self explaining.

After I assembled the structure I laid out the LEDs to have an idea of how I should arrange them. I decided to use 10 LEDs on each side of the lower part. Before I started glueing them in place with hot glue, I soldered and glued a bare copper wire square to the bottom of the first level. This copper square is used as ground. Wire this square up with 2 wires. One pointing upwards to the middle part of the Eiffel Tower and one to the ground, which will be in the base.

The last step is to glue the LEDs in place and connect their cathode (ground) to the copper square and the anode (power) to round about 15 cm long wire (which was too short). You should use double the length or more. These wires will be connected to the shift registers later .

Step 3: Building and Wiring the Middle Part

The middle part is too narrow to glue and solder the LEDs after assembling, so you have to glue them in place and wire them before assembling the wooden parts. I used bare copper wire in each corner as ground in this part. The middle part gets 4 LEDs on each side. You need to drill a hole in the second level, to stick wire through it, like in the first picture. The picture has notes, so you can see the function of each wire.

After assembling the middle part you can stick all wires through the hole of the first level and the glue the wood together. You now have to wire the shift registers to the LED wires and the other wires (ground, power, latch, data, clock). This will be explained in the step "Wiring". You can stuff all wires and shift registers into the first floor. I later coverd them up with some cardboard (glued in place with hot glue).

Step 4: Building and Wiring the Top Part

The top part has a similar solder & assembly order like the middle part with just one additional step. You need to paint the inside of the wooden parts before assembling them, so you don't have trouble afterwards because this part is even narrower than the middle part. After wiring up the shift registers, stuff everything into the second level and glue the top part to the already combined middle and lower part.

The Eiffel Tower is now complete and can be painted.

Step 5: The Base

The last building step is the base. It is just a simple rectangular box glued together from 6 plywood panels. The top plywood needs 5 holes. 4 for the cables that extend from the lower corners of the Eiffel Tower (see first picture in "Building and wiring the lower part") and 1 for the "stage".

Assemble the sides and the top of the box with wood glue. The bottom plate will be glued after everything else is done.

The stage consists of 2 small pieces of wood, which are glued at an angle of 90 degrees and fit through the hole. I added two LEDs to the stage. Lay the stage in place, fix it with some tape and then cover the whole top side of the box with spray glue. Now stick the grass mat on it. Let it cure and after some time you can cut the grass mat to reopen the holes for the wires and 3 sides of the stage (front, left & right). I needed to cut all 4 sides because I made a mistake and needed to shorten the stage. Hot glue the stepper on the end of the stage (see second image). I twisted and soldered an thick copper wire to the stepper which push the stage up.

Drill a hole on the left, right or back to place the pushbutton.

Solder all wires to the shift registers as shown in the next step.

Glue the Arduino in place and find a place for the piezo buzzer. If you don't want to hardwire the power supply you can do it like me and place an female micro usb port to the box.

Last but not least build some mini chairs and a table out of cardboard and glue it on the stage.

Step 6: Wiring

The (amateurish) schematic in the picture shows how to wire the daisy chained shift registers. Q0 to Q7 are the outputs where you have to solder your LED wires to.

The Arduino pins are connected as following:

D2 <--> piezo buzzer (resistor is recommended)
D3 <--> shift register data
D4 <--> shift register latch
D5 <--> shift register clock
D6 <--> stage LED (resistor is mandatory)
D8 <--> stepper driver in 1
D9 <--> stepper driver in 2
D10 <--> stepper driver in 3
D11 <--> stepper driver in 4
D12 <--> push button (resistor is mandatory)

Step 7: Arduino Code

First of all credits to following people, of whom i have code snippets and therefore don't have to program everything myself:

Stepper code

Shift register code

Midi to Arduino

You can find my code on GitHub.

The first code helps to locate the LEDs. Push the button and one LED will light up. The serial output will tell you which register and which register pin is connected to this LED. Push the button multiple times to cycle through all LEDs. I have wrote down the positions to a badly drawn sketch of the EIffel Tower, like seen in the picture. Red numbers stands for the register number and the black ones for the output pin.

//Pin connected to ST_CP of 74HC595
int latchPin = 4; //Pin connected to SH_CP of 74HC595 int clockPin = 5; ////Pin connected to DS of 74HC595 int dataPin = 3; int btnpin = 12;

int arr[] = {1, 2, 4, 8, 16, 32, 64, 128};

void setup() { //set pins to output because they are addressed in the main loop pinMode(latchPin, OUTPUT); pinMode(btnpin, INPUT); Serial.begin(9600); }

int buttonState = 0; int countbtn = 0; int posshift = 1;

void loop() {

buttonState = digitalRead(btnpin); if(buttonState == HIGH){ Serial.print(countbtn); Serial.print(" "); Serial.print(posshift); Serial.print(" "); Serial.print(arr[countbtn]); Serial.println(""); digitalWrite(latchPin, 0); for (int j = 0; j < 10; j++) { if(posshift == j){ shiftOut(dataPin, clockPin, arr[countbtn]); }else{ shiftOut(dataPin, clockPin, 0); } } digitalWrite(latchPin, 1); delay(300); countbtn++; if(countbtn == 8){ countbtn = 0; posshift++; } } }

// the heart of the program void shiftOut(int myDataPin, int myClockPin, byte myDataOut) { // This shifts 8 bits out MSB first, //on the rising edge of the clock, //clock idles low

//internal function setup int i = 0; int pinState; pinMode(myClockPin, OUTPUT); pinMode(myDataPin, OUTPUT);

//clear everything out just in case to //prepare shift register for bit shifting digitalWrite(myDataPin, 0); digitalWrite(myClockPin, 0);

//for each bit in the byte myDataOut� //NOTICE THAT WE ARE COUNTING DOWN in our for loop //This means that %00000001 or "1" will go through such //that it will be pin Q0 that lights. for (i = 7; i >= 0; i--) { digitalWrite(myClockPin, 0);

//if the value passed to myDataOut and a bitmask result // true then... so if we are at i=6 and our value is // %11010100 it would the code compares it to %01000000 // and proceeds to set pinState to 1. if ( myDataOut & (1 << i) ) { pinState = 1; } else { pinState = 0; }

//Sets the pin to HIGH or LOW depending on pinState digitalWrite(myDataPin, pinState); //register shifts bits on upstroke of clock pin digitalWrite(myClockPin, 1); //zero the data pin after shift to prevent bleed through digitalWrite(myDataPin, 0); }

//stop shifting digitalWrite(myClockPin, 0); }

//blinks the whole register based on the number of times you want to //blink "n" and the pause between them "d" //starts with a moment of darkness to make sure the first blink //has its full visual effect. void blinkAll_2Bytes(int n, int d) { digitalWrite(latchPin, 0); shiftOut(dataPin, clockPin, 0); shiftOut(dataPin, clockPin, 0); digitalWrite(latchPin, 1); delay(200); for (int x = 0; x < n; x++) { digitalWrite(latchPin, 0); shiftOut(dataPin, clockPin, 255); shiftOut(dataPin, clockPin, 255); digitalWrite(latchPin, 1); delay(d); digitalWrite(latchPin, 0); shiftOut(dataPin, clockPin, 0); shiftOut(dataPin, clockPin, 0); digitalWrite(latchPin, 1); delay(d); } }

The second code is the main code, which will play the music, control the LEDs and drive the stepper. Alter the Shift1 to Shift9 Arrays to set up your own led light pattern. You need one LED setting for each note of the melody. My pattern didn't work as planned, because the first shift register suddenly had no output at the Q7S pin and I had to bridge it due to lack of spare parts. One button push starts the show, the second push will reset everything.

#include <avr/pgmspace.h>

static const uint8_t tonePin = 2; //Buzzer const int dataPin = 3; //74hc595 Data const int latchPin = 4; //74hc595 Latch const int clockPin = 5; //74hc595 CLK const int ledStage = 6; //Stage light const int motorPin1 = 8; //Motor In 1 const int motorPin2 = 9; //Motor In 2 const int motorPin3 = 10; //Motor In 3 const int motorPin4 = 11; //Motor In 4 const int btn = 12; //Button

// Octave 4 Note Codes #define NOTE_C_4(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01000000) #define NOTE_CS_4(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01000001) #define NOTE_D_4(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01000010) #define NOTE_DS_4(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01000011) #define NOTE_E_4(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01000100) #define NOTE_F_4(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01000101) #define NOTE_FS_4(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01000110) #define NOTE_G_4(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01000111) #define NOTE_GS_4(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01001000) #define NOTE_A_4(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01001001) #define NOTE_AS_4(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01001010) #define NOTE_B_4(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01001011)

// Octave 5 Note Codes #define NOTE_C_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01010000) #define NOTE_CS_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01010001) #define NOTE_D_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01010010) #define NOTE_DS_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01010011) #define NOTE_E_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01010100) #define NOTE_F_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01010101) #define NOTE_FS_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01010110) #define NOTE_G_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01010111) #define NOTE_GS_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01011000) #define NOTE_A_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01011001) #define NOTE_AS_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01011010) #define NOTE_B_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01011011)

// Octave 6 Note Codes #define NOTE_C_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01100000) #define NOTE_CS_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01100001) #define NOTE_D_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01100010) #define NOTE_DS_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01100011) #define NOTE_E_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01100100) #define NOTE_F_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01100101) #define NOTE_FS_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01100110) #define NOTE_G_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01100111) #define NOTE_GS_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01101000) #define NOTE_A_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01101001) #define NOTE_AS_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01101010) #define NOTE_B_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01101011)

#define NOTE_SILENT(DURATION) ((((uint16_t)DURATION)<<8) | 0b00001111) #define NUMBER_OF_MELODIES 1

// Track 0 - Ciao Bella static const uint16_t Melody0[] PROGMEM = { NOTE_SILENT( 255 ), NOTE_SILENT( 255 ), NOTE_SILENT( 255 ), NOTE_SILENT( 255 ), NOTE_G_4( 215 ), NOTE_SILENT( 25 ), NOTE_C_5( 170 ), NOTE_SILENT( 70 ), NOTE_D_5( 195 ), NOTE_SILENT( 45 ), NOTE_DS_5( 205 ), NOTE_SILENT( 35 ), NOTE_C_5( 255 ), NOTE_C_5( 255 ), NOTE_C_5( 255 ), NOTE_C_5( 120 ), NOTE_SILENT( 75 ), NOTE_G_4( 240 ), NOTE_C_5( 175 ), NOTE_SILENT( 65 ), NOTE_D_5( 200 ), NOTE_SILENT( 40 ), NOTE_DS_5( 190 ), NOTE_SILENT( 50 ), NOTE_C_5( 255 ), NOTE_C_5( 255 ), NOTE_C_5( 255 ), NOTE_C_5( 175 ), NOTE_SILENT( 20 ), NOTE_G_4( 240 ), NOTE_C_5( 10 ), NOTE_SILENT( 230 ), NOTE_D_5( 200 ), NOTE_SILENT( 40 ), NOTE_DS_5( 255 ), NOTE_DS_5( 175 ), NOTE_SILENT( 50 ), NOTE_D_5( 170 ), NOTE_SILENT( 70 ), NOTE_C_5( 165 ), NOTE_SILENT( 75 ), NOTE_DS_5( 255 ), NOTE_DS_5( 200 ), NOTE_SILENT( 25 ), NOTE_D_5( 190 ), NOTE_SILENT( 50 ), NOTE_C_5( 220 ), NOTE_SILENT( 20 ), NOTE_G_5( 255 ), NOTE_G_5( 175 ), NOTE_SILENT( 50 ), NOTE_G_5( 255 ), NOTE_G_5( 125 ), NOTE_SILENT( 100 ), NOTE_G_5( 155 ), NOTE_SILENT( 85 ), NOTE_G_5( 160 ), NOTE_SILENT( 80 ), NOTE_F_5( 195 ), NOTE_SILENT( 45 ), NOTE_G_5( 125 ), NOTE_SILENT( 115 ), NOTE_GS_5( 190 ), NOTE_SILENT( 50 ), NOTE_C_6( 255 ), NOTE_C_6( 255 ), NOTE_C_6( 255 ), NOTE_C_6( 155 ), NOTE_SILENT( 40 ), NOTE_C_6( 150 ), NOTE_SILENT( 90 ), NOTE_AS_5( 145 ), NOTE_SILENT( 95 ), NOTE_GS_5( 120 ), NOTE_SILENT( 120 ), NOTE_C_6( 145 ), NOTE_SILENT( 95 ), NOTE_G_5( 255 ), NOTE_G_5( 255 ), NOTE_G_5( 160 ), NOTE_SILENT( 255 ), NOTE_SILENT( 80 ), NOTE_DS_5( 120 ), NOTE_SILENT( 75 ), NOTE_DS_5( 168 ), NOTE_SILENT( 72 ), NOTE_DS_5( 155 ), NOTE_SILENT( 85 ), NOTE_D_5( 255 ), NOTE_D_5( 190 ), NOTE_SILENT( 35 ), NOTE_G_5( 255 ), NOTE_G_5( 160 ), NOTE_SILENT( 65 ), NOTE_DS_5( 255 ), NOTE_DS_5( 225 ), NOTE_D_5( 5 ), NOTE_SILENT( 255 ), NOTE_SILENT( 220 ), NOTE_C_5( 255 ), NOTE_C_5( 255 ), NOTE_C_5( 255 ), NOTE_C_5( 190 ), NOTE_SILENT( 245 ), }; static const uint16_t Melody0_Length = sizeof( Melody0 ) / sizeof(uint16_t);

// Track 1 - All I want for christmas static const uint16_t Melody1[] PROGMEM = { NOTE_G_4( 120 ), NOTE_B_4( 120 ), NOTE_D_5( 120 ), NOTE_FS_5( 60 ), NOTE_G_5( 120 ), NOTE_FS_5( 180 ), NOTE_E_5( 60 ), NOTE_D_5( 180 ), NOTE_A_5( 120 ), NOTE_G_5( 180 ), NOTE_FS_5( 120 ), NOTE_G_5( 120 ), NOTE_FS_5( 120 ), NOTE_E_5( 60 ), NOTE_D_5( 240 ), NOTE_SILENT( 120 ), NOTE_E_5( 120 ), NOTE_G_5( 120 ), NOTE_A_5( 60 ), NOTE_B_5( 120 ), NOTE_A_5( 120 ), NOTE_G_5( 120 ), NOTE_E_5( 180 ), NOTE_C_5( 120 ), NOTE_DS_5( 60 ), NOTE_G_5( 180 ), NOTE_A_5( 60 ), NOTE_AS_5( 120 ), NOTE_A_5( 120 ), NOTE_F_5( 120 ), NOTE_DS_5( 180 ), NOTE_G_4( 120 ), NOTE_B_4( 120 ), NOTE_D_5( 120 ), NOTE_FS_5( 60 ), NOTE_G_5( 120 ), NOTE_FS_5( 180 ), NOTE_E_5( 60 ), NOTE_D_5( 180 ), NOTE_A_5( 120 ), NOTE_G_5( 180 ), NOTE_FS_5( 120 ), NOTE_G_5( 120 ), NOTE_FS_5( 120 ), NOTE_E_5( 60 ), NOTE_D_5( 240 ), NOTE_SILENT( 120 ), NOTE_E_5( 120 ), NOTE_G_5( 120 ), NOTE_A_5( 60 ), NOTE_B_5( 120 ), NOTE_A_5( 120 ), NOTE_G_5( 120 ), NOTE_E_5( 180 ), NOTE_C_5( 120 ), NOTE_DS_5( 60 ), NOTE_G_5( 180 ), NOTE_A_5( 60 ), NOTE_AS_5( 120 ), NOTE_A_5( 120 ), NOTE_F_5( 120 ), NOTE_DS_5( 180 ), NOTE_G_5( 120 ), NOTE_A_5( 120 ), NOTE_FS_5( 120 ), NOTE_G_5( 60 ), NOTE_E_5( 120 ), NOTE_FS_5( 120 ), NOTE_DS_5( 255 ), NOTE_DS_5( 45 ), NOTE_G_5( 120 ), NOTE_A_5( 120 ), NOTE_FS_5( 120 ), NOTE_G_5( 60 ), NOTE_E_5( 120 ), NOTE_FS_5( 120 ), NOTE_DS_5( 255 ), NOTE_DS_5( 45 ), NOTE_D_5( 120 ), NOTE_E_5( 120 ), NOTE_G_5( 60 ), NOTE_D_6( 120 ), NOTE_C_6( 120 ), NOTE_D_6( 60 ), NOTE_C_6( 120 ), NOTE_SILENT( 240 ), NOTE_B_5( 120 ), NOTE_A_5( 120 ), NOTE_G_5( 120 ), NOTE_E_5( 120 ), NOTE_DS_5( 120 ), NOTE_A_5( 240 ), NOTE_B_5( 120 ), NOTE_A_5( 60 ), NOTE_G_5( 255 ), NOTE_G_5( 255 ), NOTE_G_5( 30 ), NOTE_SILENT( 255 ), NOTE_SILENT( 105 ), }; static const uint16_t Melody1_Length = sizeof( Melody1 ) / sizeof(uint16_t);

static const uint16_t Shift1[] PROGMEM = { 0, 0, 0, 0, 240, 240, 240, 240, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 240, 240, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,

240, 15, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 157, 157, 255, 0, 0, 0, 0, 0, 0, 0, 0, 240, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 240, 15, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 157, 157, 255, 0, 0, 0, 0, 0, 0, 0, 0, 240, 0, 0, 0, 0, 15, 0, 0, 0, }; static const uint16_t Shift2[] PROGMEM = { 240, 240, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 240, 240, 240, 255, 255, 255, 255, 0, 0, 0, 0,

0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 32, 32, 47, 255, 0, 0, 0, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 32, 32, 47, 255, 0, 0, 0, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static const uint16_t Shift3[] PROGMEM = { 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 240, 240, 240, 240, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 240, 240, 240, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,

0, 15, 0, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 247, 7, 7, 15, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 3, 0, 0, 192, 0, 48, 0, 0, 0, 15, 0, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 247, 7, 7, 15, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 3, 0, 0, }; static const uint16_t Shift4[] PROGMEM = { 15, 15, 15, 15, 240, 240, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 240, 240, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,

240, 0, 0, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 80, 80, 95, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 3, 0, 12, 0, 0, 48, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 0, 0, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 80, 80, 95, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 3, 0, 12, 0, 0, 48, 0, 192, 0, 0, 0, 0, 0, 0, 0, }; static const uint16_t Shift5[] PROGMEM = { 112, 112, 112, 112, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 127, 127, 127, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 127, 127, 127, 112, 112, 112, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 127, 127, 127, 0, 0, 0, 0,

15, 0, 0, 127, 0, 0, 0, 0, 127, 0, 48, 63, 63, 63, 63, 63, 63, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0, 0, 0, 0, 64, 48, 5, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 127, 0, 0, 0, 0, 127, 0, 48, 63, 63, 63, 63, 63, 63, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0, 0, 0, 0, 64, 48, 5, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static const uint16_t Shift6[] PROGMEM = { 167, 167, 167, 167, 0, 0, 0, 0, 0, 0, 0, 0, 80, 80, 80, 80, 247, 247, 247, 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 247, 247, 247, 247, 167, 167, 167, 167, 0, 0, 0, 0, 0, 0, 0, 0, 80, 80, 80, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 247, 247, 247, 247, 0, 0, 0, 0,

0, 0, 0, 247, 0, 0, 0, 0, 247, 1, 7, 7, 7, 7, 7, 7, 87, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 0, 1, 32, 128, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 64, 0, 0, 0, 0, 247, 0, 0, 0, 0, 247, 1, 7, 7, 7, 7, 7, 7, 87, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 0, 1, 32, 128, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static const uint16_t Shift7[] PROGMEM = { 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 252, 252, 252, 252, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 252, 252, 252, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,

1, 0, 0, 255, 0, 0, 0, 0, 255, 2, 2, 2, 2, 2, 2, 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 224, 8, 16, 4, 0, 1, 0, 0, 255, 0, 0, 0, 0, 255, 2, 2, 2, 2, 2, 2, 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; static const uint16_t Shift8[] PROGMEM = { 0, 0, 0, 0, 0, 0, 0, 0, 235, 235, 235, 235, 20, 20, 20, 20, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 235, 235, 235, 235, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,

0, 235, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 73, 125, 125, 125, 125, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 168, 64, 20, 0, 0, 0, 0, 0, 0, 235, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 73, 125, 125, 125, 125, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 168, 64, 20, }; static const uint16_t Shift9[] PROGMEM = { 0, 0, 0, 0, 39, 39, 39, 39, 216, 216, 216, 216, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 216, 216, 216, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,

39, 216, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 37, 237, 253, 253, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 33, 72, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 216, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 37, 237, 253, 253, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 33, 72, 144, 0, 0, 0, 0, };

void setup() { Serial.begin(9600); Serial.println(Melody0_Length); pinMode(latchPin, OUTPUT); pinMode(ledStage, OUTPUT); pinMode(btn, INPUT); pinMode(motorPin1, OUTPUT); pinMode(motorPin2, OUTPUT); pinMode(motorPin3, OUTPUT); pinMode(motorPin4, OUTPUT);

digitalWrite(latchPin, 0); shiftOut(dataPin, clockPin, 255); shiftOut(dataPin, clockPin, 255); shiftOut(dataPin, clockPin, 255); shiftOut(dataPin, clockPin, 255); shiftOut(dataPin, clockPin, 255); shiftOut(dataPin, clockPin, 255); shiftOut(dataPin, clockPin, 255); shiftOut(dataPin, clockPin, 255); shiftOut(dataPin, clockPin, 255); digitalWrite(latchPin, 1); }

int presscount = 0; int buttonState = 0;

void loop() { buttonState = digitalRead(btn); if (buttonState == HIGH) { delay(1000); presscount++;

if (presscount == 1) { startmusic(); digitalWrite(ledStage, HIGH); stageup(); digitalWrite(latchPin, 0); shiftOut(dataPin, clockPin, 255); shiftOut(dataPin, clockPin, 255); shiftOut(dataPin, clockPin, 255); shiftOut(dataPin, clockPin, 255); shiftOut(dataPin, clockPin, 255); shiftOut(dataPin, clockPin, 255); shiftOut(dataPin, clockPin, 255); shiftOut(dataPin, clockPin, 255); shiftOut(dataPin, clockPin, 255); digitalWrite(latchPin, 1); } if (presscount == 2) { stagedown(); digitalWrite(ledStage, LOW); digitalWrite(latchPin, 0); shiftOut(dataPin, clockPin, 0); shiftOut(dataPin, clockPin, 0); shiftOut(dataPin, clockPin, 0); shiftOut(dataPin, clockPin, 0); shiftOut(dataPin, clockPin, 0); shiftOut(dataPin, clockPin, 0); shiftOut(dataPin, clockPin, 0); shiftOut(dataPin, clockPin, 0); shiftOut(dataPin, clockPin, 0); digitalWrite(latchPin, 1); presscount = 0; } } }

void setled(int pos) { digitalWrite(latchPin, 0); shiftOut(dataPin, clockPin, Shift1[pos]); shiftOut(dataPin, clockPin, Shift2[pos]); shiftOut(dataPin, clockPin, Shift3[pos]); shiftOut(dataPin, clockPin, Shift4[pos]); shiftOut(dataPin, clockPin, Shift5[pos]); shiftOut(dataPin, clockPin, Shift6[pos]); shiftOut(dataPin, clockPin, Shift7[pos]); shiftOut(dataPin, clockPin, Shift8[pos]); shiftOut(dataPin, clockPin, Shift9[pos]); digitalWrite(latchPin, 1); }

void startmusic() { Serial.println("Start Music"); uint8_t melodyToPlay = 1; uint8_t tempoToPlay = 1;

// playMelody(melodyToPlay, tempoToPlay); // // delay(4000);

melodyToPlay = 2; tempoToPlay = 4;

playMelody(melodyToPlay, tempoToPlay); }

void stageup() { for(int i = 0; i < 170; i++){ stepperright(2200); } stopstage(); }

void stagedown() { for(int i = 0; i < 170; i++){ stepperleft(2200); } stopstage(); }

void stepperright(unsigned int motorSpeed) { digitalWrite(motorPin4, HIGH); digitalWrite(motorPin3, LOW); digitalWrite(motorPin2, LOW); digitalWrite(motorPin1, LOW); delayMicroseconds(motorSpeed);

digitalWrite(motorPin4, HIGH); digitalWrite(motorPin3, HIGH); digitalWrite(motorPin2, LOW); digitalWrite(motorPin1, LOW); delayMicroseconds(motorSpeed);

digitalWrite(motorPin4, LOW); digitalWrite(motorPin3, HIGH); digitalWrite(motorPin2, LOW); digitalWrite(motorPin1, LOW); delayMicroseconds(motorSpeed);

digitalWrite(motorPin4, LOW); digitalWrite(motorPin3, HIGH); digitalWrite(motorPin2, HIGH); digitalWrite(motorPin1, LOW); delayMicroseconds(motorSpeed);

digitalWrite(motorPin4, LOW); digitalWrite(motorPin3, LOW); digitalWrite(motorPin2, HIGH); digitalWrite(motorPin1, LOW); delayMicroseconds(motorSpeed);

digitalWrite(motorPin4, LOW); digitalWrite(motorPin3, LOW); digitalWrite(motorPin2, HIGH); digitalWrite(motorPin1, HIGH); delayMicroseconds(motorSpeed);

digitalWrite(motorPin4, LOW); digitalWrite(motorPin3, LOW); digitalWrite(motorPin2, LOW); digitalWrite(motorPin1, HIGH); delayMicroseconds(motorSpeed);

digitalWrite(motorPin4, HIGH); digitalWrite(motorPin3, LOW); digitalWrite(motorPin2, LOW); digitalWrite(motorPin1, HIGH); delayMicroseconds(motorSpeed); }

void stepperleft(unsigned int motorSpeed) { digitalWrite(motorPin1, HIGH); digitalWrite(motorPin2, LOW); digitalWrite(motorPin3, LOW); digitalWrite(motorPin4, LOW); delayMicroseconds(motorSpeed);

digitalWrite(motorPin1, HIGH); digitalWrite(motorPin2, HIGH); digitalWrite(motorPin3, LOW); digitalWrite(motorPin4, LOW); delayMicroseconds(motorSpeed);

digitalWrite(motorPin1, LOW); digitalWrite(motorPin2, HIGH); digitalWrite(motorPin3, LOW); digitalWrite(motorPin4, LOW); delayMicroseconds(motorSpeed);

digitalWrite(motorPin1, LOW); digitalWrite(motorPin2, HIGH); digitalWrite(motorPin3, HIGH); digitalWrite(motorPin4, LOW); delayMicroseconds(motorSpeed);

digitalWrite(motorPin1, LOW); digitalWrite(motorPin2, LOW); digitalWrite(motorPin3, HIGH); digitalWrite(motorPin4, LOW); delayMicroseconds(motorSpeed);

digitalWrite(motorPin1, LOW); digitalWrite(motorPin2, LOW); digitalWrite(motorPin3, HIGH); digitalWrite(motorPin4, HIGH); delayMicroseconds(motorSpeed);

digitalWrite(motorPin1, LOW); digitalWrite(motorPin2, LOW); digitalWrite(motorPin3, LOW); digitalWrite(motorPin4, HIGH); delayMicroseconds(motorSpeed);

digitalWrite(motorPin1, HIGH); digitalWrite(motorPin2, LOW); digitalWrite(motorPin3, LOW); digitalWrite(motorPin4, HIGH); delayMicroseconds(motorSpeed); }

void stopstage() { digitalWrite(motorPin4, LOW); digitalWrite(motorPin3, LOW); digitalWrite(motorPin2, LOW); digitalWrite(motorPin1, LOW); }

void playMelody_Data(const uint16_t MelodyData[], const uint16_t MelodyLength, const uint8_t tempo, const uint16_t offset) { // 8th Octave Frequencies C8 to B8, lower octaves are calculated from this static const uint16_t Freq8[] PROGMEM = { 4186 , 4435 , 4699 , 4978 , 5274 , 5588 , 5920 , 6272 , 6645 , 7040 , 7459 , 7902 };

for (uint16_t x = 0; x < MelodyLength; x++) {

setled(x + offset); uint16_t data = pgm_read_word((uint16_t *)&MelodyData[x]); if ((data & 0xF) == 0xF) { noTone(tonePin); } else { uint16_t Freq = pgm_read_word(&Freq8[data & 0xF]) / ( 1 << (8 - (data >> 4 & 0xF)) ); tone(tonePin, Freq); }

int16_t Duration = data >> 8; while (Duration--) delay(tempo); } }

inline static void playMelody(uint8_t melodyNumber, uint8_t tempo) { Serial.println(melodyNumber); switch (melodyNumber - 1) { case 0: { playMelody_Data(Melody0, Melody0_Length, tempo, 0); Serial.println("Melody 0"); return; } case 1: { playMelody_Data(Melody1, Melody1_Length, tempo, Melody0_Length - 1); return; } } }

void shiftOut(int myDataPin, int myClockPin, byte myDataOut) { int i = 0; int pinState; pinMode(myClockPin, OUTPUT); pinMode(myDataPin, OUTPUT);

digitalWrite(myDataPin, 0); digitalWrite(myClockPin, 0);

for (i = 7; i >= 0; i--) { digitalWrite(myClockPin, 0);

if ( myDataOut & (1 << i) ) { pinState = 1; } else { pinState = 0; }

digitalWrite(myDataPin, pinState); digitalWrite(myClockPin, 1); digitalWrite(myDataPin, 0); }

digitalWrite(myClockPin, 0); }

Step 8: Mapping Tool

I also wrote a little tool in C# to easily create light patterns. Unfortunately, the code is very specific to my LED layout and my wiring. I will provide you the code on my GitHub, but it would be beyond the scope to explain how to customize the tool. For people with some programming knowledge it should be no problem to understand and adapt the tool.