Ever since my addressable RGB LED strips (WS2812B) came from Aliexpress, I’ve been fascinated with LED projects. Following up on my success with my Charlieplexed LED clock, I wanted to create something with more Jazz..
While browsing google images, I came across an instructable for an Arduino Infinity Mirror and the brain cells started working in overdrive. Why shouldn’t I make an infinity mirror, that’s also a clock !!
Features:
- Works like a clock – the colours of the hands are user-programmable over bluetooth
- Has an IR sensor to sense if someone is coming near the mirror. Switches off the LEDs, so that this can be used as a normal mirror
- Shows a rainbow effect every 15 minutes (time is user adjustable)
- Has a touch button to toggle between clock and rainbow effects
- Switches to night mode between midnight and 7:00 AM – can be changed in the code.
- Can be programmed over bluetooth – so you do not need to take it off the wall if you need to update the code

Step 1: Electronics
- A Standalone Arduino: http://dushyant.ahuja.ws/2013/10/standalone-arduin… OR Arduino mini pro: http://www.aliexpress.com/item/10Pcs-Lot-Pro-Mini-…
- RTC Module – DS1302: http://fabtolab.com/DS1302-RTC-module?search=rtc
- LM2596 Step Down Adjustable Power Supply Module 1.3V-35V: http://cgi.ebay.in/ws/eBayISAPI.dll?ViewItem&item=…
- 1m 60LEDs/M Addressable RGB LED Strip (WS2812B): http://www.aliexpress.com/item/1M-WS2812-WS2812B-6…
- HC-05 Bluetooth module: http://www.aliexpress.com/item/RS232-TTL-LC-05-Wir…
- IR Proximity Sensor: http://www.instructables.com/id/Simple-IR-proximit…4 IR LEDs; 1 IR LED detector: http://www.evelta.com/industrial-control/sensors/5…
- Touch Pad: http://www.aliexpress.com/item/Touch-Pad-Brick-Sen…
- 9V – 2A Adapter
- CP2102 USB-to-TTL (the RST pin on the CP2102 is not for resetting the arduino – you have to solder a wire to the DTR pad on the PCB – which sends a reset signal to program the arduino. This has to be connected to the DTR pin on the Arduino
Step 2: The Circuit
The circuit is very simple:
- LED Strip – Connect power to the LM2596 Step Down Adjustable Power Supply Module – ensure you adjust the pot so that the output is 5V; Connect the ground to the common ground; Connect data to pin D5 of the Arduino
- Touch Sensor – data to pin D2 of Arduino
- RTC Module – SDA and SCL to the A4 and A5 of the Arduino respectively
- Bluetooth Module – Connect RX to Arduino’s TX and TX to Arduino’s RX. You will have to break-out pin 32 on the module to the DTR pin on the Arduino (This allows you to program the Arduino over bluetooth)
- IR Proximity Sensor – create the sensor as per this instructable: http://www.instructables.com/id/Simple-IR-proximit… – connect the photo-diode to A1 (A0 on the instructable schematic) and the IR LEDs to D13 (D2 on the instructable schematic)
- Connect the Power 9V 2A Power supply to the input of the 7805 and the LM2596
To setup the bluetooth programming circuit, please follow this link: http://makezine.com/projects/diy-arduino-bluetooth…
Infinity Mirror.sch372 KBStep 3: The Code
This clock uses the following libraries (and all thanks to the authors of these libraries):
- RTCLib Arduino Library: https://github.com/adafruit/RTClib
- FastLED Arduino Library v2.1: http://fastled.io/ https://github.com/FastLED/FastLED/tree/FastLED2.1
- SerialCommand Arduino Library: https://github.com/scogswell/ArduinoSerialCommand…
- TimerOne Arduino Library: https://github.com/scogswell/ArduinoSerialCommand…
The latest version of the code can be downloaded from the project github: https://github.com/dushyantahuja/Smart-Infinity-Mi…
#include <Wire.h>
#include "RTClib.h"
#include "FastLED.h"
#include <SoftwareSerial.h>
#include <SerialCommand.h>
#include "EEPROM.h"
#include "TimerOne.h"
#define NUM_LEDS 60
#define DATA_PIN 5
#define UPDATES_PER_SECOND 100
#define SWITCHPIN 2
// Variables for IR Proximity
int IRpin = A1; // IR photodiode on analog pin A1
int IRemitter = 13; // IR emitter LED on digital pin 4
//
CRGBPalette16 currentPalette;
TBlendType currentBlending;
CRGB leds[NUM_LEDS],minutes,hours,seconds,l,bg,lines;
RTC_DS1307 rtc;
SerialCommand sCmd;
boolean missed=0, ledState = 1, lastsec=1, multieffects = 0;
byte lastsecond, rain;
int light_low, light_high;
DateTime now;
void(* resetFunc) (void) = 0;
void setup() {
digitalWrite(IRemitter,LOW); // turning the IR LEDs off - as a precaution - they don't have current limiting resistors
Wire.begin();
rtc.begin();
Serial.begin(115200);
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
currentPalette = RainbowStripeColors_p;
currentBlending = NOBLEND;
// ******** Setup the default values for parameters (if not set before)
if (EEPROM.read(99) != 1){ // Check if colours have been set or not
EEPROM.write(0,255); // Seconds Colour - R-G-B - White
EEPROM.write(1,255);
EEPROM.write(2,255);
EEPROM.write(3,255); // Minutes Colour - R-G-B - Red
EEPROM.write(4,0);
EEPROM.write(5,0);
EEPROM.write(6,0); // Hours Colour - R-G-B - Green
EEPROM.write(7,255);
EEPROM.write(8,0);
EEPROM.write(9,0); // BG Colour - R-G-B - Black
EEPROM.write(10,0);
EEPROM.write(11,0);
EEPROM.write(12, 0); // Light sensitivity - low
EEPROM.write(13, 55); // Light sensitivity - high
EEPROM.write(14, 15); // Minutes for each rainbow
EEPROM.write(99,1);
}
// Else read the parameters from the EEPROM
else {
seconds.r = EEPROM.read(0);
seconds.g = EEPROM.read(1);
seconds.b = EEPROM.read(2);
minutes.r = EEPROM.read(3);
minutes.g = EEPROM.read(4);
minutes.b = EEPROM.read(5);
hours.r = EEPROM.read(6);
hours.g = EEPROM.read(7);
hours.b = EEPROM.read(8);
bg.r = EEPROM.read(9);
bg.g = EEPROM.read(10);
bg.b = EEPROM.read(11);
light_low = EEPROM.read(12);
light_high = EEPROM.read(13);
rain = EEPROM.read(14);
}
// ********** Setup the serial commands
sCmd.addCommand("MULTI", set_multi);
sCmd.addCommand("STAT", clockstatus);
sCmd.addCommand("SETRAIN", set_rainbow);
sCmd.addCommand("HOUR", set_hour);
sCmd.addCommand("MIN", set_minute);
sCmd.addCommand("SEC", set_second);
sCmd.addCommand("BG", set_bg);
sCmd.addCommand("LIGHT", set_light);
sCmd.addCommand("TIME", set_time);
sCmd.addCommand("MISSED", missedCall);
sCmd.addCommand("MISSEDOFF", missedOff);
sCmd.addCommand("RAINBOW", effects);
sCmd.addCommand("MISSED", missedCall);
sCmd.addCommand("MISSEDOFF", missedOff);
sCmd.addDefaultHandler(effects);
// ********** Set all LEDs to background colour
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = bg;
}
pinMode(IRemitter,OUTPUT); // IR emitter LED on digital pin 2
digitalWrite(IRemitter,LOW);// setup IR LED as off
clockstatus();
attachInterrupt(1, set_multi, FALLING);
Timer1.initialize();
Timer1.attachInterrupt(state, 500000);
}
void loop() {
sCmd.readSerial();
if(readIR(10) > 50){ // Switch off LEDs if someone is near the mirror - so that it can be used as a mirror. Switch off LEDs between 12:00 and 6:00 to save energy and cool down the LEDs and power supplies
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB::Black;
}
FastLED.show();
ledState = 1;
//FastLED.delay(200);
}
else {
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = bg;
}
if(multieffects){ // Check if the button for multi-effects has been pressed
uint8_t secondHand;
secondHand = now.second();
if( secondHand == 0) { currentPalette = RainbowColors_p; currentBlending = BLEND; }
if( secondHand == 30) { currentPalette = RainbowStripeColors_p; currentBlending = BLEND; }
static uint8_t startIndex = 0;
startIndex = startIndex + 1;
FillLEDsFromPaletteColors( startIndex);
FastLED.show();
}
else if(ledState){ // Main clock code
// Setting brightness to light_high
int x = light_high; // analogRead(IRpin);
now = rtc.now();
if(( now.minute() % rain == 0 && now.second() == 0)){
effects();
}
for(byte i=0; i<=now.minute();i++){
//Serial.println(minutes);
leds[i] = minutes;
}
//Serial.println(now.hour(),DEC);
for(byte i = 0; i<60; i+=5){
leds[i]=CRGB::White;
}
for(byte i=(now.hour()%12)*5; i<=((now.hour())%12)*5+(now.minute()/12);i++){
leds[i] = hours;
}
if(now.hour() < 7) LEDS.setBrightness(constrain(light_low,0,100)); // Set brightness to light_low during night - cools down LEDs and power supplies.
else LEDS.setBrightness(constrain(light_high,10,255));
if(lastsec){
l=leds[now.second()];
leds[now.second()] = seconds;
lastsecond = now.second();
lastsec = 0;
// Serial.println("ON");
} else {
leds[lastsecond] = l;
if(missed) all_off();
// Serial.println("OFF");
lastsec = 1;
}
FastLED.show();
ledState = 0;
}
//delay(250);
if(multieffects) FastLED.delay(1000 / UPDATES_PER_SECOND);
}
}
void FillLEDsFromPaletteColors( uint8_t colorIndex)
{
uint8_t brightness = 255;
for( int i = 0; i < NUM_LEDS; i++) {
leds[i] = ColorFromPalette( currentPalette, colorIndex, brightness, currentBlending);
colorIndex += 3;
}
}
void set_multi(){
static unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = millis();
if (interrupt_time - last_interrupt_time > 200)
{
if(multieffects){
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = bg;
}
}
multieffects = !multieffects;
Serial.println(multieffects);
}
last_interrupt_time = interrupt_time;
}
void set_rainbow(){
rain = atoi(sCmd.next());
EEPROM.write(14,rain);
Serial.println("RAINBOW TIME SET");
}
void clockstatus(){
Serial.println("Status: ");
Serial.print("BG: ");
Serial.print(bg.r);
Serial.print(" ");
Serial.print(bg.g);
Serial.print(" ");
Serial.println(bg.b);
Serial.print("SEC: ");
Serial.print(seconds.r);
Serial.print(" ");
Serial.print(seconds.g);
Serial.print(" ");
Serial.println(seconds.b);
Serial.print("MINUTE: ");
Serial.print(minutes.r);
Serial.print(" ");
Serial.print(minutes.g);
Serial.print(" ");
Serial.println(minutes.b);
Serial.print("HOUR: ");
Serial.print(hours.r);
Serial.print(" ");
Serial.print(hours.g);
Serial.print(" ");
Serial.println(hours.b);
Serial.print("Ambient Light: ");
Serial.println(analogRead(IRpin));
Serial.print("Light set - High:");
Serial.println(light_high,DEC);
Serial.print("Light set - Low:");
Serial.println(light_low,DEC);
Serial.print("Date: ");
DateTime now = rtc.now(); // DateTime(2014,5,2,22,30,0);
Serial.print(now.day(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Serial.print('/');
Serial.println(now.year(), DEC);
Serial.print("Time: ");
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println();
Serial.print("Distance: ");
Serial.println(readIR(5),DEC);
}
void state(){
ledState = 1;
}
const int colorWheelAngle = 255 / NUM_LEDS;
void effects(){
Serial.println("RAINBOW");
for (int j=0; j<3; j++){
for (int i = 0; i < 60; i++) {
FillLEDsFromPaletteColors(i);
FastLED.show();
delay(30);
}
}
lastsec = 1;
}
void missedCall()
{
missed = 1;
}
void missedOff()
{
missed = 0;
}
void all_off(){
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB::Black;
}
}
void set_hour(){
hours.r = atoi(sCmd.next());
hours.g = atoi(sCmd.next());
hours.b = atoi(sCmd.next());
EEPROM.write(6,hours.r);
EEPROM.write(7,hours.g);
EEPROM.write(8,hours.b);
Serial.println("HOUR COLOUR SET");
}
void set_minute(){
minutes.r = atoi(sCmd.next());
minutes.g = atoi(sCmd.next());
minutes.b = atoi(sCmd.next());
EEPROM.write(3,minutes.r);
EEPROM.write(4,minutes.g);
EEPROM.write(5,minutes.b);
Serial.println("MINUTE COLOUR SET");
}
void set_second(){
seconds.r = atoi(sCmd.next());
seconds.g = atoi(sCmd.next());
seconds.b = atoi(sCmd.next());
EEPROM.write(0,seconds.r);
EEPROM.write(1,seconds.g);
EEPROM.write(2,seconds.b);
Serial.println("SECOND COLOUR SET");
}
void set_bg(){
bg.r = atoi(sCmd.next());
bg.g = atoi(sCmd.next());
bg.b = atoi(sCmd.next());
EEPROM.write(9,bg.r);
EEPROM.write(10,bg.g);
EEPROM.write(11,bg.b);
Serial.println("BG COLOUR SET");
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = bg;
}
}
void set_light(){
light_low = atoi(sCmd.next());
light_high = atoi(sCmd.next());
EEPROM.write(12,light_low);
EEPROM.write(13,light_high);
Serial.println("LIGHT SET");
}
void set_time(){
String set_date, set_time;
set_date = (String)sCmd.next() + ' ' + (String)sCmd.next() + ' ' + (String)sCmd.next();
set_time = (String)sCmd.next();
rtc.adjust(DateTime(set_date.c_str(),set_time.c_str()));
}
int readIR(int times){
int ambientIR; // variable to store the IR coming from the ambient
int obstacleIR; // variable to store the IR coming from the object
int value[10]; // variable to store the IR values
int distance; // variable that will tell if there is an obstacle or not
for(int x=0;x<times;x++){
digitalWrite(IRemitter,LOW); // turning the IR LEDs off to read the IR coming from the ambient
delay(1); // minimum delay necessary to read values
ambientIR = analogRead(IRpin); // storing IR coming from the ambient
digitalWrite(IRemitter,HIGH); // turning the IR LEDs on to read the IR coming from the obstacle
delay(1); // minimum delay necessary to read values
obstacleIR = analogRead(IRpin); // storing IR coming from the obstacle
value[x] = ambientIR-obstacleIR; // calculating changes in IR values and storing it for future average
}
for(int x=0;x<times;x++){ // calculating the average based on the "accuracy"
distance+=value[x];
}
digitalWrite(IRemitter,LOW); // turning the IR LEDs off
return(distance/times); // return the final value
}
Step 4: Assembling the Clock

The clock is primarily made up of the following:
- 2 pieces of 8mm plywood (600mm x 600mm) – needs to be cut as per the schematic above
- 1 piece of 18mm block-board (600mm x 600mm) – needs to be cut as per the schematic above. Please be careful with the circle – the circumference needs to be exactly 1000mm so that the 60 LEDs fit properly. I got it cut from a carpenter and he rounded up the radius from 159.23 to 160 – so I had to use double sided foam tape to get the LEDs to fit properly
- 1 circular 6mm mirror – 400mm dia
- 1 circular 6mm glass – 400mm dia. You need to put silver sun-control film on this – you can get this either from auto-accessories vendors or from window / glazing installers. 3M has a very good film that you should be able to get from 3M dealers
- 1 small round steel plate (100mm dia)
- N45 Silicon adhesive
- Black Paint
- Black Vinyl – you can get this from auto-accessories vendors or sticker manufacturers. We used a slightly sparkling matt black – similar to this: http://www.ebay.in/itm/Brilliant-Diamond-Black-Pea…
Steps:
- Join the pieces of plywood and block-board together, keeping the block-board in the center – you should use Fevicol (or similar) as well as nail it down
- Paint the interior portion Black
- Stick the LED strip to the interior of the circular cutout in the block-board. Ensure that the first LED is at the 12 o’ clock position
- Wire up the Arduino as per the circuit in the previous step and hot-glue it to the square cutout
- Mount the touch-sensor on one of the sides using hot-glue (you will have to drill the block-board slightly so that the wires fit and don’t show)
- Stick the mirror on the back side (facing up) using N45 silicon glue
- Stick the steel plate at the center of the mirror
- Stick the glass on the front side (film inside) using N45 silicon glue
- Test the circuit by plugging it in
- Troubleshoot
- Fix the black vinyl on the front side – you will have to cut a circle in the center (~380 mm dia) so that the joint between the glass and the wood gets hidden.
Infinity Mirror.dxf204 KBI’m an architect, a project manager, a photographer, a rock climber, an origami enthusiast, a gymnast, a skater, a dramatist, a paraglider, a sky-diver, a karate expert and a million other things. How does someone define himself / herself? To me I’m just ‘me’, to others I’m“you”, “brother”, “friend”, et al.

So I’ll make things simpler by saying what I did and not what I am.
Professionally I completed my architecture from Chandigarh in 2002, went on for a masters’ programme in project management from Delhi and finally took up a job with Cushman & Wakefield in Mumbai. Worked for a couple of years and started my own project management practice in Mumbai.
I completed an MBA in Finance and Strategy from the Indian School of Business and am currently working for EC Harris in the renewable energy space.
Personally I believe in taking things to the extreme – I have a list of dreams that I intend to fulfill and that too as fast as possible so that I can enhance that list with many more things. Till date some of the things I have done include Karate (National level player), Gymnastics (State level player), Para-gliding (P1), Sky-Diving (3 jumps), Rock Climbing (Basic Course), etc. I have my own photo-blog which I update and intend to do many more things . . .
website: http://dushyant.ahuja.ws
Step 5: Approximate Costs
The costs were roughly as follows:
- Wood Frame – INR 1200 for the wood, INR 500 to get it cut and INR 500 to get it assembled and straightened
- LED Strip – INR 700 for 1m
- Standalone Arduino – INR 200
- Bluetooth Module – INR 500
- RTC Module – INR 155
- LM2596 Module – INR 150
- IR LEDs – INR 50
- Vinyl – INR 1000
- Shipping, Veroboard, hot glue, wires, etc – INR 400
- Touch Sensor – INR 250
All in all ~INR 5,500 (~ USD 95) – not counting my time 🙂
