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…
Step 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.
I’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