Introduction: Infinity Mirror With LCD and IR Sensor
This project is going to show you how you can make an Infinity Mirror. The basic concept is that the LEDs that are on the mirror create light which bounces from the back mirror to the front mirror where some light escapes so we can see inside and some are reflected back to the back mirror and produce the infinite effect. This is based off the Infinity Mirror With Ultra Sonic Sensors Instructable by Hack Sioux Falls and How to Connect I2C Lcd Display to Arduino Uno by JunezRiyaz.
Supplies
1. A Shadow Box ($50)
2. 1-way Mirror Film ($17)
3. An RGB LED light strip ($30)
4. Arduino ($35)
5. A mirror ($10)
Optional:
1. IR Sensor and Remote ($20)
2. An LCD ($20)
3. 7 Female to Male Connectors
4. 3 Male to Male Connectors
Tools:
1. Soldering Iron
2. Wire Cutter and Stripper
3. Something to cut the mirror with
*Note all prices in CAD and sizes will depend on the box that you use.
Step 1: Installing the Mirror and the Mirror Film
The first thing to do is to install the mirror that is going to be in the back of the shadow box. You want to either get one that is the right size or get one that's larger and then cut it down. But for this, you have to use a mirror and not the film because we want all of the light to be reflected back, the mirror film also can take on the pattern of the material it is on if it's not glass.
When you are installing the mirror make sure to secure it in the shadow box, I would recommend using small pieces of wood and nails to make sure the mirror doesn't move. I didn't have a mirror in the time I had to complete this project so I used the film (You can see in the pictures). The concept works out but not very well and gives an angled and chaotic look that in my opinion does not look good.
Every mirror film is going to be a little different but the one that I got said to clean the glass that we are putting the film on then to use tape to take off the protective film that is on one side and using soapy water in a spray bottle put the film on the glass. Then use a credit card or squeegee to get rid of any air bubbles and excess water. I once again didn't have glass but it still works the same. Then leave the glass to dry overnight and it should be done.
Step 2: Setting Up the LED Strip
For the LEDs I just used the included double sided tape to stick it to the frame and made a hole for the wire to come out. At each corner, I soldered leads to connect to another strip to keep the box shape without rounded edges. Make sure to only cut the strips at points where there are contacts that would allow such to happen and do not switch the direction the LEDs connect to each other known as reversing the polarity. The strip that I got had arrows to make sure this doesn't happen so make sure to check they are all pointing in the same direction.
I made a small hole to put the cable through to the outside of the box, the LEDs that I had were 3 Pin addressable LEDs so I had power, ground and data connectors. I used Digital Pin 3 for the data connector, make sure to test your LED strips to make sure they work before sticking them to the frame and make sure to use only the recommended voltage for my one that is 5 - 6 V.
Step 3: Mounting the IR Sensor and the LCD (Optional)
I chose to use an IR sensor and remote to change the lighting effects and to use and an LCD to show the user the options they have for the effects.
To mount the LCD and IR sensor I would use a hot glue gun and stick them to the top of the frame and wired everything to the back but I didn't want them to be fixed because I might have to take them off after the project is done.
To connect up the LCD and IR Receiver I used Male to Female Connectors which plug into the Arduino for data as well as ground and power. To wire everything up I used pin 12 with a 2 kΩ resistor on the data pin for the IR sensor, Analog Pin A4 for the SDA of the LCD adn Analog Pin 5 for the SCL of the LCD.
Step 4: Code
For this project, I use I few libraries that you would have to download to use. I am going to put them here, bear in mind that I am on windows so that may have an effect if you are on Mac or Linux.
You are going to have to change the number of pixels depending on the number of LEDs you have. I also built in a Serial Monitor interface so you can interface that way with it if you are not using an IR sensor.
// Libraries #include Adafruit_NeoPixel.h> #include <IRremote.h> #include <LiquidCrystal_I2C.h> #include <Wire.h>// Pins and Constant values // Pins and Constant values<br>const int RECV_PIN = 12; IRrecv irrecv(RECV_PIN); decode_results results; unsigned long key_value = 0; const int NUMPIXELS = 27;</p><p>Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS,3); LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);// Variables int IRNumb; int IRNumb2; int rainbow = 0; uint32_t rainbowColor = pixels.Color(0, 0, 0); char input = 0; char input2 = 0; int r; int g; int b; int breakCondition = 0; void setup() { // Initallizes elements that do not need to be repeated. lcd.begin(16,2);//Defining 16 columns and 2 rows of lcd display lcd.backlight();//To turn on the backlight Serial.begin(9600); pixels.begin(); irrecv.enableIRIn(); Serial.println("Enter a 0 to turn the lights off"); // Begining Instructions Serial.println("Enter a 1 to turn the LEDs to white"); Serial.println("Enter a 2 to have the LEDs cycle through different colours"); Serial.println("Enter a 3 to customize the LED colours manually"); } void loop() { IRNumb = 10; IRScan(); mainOptions(); if (Serial.available() > 0) { // Checks if a character is available? input = (char)Serial.read(); } // Chooses the different options if (input == '1' || IRNumb == 1){ rainbowColor = colourChange(255,255,255); // White setAll(rainbowColor); // Updats the LEDs } else if (input == '0' || IRNumb == 0){ rainbowColor = colourChange(0,0,0); // Off setAll(rainbowColor); // Updats the LEDs } else if (input == '2' || IRNumb == 2){ breakCondition = 0; r = 0; g = 0; b = 0;</p><p> Serial.println("Enter anything that is not a 2 to exit this effect, then any of the preveous options for a new effect."); lcd.clear(); //Clean the screen lcd.setCursor(0,0); lcd.print("Exit using any-"); lcd.setCursor(0,1); lcd.print("numb that != 2"); for(int i = 0; i < 255; i++ ) { // First effect strts input = (char)Serial.read(); IRScan(); if ((input != '2' && input != ' ' && input >= 48 && input <= 57) || breakCondition == 1 || IRNumb != 2){ // Checks if the effect needs to be exited rainbowColor = colourChange(0,0,0); setAll(rainbowColor); breakCondition = 1; break; } rainbowColor = colourChange (i, 0, 0); setAll(rainbowColor); showStrip(); delay(100); } for(int i = 0; i < 255; i++ ) { // Second effect input = (char)Serial.read(); IRScan(); if ((input != '2' && input != ' ' && input >= 48 && input <= 57) || breakCondition == 1 || IRNumb != 2){ // Checks if the effect needs to be exited rainbowColor = colourChange(0,0,0); setAll(rainbowColor); breakCondition = 1; break; } g++; rainbowColor = colourChange (i, g, 0); setAll(rainbowColor); showStrip(); delay(100); } for(int g = 0; g < 255; g++ ) { // Third effect input = (char)Serial.read(); IRScan(); if ((input != '2' && input != ' ' && input >= 48 && input <= 57) || breakCondition == 1 || IRNumb != 2){ // Checks if the effect needs to be exited rainbowColor = colourChange(0,0,0); setAll(rainbowColor); breakCondition = 1; break; } r+= 2; rainbowColor = colourChange (r, g, 0); setAll(rainbowColor); showStrip(); delay(100); } for(int b = 0; b < 255; b++ ) { // Fourth effect input = (char)Serial.read(); IRScan(); if ((input != '2' && input != ' ' && input >= 48 && input <= 57) || breakCondition == 1 || IRNumb != 2){ // Checks if the effect needs to be exited rainbowColor = colourChange(0,0,0); setAll(rainbowColor); breakCondition = 1; break; } r+= 2; g+= 5; rainbowColor = colourChange (r, g, b); setAll(rainbowColor); showStrip(); delay(100); } mainOptions(); } else if (input == '3' || IRNumb == 3){ r = 0; g = 0; b = 0;</p><p> Serial.println("Enter n to exit"); Serial.println("Use w and s to increase or decrease the red colour"); Serial.println("Use r and f to increase or decrease the green colour"); Serial.println("Use y and h to increase or decrease the blue colour"); lcd.clear();//Clean the screen lcd.setCursor(0,0); lcd.print("exit:0,4/7:+/-red"); lcd.setCursor(0,1); lcd.print("5/8:green,6/9:blue");</p><p> while (input2 != 'n' || IRNumb2 != 0){ if (Serial.available() > 0) { // is a character available? input2 = (char)Serial.read(); } //Serial.println(IRNumb2); IRNumb2 = 10; IRScan2();</p><p> if (IRNumb2 == 0){ break; } if (input2 == 'w' || IRNumb2 == 4){ r+= 2; rainbowColor = colourChange (r, g, b); setAll(rainbowColor); showStrip(); } else if (input2 == 's' || IRNumb2 == 7){ r-= 2; rainbowColor = colourChange (r, g, b); setAll(rainbowColor); showStrip(); } else if (input2 == 'r' || IRNumb2 == 5){ g+= 2; rainbowColor = colourChange (r, g, b); setAll(rainbowColor); showStrip(); } else if (input2 == 'f' || IRNumb2 == 8){ g-= 2; rainbowColor = colourChange (r, g, b); setAll(rainbowColor); showStrip(); } </p><p> else if (input2 == 'y' || IRNumb2 == 6){ b+= 2; rainbowColor = colourChange (r, g, b); setAll(rainbowColor); showStrip(); } </p><p> else if (input2 == 'h' || IRNumb2 == 9){ b-= 2; rainbowColor = colourChange (r, g, b); setAll(rainbowColor); showStrip(); } } mainOptions(); } setAll(rainbowColor); showStrip(); } void showStrip() { pixels.show(); } void setAll(uint32_t color) { for(int i = 0; i < NUMPIXELS; i++ ) { pixels.setPixelColor(i, color); } } // Input a value 0 to 255 to get a color value. uint32_t colourChange (int r, int g, int b) { return pixels.Color(r, g, b); } void mainOptions (){ // Sjows the main effect options lcd.clear();//Clean the screen lcd.setCursor(0,0); lcd.print("0 For Lights off"); lcd.setCursor(0,1); lcd.print("1: White Light"); delay(1000); lcd.clear();//Clean the screen lcd.setCursor(0,0); lcd.print("2: Colour effect"); lcd.setCursor(0,1); lcd.print("3: Manual"); delay(1000); } void IRScan (){ if (irrecv.decode(&results)){</p><p> if (results.value == 0XFFFFFFFF) results.value = key_value; else{ //Serial.println(results.value, HEX); if (results.value == 0xFF6897 || results.value == 0xC101E57B){ IRNumb = 0; //Serial.println("0"); } else if (results.value == 0x9716BE3F || results.value == 0xFF30CF){ IRNumb = 1; //Serial.println("1"); } else if (results.value == 0x3D9AE3F7 || results.value == 0xFF18E7){ IRNumb = 2; //Serial.println("2"); } else if (results.value == 0xFF7A85 || results.value == 0x6182021B){ IRNumb = 3; // Serial.println("3"); } else if (results.value == 0x8C22657B || results.value == 0xFF10EF){ IRNumb = 4; //Serial.println("4"); } else if (results.value == 0xFF38C7 || results.value == 0x488F3CBB){ IRNumb = 5; // Serial.println("5"); } else if (results.value == 0x449E79F || results.value == 0xFF5AA5){ IRNumb = 6; // Serial.println("6"); } else if (results.value == 0xFF42BD || results.value == 0x32C6FDF7){ IRNumb = 7; //Serial.println("7"); } else if (results.value == 0x1BC0157B || results.value ==0xFF4AB5){ IRNumb = 8; //Serial.println("8"); } else if (results.value == 0xFF52AD || results.value == 0x3EC3FC1B){ IRNumb = 9; // Serial.println("9"); } } key_value = results.value; irrecv.resume(); } } void IRScan2 (){ if (irrecv.decode(&results)){</p><p> if (results.value == 0XFFFFFFFF) results.value = key_value; else{ // Serial.println(results.value, HEX); if (results.value == 0xFF6897 || results.value == 0xC101E57B){ IRNumb2 = 0; //Serial.println("0"); } else if (results.value == 0x9716BE3F || results.value == 0xFF30CF){ IRNumb2 = 1; //Serial.println("1"); } else if (results.value == 0x3D9AE3F7 || results.value == 0xFF18E7){ IRNumb2 = 2; //Serial.println("2"); } else if (results.value == 0xFF7A85 || results.value == 0x6182021B){ IRNumb2 = 3; // Serial.println("3"); } else if (results.value == 0x8C22657B || results.value == 0xFF10EF){ IRNumb2 = 4; //Serial.println("4"); } else if (results.value == 0xFF38C7 || results.value == 0x488F3CBB){ IRNumb2 = 5; // Serial.println("5"); } else if (results.value == 0x449E79F || results.value == 0xFF5AA5){ IRNumb2 = 6; // Serial.println("6"); } else if (results.value == 0xFF42BD || results.value == 0x32C6FDF7){ IRNumb2 = 7; //Serial.println("7"); } else if (results.value == 0x1BC0157B || results.value ==0xFF4AB5){ IRNumb2 = 8; //Serial.println("8"); } else if (results.value == 0xFF52AD || results.value == 0x3EC3FC1B){ IRNumb2 = 9; // Serial.println("9"); } } key_value = results.value; irrecv.resume(); } }
Step 5: Mounting
To mount the Arduino to the frame I would just glue it and the breadboard that I used to the back of the frame and try to cover/hide the cables to make it look good.