/* Trying to sync with the following song... https://youtu.be/okyO1Fze7Jo https://musescore.com/user/25698851/scores/4787304 I own the .mp3 of this song! In the YouTube video, there are 8 LED sections, whereas I have 4. Using an IR receiver is a great way to control the Arduino. Mine is model AX-1838HS, which picks up signals from my TV remote control! Connect the appropriate pins to Arduino... GND 5V A0 (does not need to use an analog pin) If you set variable "receiver" to be true, the Arduino will wait for an IR signal before starting. To sync the song, you'll likely need a few tries, so just hit the reset button on the Arduino if you mess up the timing. Or, my code periodically checks if any IR signal has been received. If a signal has been received, it "resets" itself! Sadly, it takes about 5 seconds until the code starts recording IR signals, then it can take a while for the code to check what has been recorded. My functions beginning with "write" are basic and probably shouldn't often appear directly in loop(). My goal with these is to only have to replace these if I switch to outputting to a new device. write1 write2 write3 write4 writeAll If a non-"write" function (one that CALLS "write" functions) leaves LEDs on, I leave a comment above the function. Or, if a non-"write" function requires LEDs to be on when starting, I leave a comment above the function. Otherwise, functions assume that all LEDs are off when starting and finishing. All non-"write" functions are polite and leave the appropriate delay at the very end. All times fed to these functions are the times that the function should be finished executing. Here are the functions... blink alternate towardsCenter shrinkTowardsTop moveUpAndDown moveFromCenter moveFromCenter2 towardsCenter2 flowUpwards moveUpwards growUpwards moveUpwardsDouble fallWithStyle (c) 2018 Bradley Knockel */ // Are you using an IR receiver? bool const receiver = true; // code for IR receiver int const pin = A0; #include "IRremote.h" // https://www.arduinolibraries.info/libraries/i-rremote IRrecv irrecv(pin); // create instance of 'irrecv' decode_results results; // create instance of 'decode_results' bool wait; bool checkIR() { //Serial.print("IR checked "); //Serial.println(millis()); if (irrecv.decode(&results)) { // have we received an IR signal? //Serial.println(results.value, HEX); delay(1000); // allow user time to stop pressing button wait = true; irrecv.resume(); // receive the next value return true; } else { return false; } } // set the pin numbers! int const pin1 = 2; int const pin2 = 4; int const pin3 = 5; int const pin4 = 7; // note that pin1 is for the TOP of the tree (pin4 is bottom) void write1(bool b) { if (b) {digitalWrite(pin1, HIGH);} else {digitalWrite(pin1, LOW);} } void write2(bool b) { if (b) {digitalWrite(pin2, HIGH);} else {digitalWrite(pin2, LOW);} } void write3(bool b) { if (b) {digitalWrite(pin3, HIGH);} else {digitalWrite(pin3, LOW);} } void write4(bool b) { if (b) {digitalWrite(pin4, HIGH);} else {digitalWrite(pin4, LOW);} } void writeAll(bool b) { if (b) { digitalWrite(pin1, HIGH); digitalWrite(pin2, HIGH); digitalWrite(pin3, HIGH); digitalWrite(pin4, HIGH); } else { digitalWrite(pin1, LOW); digitalWrite(pin2, LOW); digitalWrite(pin3, LOW); digitalWrite(pin4, LOW); } } void setup() { pinMode(pin1, OUTPUT); pinMode(pin2, OUTPUT); pinMode(pin3, OUTPUT); pinMode(pin4, OUTPUT); writeAll(false); // necessary? if (receiver) { wait = true; irrecv.enableIRIn(); // start the IR receiver (uses timer2) } else {wait = false;} Serial.begin(9600); } // timing a song can be tricky // so the only good way is to make frequent calls to millis() float timee; // keeping track of time in ms float const eighth=202.3; // ms for an eighth note float a; bool const t=true; bool const f=false; void blink(float c) { a=(c-millis())/2; writeAll(t); delay(c-a-millis()); writeAll(f); delay(c-millis()); } // Does not care about previous LED state. // Leaves 2,4 on void alternate(float c) { a=(c-millis())/2; write1(t); write2(f); write3(t); write4(f); delay(c-a-millis()); write1(f); write2(t); write3(f); write4(t); delay(c-millis()); } void towardsCenter(float c) { a=(c-millis())/4; write1(t); write4(t); delay(c-3*a-millis()); write2(t); write3(t); delay(c-2*a-millis()); write1(f); write4(f); delay(c-a-millis()); write2(f); write3(f); delay(c-millis()); } void shrinkTowardsTop(float c) { a=(c-millis())/4; writeAll(t); delay(c-3*a-millis()); write4(f); delay(c-2*a-millis()); write3(f); delay(c-a-millis()); write2(f); delay(c-a/2-millis()); write1(f); delay(c-millis()); } void moveUpAndDown(float c) { a=(c-millis())/6; // awkward timing write4(t); delay(c-5*a-millis()); write4(f); write3(t); delay(c-4*a-millis()); write3(f); write2(t); delay(c-3*a-millis()); write2(f); write1(t); delay(c-2*a-millis()); write2(t); write1(f); delay(c-a-millis()); write3(t); write2(f); delay(c-millis()); write3(f); } void moveFromCenter(float c) { a=(c-millis())/5; write2(t); write3(t); delay(c-3*a-millis()); write2(f); write3(f); write1(t); write4(t); delay(c-a-millis()); write1(f); write4(f); delay(c-millis()); } void moveFromCenter2(float c) { a=(c-millis())/4; writeAll(t); delay(c-3*a-millis()); write1(f); write4(f); delay(c-2*a-millis()); write2(f); write3(f); write1(t); write4(t); delay(c-a-millis()); write1(f); write4(f); delay(c-millis()); } // leaves all LEDs on void towardsCenter2(float c) { a=(c-millis())/5; write1(t); delay(c-4*a-millis()); write4(t); delay(c-3*a-millis()); write2(t); delay(c-2*a-millis()); write3(t); delay(c-millis()); } void flowUpwards(float c) { a=(c-millis())/4; write4(t); delay(c-3*a-millis()); write4(f); write3(t); delay(c-2*a-millis()); write3(f); write2(t); delay(c-a-millis()); write2(f); write1(t); delay(c-millis()); write1(f); } // YouTube tree did this as moveFromCenter, // but I don't have enough LED sections void moveUpwards(float c) { a=(c-millis())/6; write4(t); delay(c-5*a-millis()); write4(f); write3(t); delay(c-4*a-millis()); write3(f); write2(t); delay(c-2*a-millis()); write2(f); write1(t); delay(c-millis()); write1(f); } // leaves all LEDs on void growUpwards(float c) { a=(c-millis())/4; write4(t); delay(c-3*a-millis()); write3(t); delay(c-2*a-millis()); write2(t); delay(c-a-millis()); write1(f); delay(c-millis()); } // leaves 1 and 3 lit void moveUpwardsDouble(float c) { a=(c-millis())/8; writeAll(t); delay(c-7*a-millis()); writeAll(f); write4(t); delay(c-6*a-millis()); write4(f); write3(t); delay(c-4*a-millis()); write3(f); write4(t); write2(t); delay(c-2*a-millis()); write2(f); write4(f); write1(t); write3(t); delay(c-millis()); } void fallWithStyle(float c) { a=(c-millis())/4; write1(t); delay(c-3*a-millis()); write1(f); write3(t); delay(c-2*a-millis()); write3(f); write2(t); delay(c-a-millis()); write2(f); write4(t); delay(c-millis()); write4(f); } void loop() { while(true) { // allows "break;" to "reset" the code while (wait) { if (irrecv.decode(&results)) { // have we received an IR signal? wait = false; //Serial.println(results.value, HEX); } } // // the following test takes 15 ms (negligible) //int j=0; //Serial.println(millis()); //for(int i=0;i<10000;i++){ // j=j+millis(); //} //Serial.println(millis()); //delay(4900); // for syncing up with YouTube video timee = millis(); towardsCenter(timee += eighth*3*8); irrecv.resume(); // receive the next IR value shrinkTowardsTop(timee += eighth*8); towardsCenter(timee += eighth*3*8); shrinkTowardsTop(timee += eighth*8); if(checkIR()){break;} moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); shrinkTowardsTop(timee += eighth*8); if(checkIR()){break;} moveUpAndDown(timee += eighth*4); // at measure 13 moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); blink(timee += eighth*2); timee += eighth;delay(timee-millis()); moveFromCenter(timee += eighth*5); if(checkIR()){break;} blink(timee += eighth*2); timee += eighth;delay(timee-millis()); moveFromCenter(timee += eighth*5); blink(timee += eighth*2); // start measure 18 timee += eighth;delay(timee-millis()); alternate(timee += eighth*2); timee += eighth*2;delay(timee-millis()); alternate(timee += eighth*2); timee += eighth;delay(timee-millis()); alternate(timee += eighth*4); write1(t);write2(f);write3(t);write4(f);timee += eighth;delay(timee-millis()); writeAll(f);timee += eighth;delay(eighth); if(checkIR()){break;} moveUpAndDown(timee += eighth*4); // start measure 20 moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); blink(timee += eighth*2); timee += eighth;delay(timee-millis()); moveFromCenter(timee += eighth*5); if(checkIR()){break;} moveUpAndDown(timee += eighth*4); // start measure 24 moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); blink(timee += eighth*2); timee += eighth;delay(timee-millis()); moveFromCenter(timee += eighth*5); if(checkIR()){break;} blink(timee += eighth*2); //start measure 28 timee += eighth;delay(timee-millis()); moveFromCenter(timee += eighth*5); blink(timee += eighth*2); // start measure 29 timee += eighth;delay(timee-millis()); alternate(timee += eighth*2); timee += eighth*2;delay(timee-millis()); write1(t);write2(f);write3(t);write4(f);timee += eighth;delay(timee-millis()); if(checkIR()){break;} shrinkTowardsTop(timee += eighth*16); // measures 30 and 31 if(checkIR()){break;} // measures 32, 33, 34, 35 flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); if(checkIR()){break;} // 1:02 in the YouTube video timee += eighth*2;delay(timee-millis()); moveUpwards(timee += eighth*6); timee += eighth*2;delay(timee-millis()); moveUpwards(timee += eighth*6); timee += eighth*2;delay(timee-millis()); moveUpwards(timee += eighth*6); flowUpwards(timee += eighth*8); // measure 39 if(checkIR()){break;} timee += eighth*2;delay(timee-millis()); moveUpwards(timee += eighth*6); timee += eighth*2;delay(timee-millis()); moveUpwards(timee += eighth*6); timee += eighth*2;delay(timee-millis()); moveUpwards(timee += eighth*6); flowUpwards(timee += eighth*8); // measure 43 if(checkIR()){break;} blink(timee += eighth*2); timee += eighth;delay(timee-millis()); moveFromCenter(timee += eighth*5); blink(timee += eighth*2); timee += eighth;delay(timee-millis()); moveFromCenter(timee += eighth*5); blink(timee += eighth*2); // start measure 46 timee += eighth;delay(timee-millis()); alternate(timee += eighth*2); timee += eighth*2;delay(timee-millis()); alternate(timee += eighth*2); timee += eighth;delay(timee-millis()); alternate(timee += eighth*4); write1(t);write2(f);write3(t);write4(f);timee += eighth;delay(timee-millis()); writeAll(f);timee += eighth;delay(timee-millis()); if(checkIR()){break;} moveUpAndDown(timee += eighth*4); // start measure 48 moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); shrinkTowardsTop(timee += eighth*8); if(checkIR()){break;} moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); blink(timee += eighth*2); timee += eighth;delay(timee-millis()); moveFromCenter(timee += eighth*5); // end at 1:34 in YouTube if(checkIR()){break;} blink(timee += eighth*2); //start measure 56 timee += eighth;delay(timee-millis()); moveFromCenter(timee += eighth*5); blink(timee += eighth*2); // start measure 57 timee += eighth;delay(timee-millis()); towardsCenter2(timee += eighth*5); shrinkTowardsTop(timee += eighth*16); // measures 58 and 59 if(checkIR()){break;} growUpwards(timee += eighth*8); //measure 60 shrinkTowardsTop(timee += eighth*8); //measure 61 if(checkIR()){break;} for(int i=0;i<32;i++){ blink(timee += eighth*1); blink(timee += eighth*2); blink(timee += eighth*1); } if(checkIR()){break;} // at measure 78 (at 2:10 in YouTube video) blink(timee += eighth*2); timee += eighth;delay(timee-millis()); moveFromCenter(timee += eighth*5); blink(timee += eighth*2); timee += eighth;delay(timee-millis()); moveFromCenter(timee += eighth*5); blink(timee += eighth*2); // start measure 80 timee += eighth;delay(timee-millis()); alternate(timee += eighth*2); timee += eighth*2;delay(timee-millis()); alternate(timee += eighth*2); timee += eighth;delay(timee-millis()); alternate(timee += eighth*4); write1(t);write2(f);write3(t);write4(f);timee += eighth;delay(timee-millis()); writeAll(f);timee += eighth;delay(timee-millis()); if(checkIR()){break;} moveUpAndDown(timee += eighth*4); // start measure 82 moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); shrinkTowardsTop(timee += eighth*8); if(checkIR()){break;} moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); if(checkIR()){break;} moveUpwardsDouble(timee += eighth*8); // measure 89 (at 2:28 in YouTube video) write2(t);write4(t);timee += eighth*2;delay(timee-millis()); // measure 90 alternate(timee += eighth*2); alternate(timee += eighth*2); alternate(timee += eighth*2); write3(t);write1(t);timee += eighth/2;delay(timee-millis()); // start measure 91 write1(f);write2(t);write3(f);write4(t);timee += eighth/2;delay(timee-millis()); alternate(timee += eighth); alternate(timee += eighth); alternate(timee += eighth); write3(t);write1(t);timee += eighth/2;delay(timee-millis()); write1(f);write2(t);write3(f);write4(t);timee += eighth/2;delay(timee-millis()); alternate(timee += eighth); alternate(timee += eighth); alternate(timee += eighth); blink(timee += eighth*2); // start measure 92 blink(timee += eighth*2); blink(timee += eighth*2); shrinkTowardsTop(timee += eighth*2); if(checkIR()){break;} //measures 93,94,95,96,97,98,99,100 for(int i=0;i<8;i++){ moveUpAndDown(timee += eighth*4); moveUpAndDown(timee += eighth*4); if(checkIR()){break;} } // at 2:47 in YouTube video fallWithStyle(timee += eighth*4); fallWithStyle(timee += eighth*4); fallWithStyle(timee += eighth*4); fallWithStyle(timee += eighth*4); if(checkIR()){break;} for(int i=0;i<4;i++){ blink(timee += eighth); blink(timee += eighth); blink(timee += eighth); blink(timee += eighth); blink(timee += eighth); blink(timee += eighth); blink(timee += eighth); blink(timee += eighth); } if(checkIR()){break;} // at measure 107 (at 2:57 in YouTube video) shrinkTowardsTop(timee += eighth*4); moveFromCenter2(timee += eighth*4); moveUpwardsDouble(timee += eighth*4); blink(timee += eighth*2); alternate(timee += eighth*2); shrinkTowardsTop(timee += eighth*4); moveFromCenter2(timee += eighth*4); moveUpwardsDouble(timee += eighth*4); blink(timee += eighth*2); alternate(timee += eighth*2); // I guess the sheet sheet music leaves out 2 measures! if(checkIR()){break;} // measures 109 and 110 of sheet music flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); flowUpwards(timee += eighth*2); if(checkIR()){break;} // measure 111 of sheet music blink(timee += eighth*4); delay(5000); while(true){ if(checkIR()){break;} } /* */ } }