The Infinity Mirror Kit V2 is a desktop display that creates a dazzling optical illusion - a tunnel of light that tears through space!
The Infinity Mirror Kit V2 entered production in October 2018, and now ships with an Adafruit Trinket to control the lights! If you are cool enough to own the original version follow this link to find the Assembly Instructions for the Infinity Kit Version 1.
Ok, back to the latest version! Here are the guides for V2:
- Assembly Instructions
- Operation Instructions
- Tutorials - Editing Your Code
- Alternative Modes
- Going Further
Assembly
This video will guide you through assembling your Infinity Kit. If you prefer working off paper, you can Download the Infinity Mirror V2 Assembly Instructions.
Nothing beats the feeling of a completed project! Your Infinity Mirror Kit will serve as a mesmerising piece of desk-top tech with its default program. Check out the following tutorials If you'd like to learn how to upload your own code to your Infinity Kit.
Operation Instructions
The Infinity Mirror V2 comes already flashed with code to play five different animations. The potentiometer is used to change between the different modes. Some of the modes aren't just nice to look at, but they change the settings for the other modes! Here is a full explanation of the different mode types:
Off: This mode isn't truly off, but the LEDs are turned off, and all user settings are reset to the default values (brightness returns to full, colour returns to green).
Rainbow: This mode plays a rotating rainbow animation
Colour: Colour mode cycles through every colour. The colour shown when you change modes is saved for use in the other animations.
Comet: The mode a comet effect races around the mirror. The colour of the comet is set with Colour Mode
Solid: This mode is solid on, the colour is the same as whatever colour was last shown in colour mode.
Brightness: This mode slowly fades the LEDs up and down. The colour is set with colour mode, and the brightness is saved for all the other animations when you change modes.
Tutorial - Editing Your Code
The Trinket comes with this sketch already flashed to the board. If you would like to change the animations in the Infinity Kit, we'll walk you through the code to make it easy!
Installing Drivers
If you are using a Windows machine you will need to install some drivers for your Trinket to be recognized. If you are using Mac or Linux there are no drivers required!
To install the latest drivers for the Adafruit Trinket. Follow this link to find the Latest Windows Drivers for the Adafruit Trinket and How to Install.
Setting up the Arduino IDE
The next thing we need to do before reprogramming your Infinity Mirror Kit V2 is to set up the Arduino IDE. We need to add the Trinket board to the IDE so we can connect to it properly. We need to go to file>preferences and paste this link: https://adafruit.github.io/arduino-board-index/package_adafruit_index.json into the additional boards manager.
The next step is to install the Trinket board package in the board manager.
Search for "Trinket" and install the Adafruit AVR Boards Package.
Uploading Code
You should now see Adafruit boards in the Arduino IDE, including the Trinket 5V/16MHz USB. When the Trinket is reset it enters "Bootloader Mode" for 10 seconds. The reset button is located on the top of the board opposite the USB port. While in bootloader mode the red light will pulse. When you upload your sketch onto the Trinket you need to time everything right so the uploading begins while the Trinket is still in Bootloader Mode. We found that the best way is to press the Trinket reset button just before or after clicking upload in the IDE depending on how fast your computer is.
The Trinket uses some pretty slick programming to communicate over USB without having serial support. This means that it is pretty particular about where its plugged into your computer. The Trinket may not be able to handle the timing with the Arduino IDE while connected through a USB hub, or connected to a USB 3.0 port. Connect your Trinket directly to a USB 2.0 port if possible.
If you are using a Linux machine, or a Raspberry Pi to upload your code, you will need to run the Arduino IDE with root access to upload your code.
The Code
The Infinity Mirror Kit V2 comes with a Trinket preloaded with the code required to play the animations explained in the operating instructions. If you want to edit or change the animations then go for it! This project is made to be hacked, so here is the code and an explanation about how it works! We even included
/* * This program drives the Core Electronics Infinity Kit * http://coreelec.io/infinitykit * 2018 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ /******************************************************************************* * Libraries * ******************************************************************************/ #include <Adafruit_NeoPixel.h> #ifdef __AVR__ #include <avr/power.h> #endif /******************************************************************************* * Hardware Definitions * You shouldn't need to change these ******************************************************************************/ // The digital pin that drives the LED strip const byte strip_pin = 0; // Number of LEDs in the strip. Shouldn't need changing unless you hack the hardware const byte num_leds = 44; // Potentiometer pin selects the mode const byte pot = 1; // Trinkets use 10bit ADCs. If you wish to port to a different platform you might want to redefine the ADC precision const int ADC_precision = 1023; // This pin to be used as Ground for the Potentiometer const byte potLow = 1; /******************************************************************************* * Global Variables * ******************************************************************************/ // Number of modes const byte mode_count = 6; // This is the option that determines the order that settings are available from the user-input pot. // Here we name each mode and give it a number designation const byte mode_off = 1; const byte mode_rainbow = 2; const byte mode_scroll = 3; const byte mode_comet = 4; const byte mode_solid = 5; const byte mode_brightness = 6; // Parameter 1 = number of pixels in strip // Parameter 2 = Arduino pin number (most are valid) // Parameter 3 = pixel type flags, add together as needed: // NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) // NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) // NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products) // NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2) // NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products) Adafruit_NeoPixel strip = Adafruit_NeoPixel(num_leds, strip_pin, NEO_GRB NEO_KHZ800); /******************************************************************************* * SETUP * ******************************************************************************/ void setup() { // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket #if defined (__AVR_ATtiny85__) if (F_CPU == 16000000) clock_prescale_set(clock_div_1); #endif // End of trinket special code pinMode(potLow, OUTPUT); // Set Pin #1 to Ground for the Pot digitalWrite(potLow, LOW); strip.begin(); // Initialize all pixels to off strip.show(); } /******************************************************************************* * LOOP * ******************************************************************************/ void loop() { // User-set starting colour used in the wheel to determine color static byte userColour = 0; // In this section we detect and set the mode. if (mode_check() == mode_off) { off(); } else if (mode_check() == mode_rainbow) { rainbow(); }else if (mode_check() == mode_scroll) { // The scroll() function returns the new userColour to be used elsewhere userColour = scroll(); } else if (mode_check() == mode_comet) { comet(userColour); } else if (mode_check() == mode_solid) { solid(userColour); } else if (mode_check() == mode_brightness){ brightness(userColour); } } /******************************************************************************* * Functions * ******************************************************************************/ // check and set the mode byte mode_check() { // break the pot reading into steps short mode_step = ADC_precision / mode_count; // take a reading from the potentiometer short val = analogRead(pot); byte i; // compare the pot reading to the steps for (i=1; i<mode_count; i ){ val = val - mode_step; if(val <= 0) break; } // catch any strange errors if (i > mode_count) i = mode_count; // return i (mode) return i; } // This is the off mode. Turn all LEDs OFF void off() { for(byte i=0; i<strip.numPixels(); i ) { strip.setPixelColor(i, (0,0,0)); } strip.setBrightness(255); strip.show(); delay(50); } // First part of comet mode. Moves the head pixel around the strip void comet(byte userColour){ byte ofs = 15; for (int j=0; j<strip.numPixels(); j ){ strip.clear(); // send position and color to drawComet drawComet(j, userColour); delay(30); strip.show(); } } // Draw a comet on the strip and handle wrapping gracefully. void drawComet(int pos, byte userColour) { // Brightness of the first LED in the comet is taken from universal brightness setting // Length of comet tail byte len = 40; // Parameter that effects how quickly the comet tail dims float lambda = 0.001; // Get colour of comet head, and prepare variables to use for tail-dimming // Convert to RGB for dimming uint8_t R, G, B; R = (Wheel(userColour) >> 16) & 0xFF; G = (Wheel(userColour) >> 8) & 0xFF; B = Wheel(userColour) & 0xFF; // Head of comet strip.setPixelColor(pos, Wheel(userColour)); // Figure out if the current pixel is wrapped across the strip ends or not, light that pixel for(int i=1; i<len; i ){ // Wrapped if( pos - i < 0 ){ strip.setPixelColor(strip.numPixels() pos-i, strip.Color(R,G,B)); //while (true) {} // strip.setPixelColor(44 pos-i, strip.Color(R,G,B)); // Not wrapped } else { strip.setPixelColor(pos-i, strip.Color(R,G,B)); } // Exponential decay function to dim tail LEDs R = uint8_t(R * exp(-lambda)); G = uint8_t(G * exp(-lambda)); B = uint8_t(B * exp(-lambda)); lambda = lambda; } } // Rainbow function void rainbow() { byte i, j; for(j=0; j<256; j ) { for(i=0; i<strip.numPixels(); i ) { strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) j) & 255)); } strip.show(); // Break out of loop if mode is changed if(mode_check() != mode_rainbow) return; delay(40); } } // Input a value 0 to 255 to get a color value. // The colours are a transition r - g - b - back to r. uint32_t Wheel(byte WheelPos) { WheelPos = 255 - WheelPos; if(WheelPos < 85) { return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3); } if(WheelPos < 170) { WheelPos -= 85; return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3); } WheelPos -= 170; return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0); } // Display a single colour on all LEDs. While accessible from the state machine, this function does not set/check // the state variables, because it is called by other states. void solid(byte userColour){ short i; for(i=0; i<strip.numPixels(); i ){ // Color set by user variable userColour and that color's position on the wheel strip.setPixelColor(i, Wheel(userColour)); } strip.show(); delay(50); } // Scroll through the colour wheel for all LEDs. Also allows user to set a desired colour for other modes. uint16_t scroll() { // Initialize userColour. 1-255 number to be used in Wheel() byte userColour; byte i, j; // Transition through all colors of the wheel for(j=0; j<256; j ) { for(i=0; i<strip.numPixels(); i ) { strip.setPixelColor(i, Wheel((j) & 255)); } // Store last wheel position as userColour userColour = j; strip.show(); // Return the set colour as 1-255 Wheel number for use in other sequences if(mode_check() != mode_scroll) return userColour; delay(40); } } // Fade the brightness up down and update a brightness parameter for other modes. void brightness(byte userColour) { byte i; // Increases brightness for (i=0; i<255; i ){ strip.setBrightness(i); // Slows brightness change when dim if (i<50){ delay(50); } // Updates changes through solid() solid(userColour); // Checks to see mode hasn't been changed if(mode_check() != mode_brightness) return; } // Decreases Brightness for (i=255; i>1; i--){ strip.setBrightness(i); if (i<50){ delay(50); } // Updates changes through solid() solid(userColour); // Checks to see mode hasn't been changed if(mode_check() != mode_brightness) return; } }
Let's break it down into parts. The code is organized by the different modes, so let's talk about mode control.
Mode Control
To control the various modes of the Infinity Mirror V2, we first define the constant variables that will be used to control the modes. We define how many modes we will have and give each a number. These are used later in the sketch. To add an additional mode, increase the mode_count variable.
// Number of modes const byte mode_count = 6; // This is the option that determines the order that settings are available from the user-input pot. // Here we name each mode and give it a number designation const byte mode_off = 1; const byte mode_rainbow = 2; const byte mode_scroll = 3; const byte mode_comet = 4; const byte mode_solid = 5; const byte mode_brightness = 6; void setup(){ }
The next portion of the code we call our mode check function (up next) and assign the mode based on the readings.
void loop() { // User-set starting colour used in the wheel to determine color static byte userColour = 0; // In this section we detect and set the mode. if (mode_check() == mode_off) { off(); } else if (mode_check() == mode_rainbow) { rainbow(); }else if (mode_check() == mode_scroll) { // The scroll() function returns the new userColour to be used elsewhere userColour = scroll(); } else if (mode_check() == mode_comet) { comet(userColour); } else if (mode_check() == mode_solid) { solid(userColour); } else if (mode_check() == mode_brightness){ brightness(userColour); } } }
This is the function where we check the position of the potentiometer to change the mode. We will call this throughout the sketch to check to see if the mode has changed.
// check and set the mode byte mode_check() { // break the pot reading into steps short mode_step = ADC_precision / mode_count; // take a reading from the potentiometer short val = analogRead(pot); byte i; // compare the pot reading to the steps for (i=1; i<mode_count; i ){ val = val - mode_step; if(val <= 0) break; } // catch any strange errors if (i > mode_count) i = mode_count; // return i (mode) return i; }
The Modes
Off - Turns off all the LEDs and resets the user settings to default
// This is the off mode. Turn all LEDs OFF void off() { for(byte i=0; i<strip.numPixels(); i ) { strip.setPixelColor(i, (0,0,0)); } strip.setBrightness(255); strip.show(); delay(50); }
Comet - This animation makes a comet effect that spins around the mirror. You can edit the style of this animation by changing len and lambda
// First part of comet mode. Moves the head pixel around the strip void comet(byte userColour){ byte ofs = 15; for (int j=0; j<strip.numPixels(); j ){ strip.clear(); // send position and color to drawComet drawComet(j, userColour); delay(30); strip.show(); } } // Draw a comet on the strip and handle wrapping gracefully. void drawComet(int pos, byte userColour) { // Brightness of the first LED in the comet is taken from universal brightness setting // Length of comet tail byte len = 40; // Parameter that effects how quickly the comet tail dims float lambda = 0.001; // Get colour of comet head, and prepare variables to use for tail-dimming // Convert to RGB for dimming uint8_t R, G, B; R = (Wheel(userColour) >> 16) & 0xFF; G = (Wheel(userColour) >> 8) & 0xFF; B = Wheel(userColour) & 0xFF; // Head of comet strip.setPixelColor(pos, Wheel(userColour)); // Figure out if the current pixel is wrapped across the strip ends or not, light that pixel for(int i=1; i<len; i ){ // Wrapped if( pos - i < 0 ){ strip.setPixelColor(strip.numPixels() pos-i, strip.Color(R,G,B)); //while (true) {} // strip.setPixelColor(44 pos-i, strip.Color(R,G,B)); // Not wrapped } else { strip.setPixelColor(pos-i, strip.Color(R,G,B)); } // Exponential decay function to dim tail LEDs R = uint8_t(R * exp(-lambda)); G = uint8_t(G * exp(-lambda)); B = uint8_t(B * exp(-lambda)); lambda = lambda; } }
Rainbow - This animation is a rainbow effect that appears to rotate in within the mirror. It's important to note the mode check portion of this function. Every 40ms we check to see if the knob has been moved into a different mode.
// Rainbow function void rainbow() { byte i, j; for(j=0; j<256; j ) { for(i=0; i<strip.numPixels(); i ) { strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) j) & 255)); } strip.show(); // Break out of loop if mode is changed if(mode_check() != mode_rainbow) return; delay(40); } }
Wheel - This function is used in all the others to create a colour wheel. It's best not to touch it!
// Input a value 0 to 255 to get a color value. // The colours are a transition r - g - b - back to r. uint32_t Wheel(byte WheelPos) { WheelPos = 255 - WheelPos; if(WheelPos < 85) { return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3); } if(WheelPos < 170) { WheelPos -= 85; return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3); } WheelPos -= 170; return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0); }
Solid - This sets the LEDs to one colour. The colour is set by using Scroll mode.
// Display a single colour on all LEDs. While accessible from the state machine, this function does not set/check // the state variables, because it is called by other states. void solid(byte userColour){ short i; for(i=0; i<strip.numPixels(); i ){ // Color set by user variable userColour and that color's position on the wheel strip.setPixelColor(i, Wheel(userColour)); } strip.show(); delay(50); }
Scroll - This mode transitions all the LEDs through each colour. When we change modes the last colour is saved to be used in other animations.
// Scroll through the colour wheel for all LEDs. Also allows user to set a desired colour for other modes. uint16_t scroll() { // Initialize userColour. 1-255 number to be used in Wheel() byte userColour; byte i, j; // Transition through all colors of the wheel for(j=0; j<256; j ) { for(i=0; i<strip.numPixels(); i ) { strip.setPixelColor(i, Wheel((j) & 255)); } // Store last wheel position as userColour userColour = j; strip.show(); // Return the set colour as 1-255 Wheel number for use in other sequences if(mode_check() != mode_scroll) return userColour; delay(40); } }
Brightness - This mode is pretty self-explanatory. It fades through brightnesses, and when you change mode the brightness level is set for all other animations.
// Fade the brightness up down and update a brightness parameter for other modes. void brightness(byte userColour) { byte i; // Increases brightness for (i=0; i<255; i ){ strip.setBrightness(i); // Slows brightness change when dim if (i<50){ delay(50); } // Updates changes through solid() solid(userColour); // Checks to see mode hasn't been changed if(mode_check() != mode_brightness) return; } // Decreases Brightness for (i=255; i>1; i--){ strip.setBrightness(i); if (i<50){ delay(50); } // Updates changes through solid() solid(userColour); // Checks to see mode hasn't been changed if(mode_check() != mode_brightness) return; } }
Alternative Modes
If you decide to change the modes on your Infinity Mirror V2, keep in mind that there is very little memory on the Trinket. This program uses 91% of the available memory and quite a bit of time was spent shrinking it to fit. That said, you can change all you like, but you will probably need to replace one of the existing modes instead of just adding more. We recommend removing the Comet mode first, as it uses 34% of the total available memory. That way you can even add a couple additional modes. Save space by using the smallest variable types possible! Here are some animations that you could try out on your Infinity Mirror V2!
We've created this animation packed version of the code to help you get started!
/* * This program drives the Core Electronics Infinity Kit * http://coreelec.io/infinitykit * 2018 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ /******************************************************************************* * Libraries * ******************************************************************************/ #include <Adafruit_NeoPixel.h> #ifdef __AVR__ #include <avr/power.h> #endif /******************************************************************************* * Hardware Definitions * You shouldn't need to change these ******************************************************************************/ // The digital pin that drives the LED strip const byte strip_pin = 0; // Number of LEDs in the strip. Shouldn't need changing unless you hack the hardware const byte num_leds = 44; // Potentiometer pin selects the mode const byte pot = 1; // Trinkets use 10bit ADCs. If you wish to port to a different platform you might want to redefine the ADC precision const int ADC_precision = 1023; // This pin to be used as Ground for the Potentiometer const byte potLow = 1; /******************************************************************************* * Global Variables * ******************************************************************************/ // Number of modes const byte mode_count = 6; // This is the option that determines the order that settings are available from the user-input pot. // Here we name each mode and give it a number designation const byte mode_off = 1; const byte mode_rainbow = 2; const byte mode_scroll = 3; const byte mode_comet = 4; const byte mode_solid = 5; const byte mode_brightness = 6; // Parameter 1 = number of pixels in strip // Parameter 2 = Arduino pin number (most are valid) // Parameter 3 = pixel type flags, add together as needed: // NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) // NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) // NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products) // NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2) // NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products) Adafruit_NeoPixel strip = Adafruit_NeoPixel(num_leds, strip_pin, NEO_GRB NEO_KHZ800); /******************************************************************************* * SETUP * ******************************************************************************/ void setup() { // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket #if defined (__AVR_ATtiny85__) if (F_CPU == 16000000) clock_prescale_set(clock_div_1); #endif // End of trinket special code pinMode(potLow, OUTPUT); // Set Pin #1 to Ground for the Pot digitalWrite(potLow, LOW); strip.begin(); // Initialize all pixels to off strip.show(); } /******************************************************************************* * LOOP * ******************************************************************************/ void loop() { // User-set starting colour used in the wheel to determine color static byte userColour = 0; // In this section we detect and set the mode. if (mode_check() == mode_off) { off(); }else if (mode_check() == mode_rainbow) { //rainbow(); // This is an optional pattern, try commenting rainbow() and put this in! theaterChaseRainbow(50); }else if (mode_check() == mode_scroll) { // The scroll() function returns the new userColour to be used elsewhere //userColour = scroll(); // This optional pattern replaces scroll() and changes the colors with a wipe userColour = scrollWipe(); }else if (mode_check() == mode_comet) { //comet(userColour); // This is an optional pattern, try commenting comet() and put this in! Sparkle(0xff, 0xff, 0xff, 10); }else if (mode_check() == mode_solid) { //solid(userColour); // This is an optional pattern, try commenting solid() and put this in! theaterChase(userColour, 50); }else if (mode_check() == mode_brightness){ //brightness(userColour); colorWipe(strip.Color(64, 0, 0), 50); // Red colorWipe(strip.Color(0, 64, 0), 50); // Green colorWipe(strip.Color(0, 0, 64), 50); // Blue colorWipe(strip.Color(0,0,0), 25); // Black } } /******************************************************************************* * Functions * ******************************************************************************/ // check and set the mode byte mode_check() { // break the pot reading into steps short mode_step = ADC_precision / mode_count; // take a reading from the potentiometer short val = analogRead(pot); byte i; // compare the pot reading to the steps for (i=1; i<mode_count; i ){ val = val - mode_step; if(val <= 0) break; } // catch any strange errors if (i > mode_count) i = mode_count; // return i (mode) return i; } // This is the off mode. Turn all LEDs OFF void off() { for(byte i=0; i<strip.numPixels(); i ) { strip.setPixelColor(i, (0,0,0)); } strip.setBrightness(255); strip.show(); delay(50); } // First part of comet mode. Moves the head pixel around the strip void comet(byte userColour){ byte ofs = 15; for (int j=0; j<strip.numPixels(); j ){ strip.clear(); // send position and color to drawComet drawComet(j, userColour); delay(30); strip.show(); } } // Draw a comet on the strip and handle wrapping gracefully. void drawComet(int pos, byte userColour) { // Brightness of the first LED in the comet is taken from universal brightness setting // Length of comet tail byte len = 40; // Parameter that effects how quickly the comet tail dims float lambda = 0.001; // Get colour of comet head, and prepare variables to use for tail-dimming // Convert to RGB for dimming uint8_t R, G, B; R = (Wheel(userColour) >> 16) & 0xFF; G = (Wheel(userColour) >> 8) & 0xFF; B = Wheel(userColour) & 0xFF; // Head of comet strip.setPixelColor(pos, Wheel(userColour)); // Figure out if the current pixel is wrapped across the strip ends or not, light that pixel for(int i=1; i<len; i ){ // Wrapped if( pos - i < 0 ){ strip.setPixelColor(strip.numPixels() pos-i, strip.Color(R,G,B)); //while (true) {} // strip.setPixelColor(44 pos-i, strip.Color(R,G,B)); // Not wrapped } else { strip.setPixelColor(pos-i, strip.Color(R,G,B)); } // Exponential decay function to dim tail LEDs R = uint8_t(R * exp(-lambda)); G = uint8_t(G * exp(-lambda)); B = uint8_t(B * exp(-lambda)); lambda = lambda; } } // Rainbow function void rainbow() { byte i, j; for(j=0; j<256; j ) { for(i=0; i<strip.numPixels(); i ) { strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) j) & 255)); } strip.show(); // Break out of loop if mode is changed if(mode_check() != mode_rainbow) return; delay(40); } } // Input a value 0 to 255 to get a color value. // The colours are a transition r - g - b - back to r. uint32_t Wheel(byte WheelPos) { WheelPos = 255 - WheelPos; if(WheelPos < 85) { return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3); } if(WheelPos < 170) { WheelPos -= 85; return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3); } WheelPos -= 170; return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0); } // Display a single colour on all LEDs. While accessible from the state machine, this function does not set/check // the state variables, because it is called by other states. void solid(byte userColour){ short i; for(i=0; i<strip.numPixels(); i ){ // Color set by user variable userColour and that color's position on the wheel strip.setPixelColor(i, Wheel(userColour)); } strip.show(); delay(50); } // Scroll through the colour wheel for all LEDs. Also allows user to set a desired colour for other modes. uint16_t scroll() { // Initialize userColour. 1-255 number to be used in Wheel() byte userColour; byte i, j; // Transition through all colors of the wheel for(j=0; j<256; j ) { for(i=0; i<strip.numPixels(); i ) { strip.setPixelColor(i, Wheel((j) & 255)); } // Store last wheel position as userColour userColour = j; strip.show(); // Return the set colour as 1-255 Wheel number for use in other sequences if(mode_check() != mode_scroll) return userColour; delay(40); } } // Fade the brightness up down and update a brightness parameter for other modes. void brightness(byte userColour) { byte i; // Increases brightness for (i=0; i<255; i ){ strip.setBrightness(i); // Slows brightness change when dim if (i<50){ delay(50); } // Updates changes through solid() solid(userColour); // Checks to see mode hasn't been changed if(mode_check() != mode_brightness) return; } // Decreases Brightness for (i=255; i>1; i--){ strip.setBrightness(i); if (i<50){ delay(50); } // Updates changes through solid() solid(userColour); // Checks to see mode hasn't been changed if(mode_check() != mode_brightness) return; } } /******************************************************************************* * Optional Light Effects * Replace the existing light effects by calling these instead! ******************************************************************************/ //Theatre-style crawling lights with rainbow effect void theaterChaseRainbow(uint8_t wait) { for (int j=0; j < 256; j ) { // cycle all 256 colors in the wheel for (int q=0; q < 3; q ) { for (uint16_t i=0; i < strip.numPixels(); i=i 3) { //turn every third pixel on strip.setPixelColor(i q, Wheel(((i * 256 / strip.numPixels()) j) & 255)); } strip.show(); // Break out of loop if mode is changed if(mode_check() != mode_rainbow) return; delay(wait); for (uint16_t i=0; i < strip.numPixels(); i=i 3) { //turn every third pixel off strip.setPixelColor(i q, 0); } } } } //Single-color theatre-style crawling lights void theaterChase(uint32_t c, uint8_t wait) { //do 10 cycles of chasing for (int j=0; j<10; j ) { for (int q=0; q < 3; q ) { for (uint16_t i=0; i < strip.numPixels(); i=i 3) { //turn every third pixel on strip.setPixelColor(i q, Wheel(c)); } strip.show(); delay(wait); for (uint16_t i=0; i < strip.numPixels(); i=i 3) { //turn every third pixel off strip.setPixelColor(i q, 0); } } } } // Fill the dots one after the other with a color void colorWipe(uint32_t c, uint8_t wait) { for(uint16_t i=0; i<strip.numPixels(); i ) { strip.setPixelColor(i, c); strip.show(); if(mode_check() != mode_brightness) return; // Break out of loop if mode is changed delay(wait); } } // Fill the dots one after the other with a color uint16_t scrollWipe() { byte userColour; // Initialize userColour. 1-255 number to be used in Wheel() byte i, j; for(j=0; j<256; j= j 16) { // Transition through all colors of the wheel for(uint16_t i=0; i<strip.numPixels(); i ) { strip.setPixelColor(i, Wheel((j) & 255)); userColour = j; strip.show(); if(mode_check() != mode_scroll) return userColour;// Return the set colour as 1-255 Wheel number for use in other sequences delay(40); } } } void Sparkle(byte red, byte green, byte blue, byte SpeedDelay) { byte Pixel = random(strip.numPixels()); strip.setPixelColor(Pixel,red,green,blue); strip.show(); delay(SpeedDelay); strip.setPixelColor(Pixel,0,0,0); }
Changing Modes
To change to one of our provided alternate modes, you just need to change the functions called in the main loop. Just comment out the animation being used and uncomment the one you want. Mix and match however you like, and add your own! If you create a great animation for the Infinity Kit, please share it on our Forum!
Here is an explanation of the alternate animations we provided.
Color Wipe - Replaces all the colours with what is called out when you start the function:
void loop() { // Some example procedures showing how to display to the pixels: colorWipe(strip.Color(0,0,0), 25); // Black colorWipe(strip.Color(64, 0, 0), 100); // Red colorWipe(strip.Color(0, 64, 0), 100); // Green colorWipe(strip.Color(0, 0, 64), 100); // Blue } // Fill the dots one after the other with a color void colorWipe(uint32_t c, uint8_t wait) { for(uint16_t i=0; i<strip.numPixels(); i ) { strip.setPixelColor(i, c); strip.show(); // Break out of loop if mode is changed if(mode_check() != mode_brightness) return; delay(wait); } }
Theater Chase - Gives an illusion that the lights are crawling around in a circle. You could use your selected colour rather than entering it each time.
void loop() { // Send a theater pixel chase in... theaterChase(strip.Color(userColour, 50); } void theaterChase(uint32_t c, uint8_t wait) { for (int j=0; j<10; j ) { //do 10 cycles of chasing for (int q=0; q < 3; q ) { for (uint16_t i=0; i < strip.numPixels(); i=i 3) { strip.setPixelColor(i q, Wheel(c)); //turn every third pixel on } strip.show(); delay(wait); for (uint16_t i=0; i < strip.numPixels(); i=i 3) { strip.setPixelColor(i q, 0); //turn every third pixel off } } } }
Rainbow Theater Chase - The same as theater chase but rather than selecting the color its rainbow!
void loop() { theaterChaseRainbow(50); } //Theatre-style crawling lights with rainbow effect void theaterChaseRainbow(uint8_t wait) { for (int j=0; j < 256; j ) { // cycle all 256 colors in the wheel for (int q=0; q < 3; q ) { for (uint16_t i=0; i < strip.numPixels(); i=i 3) { //turn every third pixel on strip.setPixelColor(i q, Wheel(((i * 256 / strip.numPixels()) j) & 255)); } strip.show(); // Break out of loop if mode is changed. CHANGE THIS TO WHATEVER MODE YOU USE if(mode_check() != mode_rainbow) return; delay(wait); for (uint16_t i=0; i < strip.numPixels(); i=i 3) { strip.setPixelColor(i q, 0); //turn every third pixel off } } } }
Scroll Wipe - Change through the colors with a wipe effect!
uint16_t scrollWipe() { // Initialize userColour. 1-255 number to be used in Wheel() byte userColour; byte i, j; // Transition through all colors of the wheel for(j=0; j<256; j= j 16) { for(uint16_t i=0; i<strip.numPixels(); i ) { strip.setPixelColor(i, Wheel((j) & 255)); userColour = j; strip.show(); // Return the set colour as 1-255 Wheel number for use in other sequences if(mode_check() != mode_scroll) return userColour; delay(40); } } }
Sparkle - Sparkling colour!
void Sparkle(byte red, byte green, byte blue, byte SpeedDelay) { byte Pixel = random(strip.numPixels()); strip.setPixelColor(Pixel,red,green,blue); strip.show(); delay(SpeedDelay); strip.setPixelColor(Pixel,0,0,0); }
Going Further
There we have it! A complete Infinity Mirror Kit V2, and possibly even customised. We designed this project to be hacked, don't be scared to make this project your own by adding sensors or other interfaces! If you want to learn more about Arduino and coding, check out our Arduino Tutorials and our Arduino Workshop. Please pop over to the Infinity Mirror Kit Hall of Backers to see the people who originally backed this Kickstarter! If you have any questions about this kit, please reach out on the forum! We are here to help!