Final Project


For the final project I wanted to create something very usefull and something that is not available in our market, and I spent the inital 2 months of my academy to find something usefull to make. Then one day I noticed my Mother counting on her fingers to find the time in USA.
Since my sister is settled in New Jersey and my mother being in India they both have a time gap of -8:30 hours. And my mother always use her fingers to subtract 8:30 hours from the Indian time to obtain the Eastern time so that she can call my sister and talk to her. I noticed that it was'nt easy for her to subtract 8:30 hours all the time, and even though she uses an android phone she never used the clock app to find the time nor googled. So I thought I can easly solve this problem by making a smart clock for her.
The full development of the final project can be found in the documentation of week 19: project development
.



Final Project video and slide




What does it do?
I have made a smart clock for my mother. Why I made this for my mother is because, My mother is in India and my sister is in New Jersey. They both have a time gap of -8:30 hours and I have always seen my mother counting on her fingers to subtract 8:30 hours from the Indian time to obtain the US time. So I want the clock to switch between Time Zones whenever my mothers wants to.

Who’s done what beforehand?
I searched on the internet and found 2 intresting projects on analog clock.

  • Ingrein Smart Clock
    A project on Kickstarter with almost the same concept as mine. The clock has a wooden frame and works with the quarts mechanism. It has a LCD screen and shows all the information using Wifi connection.

  • Story: The Levitating Time Piece
    A project on Kickstarter with a stunning design. The most attractive part is the levitating metal ball that moves around the clock. It is a unique way to visualize time. It has smart connectivity and a seamless display.
What did you design?
I made my clock to look like a simple analog clock, because my parents are used to those. They don't recommened a digital clock.
To make an analog smart clock I designed a mechanism to move the needles using stepper motors.
The clock have a ESP32 microprocessor and a WiFi module to fetch time from the internet and to operate the motors.
The clock have a Ultrasonic sensing module to sense the action from my mother to change the time zone.
I designed a casing for holding the motors and all electronic components.

What materials and components were used? Where did they come from? How much did they cost?


What parts and systems were made?
The parts made for the clock include a wooden clock face with numbers, hour and minute needles, spur gears, circuit board and a casing to accomodate the PCB and the mechanism.

What processes were used?
  • CNC Milling : To make the clock face using plywood.
  • Lasercutting : To make the hour and minute hand, back cover for the casing and a lock for the mechanism.
  • 3D Printing : To make the spur gears.
  • Sand Blasting : To give a rough texture for the acrylic around the LED strip.
  • PCB Milling : To mill the main control board.
  • Soldering : For stuffing all the electronic components onto the PCB board.

What questions were answered?
My main concern was how to design and integrate all the system and the bulky NEMA 17 stepper motor available in our lab inside the clock's case by keeping the size of the clock as same as of an ordinary analog clock available in the market. I somehow managed to find out suitable mini stepper motors that are compact and can be easily placed into the casing.

Another concern of mine was how to show the correct time always. I was able to make a program that first orgins all the needles to the 12 postion when the clock is switched ON and then fetches the IST and EST from the web and then move the needles to the respective positions. Also the program checks the time every second so that the time will always be accurate.

What worked? What didn’t?
The mini stepper motors I choosed where perfect for this application and it worked.

The ultrasonic sensor module was the pefect option for sensing the presence in this project because I was able to perform 2 function using the same sensor, ie, when it senses a presence within 10cm it gave signal to change time and if the sensor finds presence above 10cm it gave signal for the night light to turn ON.

The power distribution system I designed in the PCB board didn't work, It had some problem that the PCB Board was overheating and later the board crashed. It is something I have to redesign.

How was it evaluated?
Did I make the must have features: Yes
I made an analog clock which can be controlled by the gesture made by the human in its close proximity. The light behind the clock changes from white to yellow to orange to red to blue as the day progresses to night. I was able to add a night light that switches on when a movement is detected in its vicinity.

What are the features that I could not include
I was unable to create a program that automatically updates time according to the Daylight saving time in New Jersey. I would like to include this later on. Presently, it is a limitation of my project and I have to manually code it to get the correct time.

What are the implications?
Once I finish the project after rectifying the power distribution issue, I would first gift this clock to my mother. I hope she will be very happy to see my work.

I believe this clock will be very useful for the all the fablabs because all the fablabs are dependent on EST (Eastern Standard Time) that Neil follows. So the fablabs can set their local time and they can quickly change the time zone to EST.

Right now I dont have plans to commercialize this product. But I have licensed it in such a way that anyone can use this and make modifications for learning purpose, but restricts comercialization

Fabrication Process

Designing

Made the complete design using Fusion 360.


Electronics Designing

I used Autodesk Eagle for designing the circuit
PCB SCHEMATIC


PCB BOARD


I used 1/64 bit for milling the traces and 1/32 bit for milling the holes and the outline of the board.


Then I soldered all the components on to the board.


3D PRINTING

The following are the parts to be 3D printed.








LASER CUTTING

The following are the parts to be Laser cut.


SAND BLASTING

The acrylic strip placed around the neopixel need to be sand blasted to get a brushed texture, so that the light passing throung it will get disperssed.


After sand blasting


CNC MILLING

The face of the clock is to be milled using the CNC router.


I used a 3mm end mill on the ShopBot for milling. But using the 3mm end mill, I can't mill out the numbers around the clock as pockets, for that I require a smaller diameter end mill. Since a smaller diameter end mill was not available I milled out the outline of the numbers



Programming

Arduino Code:
                  #include <NTPClient.h>
                  #include <WiFi.h>
                  #include <WiFiUdp.h>
                  #include <time.h>
                  #include <Adafruit_NeoPixel.h>
                  
                  #define WiFi_led 4
                  #define dir1 15
                  #define step1 13
                  #define dir2 12
                  #define step2 14
                  #define MS1 32
                  #define MS2 33
                  #define MS3 25
                  #define lms1 4
                  #define lms2 2
                  #define neo 18
                  #define trigPin 16
                  #define echoPin 17
                  
                  Adafruit_NeoPixel pixels = Adafruit_NeoPixel(19, neo, NEO_GRB + NEO_KHZ800);
                  
                  int HR = 0, MIN = 0, SEC = 0;
                  int HR_P = 0, MIN_P = 0;
                  int USHr = 0, USMin = 0, USSec = 0;
                  
                  const char *ssid     = "panakkal";
                  const char *password = "JacOb123";
                  
                  WiFiClient client;
                  
                  const char* ntpServer   = "pool.ntp.org";
                  
                  int  gmtOffset_sec_US      = -18000;
                  int  gmtOffset_sec_IN      = 19800;
                  
                  char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
                  
                  WiFiUDP ntpUDP;
                  NTPClient timeClientIN(ntpUDP, "pool.ntp.org", gmtOffset_sec_IN);
                  NTPClient timeClientUS(ntpUDP, "pool.ntp.org", gmtOffset_sec_US);
                  
                  void setup()
                  {
                    pinMode(WiFi_led, OUTPUT);
                    pinMode(step1, OUTPUT);
                    pinMode(dir1, OUTPUT);
                    pinMode(step2, OUTPUT);
                    pinMode(dir2, OUTPUT);
                    pinMode(MS1, OUTPUT);
                    pinMode(MS2, OUTPUT);
                    pinMode(MS2, OUTPUT);
                    pinMode(neo, OUTPUT);
                    pinMode(trigPin, OUTPUT);
                    pinMode(echoPin, INPUT);
                    pinMode(lms1, INPUT_PULLUP);
                    pinMode(lms2, INPUT_PULLUP);
                    
                    Serial.begin(115200);
                  
                    WiFi.begin(ssid, password);
                  
                    Serial.println();
                    Serial.println("Connecting to WiFi");
                  
                    while ( WiFi.status() != WL_CONNECTED ) 
                    {
                      digitalWrite(WiFi_led, HIGH);
                      Serial.print( "." );
                      delay ( 250 );
                  
                      digitalWrite(WiFi_led, LOW);
                      Serial.print( "." );
                      delay ( 250 );
                    }
                    
                    digitalWrite(WiFi_led, HIGH);
                    Serial.println();
                    Serial.println("WiFi Connected, Fetching Time from web"); 
                    pixels.begin();
                    rotate_zero();
                    timeClientIN.begin();
                    timeClientUS.begin();
                    Set_Needle();
                  }  
                  
                  void loop() 
                  {
                    UpdateINTime();
                    Change_INTime();
                    Neo_IN();
                    Presence();
                    delay(500);
                  }
                  
                  void rotate_zero()                                  //Function to set the needles to zero position
                  {
                    int limit1 = digitalRead(lms1);
                    int limit2 = digitalRead(lms2);
                    digitalWrite(MS1, LOW);
                    digitalWrite(MS2, LOW);
                    digitalWrite(MS3, LOW);
                    digitalWrite(dir1, LOW);
                    digitalWrite(dir2, LOW);
                  
                    for(int x = 0; x < 3500 && limit1 == 1; x++)
                    {
                      digitalWrite(step1, HIGH);
                      delayMicroseconds(1000);
                      digitalWrite(step1, LOW);
                      delayMicroseconds(1000);
                      limit1 = digitalRead(lms1);
                    }
                    
                    for(int x = 0; x < 3500 && limit2 == 1; x++)
                    {
                      digitalWrite(step2, HIGH);
                      delayMicroseconds(1000);
                      digitalWrite(step2, LOW);
                      delayMicroseconds(1000);
                      limit2 = digitalRead(lms2);
                    }  
                  }
                  
                  void Set_Needle()                                        //Function to set the needles to the current IST
                  {
                    rotate_zero();
                    
                    UpdateINTime();
                    Serial.print("INDIA      : ");
                    Serial.print(daysOfTheWeek[timeClientIN.getDay()]);
                    Serial.print(", ");
                    Serial.print(HR); 
                    Serial.print(":"); 
                    Serial.print(MIN);
                    Serial.print(":"); 
                    Serial.println(SEC);
                  
                    UpdateUSTime();
                  
                    int stephr = 0;
                    int stepmin = 0;
                  
                    if(stepmin != MIN)
                    {
                      digitalWrite(MS1, LOW);
                      digitalWrite(MS2, LOW);
                      digitalWrite(MS3, LOW);
                      
                      if(MIN <= 30)
                      {
                        digitalWrite(dir1, HIGH);
                        for(int x = 0; x < (51.2*MIN); x++)
                        {
                          digitalWrite(step1, HIGH);
                          delayMicroseconds(1000);
                          digitalWrite(step1, LOW);
                          delayMicroseconds(1000);
                        }
                      }  
                  
                      if(MIN > 30)
                      {
                        digitalWrite(dir1, LOW);
                        for(int x = 0; x < (51.2*(60-MIN)); x++)
                        {
                          digitalWrite(step1, HIGH);
                          delayMicroseconds(1000);
                          digitalWrite(step1, LOW);
                          delayMicroseconds(1000);
                        }  
                      }
                      stepmin = MIN;
                    }
                  
                    if(stephr != HR)
                    {
                      digitalWrite(MS1, LOW);
                      digitalWrite(MS2, LOW);
                      digitalWrite(MS3, LOW);
                  
                      if((HR > 0 && HR <= 6) || (HR > 12 && HR <= 18))
                      {
                        digitalWrite(dir2, HIGH);
                        if(HR > 0 && HR <= 6)
                        {
                          for(int x = 0; x < (256*HR); x++)
                          {
                            digitalWrite(step2, HIGH);
                            delayMicroseconds(1000);
                            digitalWrite(step2, LOW);
                            delayMicroseconds(1000);
                          }
                        }
                        else
                        {
                          for(int x = 0; x < (256*(HR-12)); x++)
                          {
                            digitalWrite(step2, HIGH);
                            delayMicroseconds(1000);
                            digitalWrite(step2, LOW);
                            delayMicroseconds(1000);
                          }  
                        }
                      }
                  
                      if((HR > 6 && HR < 12) || (HR > 18 && HR < 24))
                      {
                        digitalWrite(dir2, LOW);
                        if(HR > 6 && HR < 12)
                        {
                          for(int x = 0; x < (256*(12-HR)); x++)
                          {
                            digitalWrite(step2, HIGH);
                            delayMicroseconds(1000);
                            digitalWrite(step2, LOW);
                            delayMicroseconds(1000);
                          }
                        }
                        else
                        {
                          for(int x = 0; x < (256*(24-HR)); x++)
                          {
                            digitalWrite(step2, HIGH);
                            delayMicroseconds(1000);
                            digitalWrite(step2, LOW);
                            delayMicroseconds(1000);
                          }  
                        }
                      }  
                      stephr = HR;
                    }
                  
                    MIN_P = MIN;
                    HR_P = HR;
                  }
                  
                  void UpdateINTime()                                            //Function to fetch IST from internet
                  {
                    timeClientIN.update();
                    HR = timeClientIN.getHours();
                    MIN = timeClientIN.getMinutes();
                    SEC = timeClientIN.getSeconds();   
                  }
                  
                  void UpdateUSTime()                                            //Function to fetch EST from internet
                  {
                    timeClientUS.update();
                    USHr = timeClientUS.getHours();
                    USMin = timeClientUS.getMinutes();
                    USSec = timeClientUS.getSeconds();   
                  
                    Serial.print("NEW JERSEY : ");
                    Serial.print(daysOfTheWeek[timeClientUS.getDay()]);
                    Serial.print(", ");
                    Serial.print(USHr); 
                    Serial.print(":"); 
                    Serial.print(USMin);
                    Serial.print(":"); 
                    Serial.println(USSec);
                  }
                  
                  void Change_INTime()                                             //Function to move the needles every minute
                  {
                    int limit1 = digitalRead(lms1);
                    int limit2 = digitalRead(lms2);
                    
                    if(MIN_P != MIN)
                    {
                      Serial.print("INDIA      : ");
                      Serial.print(daysOfTheWeek[timeClientIN.getDay()]);
                      Serial.print(", ");
                      Serial.print(HR); 
                      Serial.print(":"); 
                      Serial.print(MIN);
                      Serial.print(":"); 
                      Serial.println(SEC);
                  
                      UpdateUSTime();
                      
                      digitalWrite(dir1, HIGH);
                      if(MIN == 0)
                      {
                        for(int x = 0; x < 3500 && limit1 == 1; x++)
                        {
                          digitalWrite(step1, HIGH);
                          delayMicroseconds(1000);
                          digitalWrite(step1, LOW);
                          delayMicroseconds(1000);
                          limit1 = digitalRead(lms1);
                        }  
                      }
                      else
                      {
                        for(int x = 0; x < (51.2*(MIN-MIN_P)); ++x)
                        {
                          digitalWrite(step1, HIGH);
                          delayMicroseconds(1000);
                          digitalWrite(step1, LOW);
                          delayMicroseconds(1000);
                        }
                      
                        MIN_P = MIN;
                      }
                    }  
                  
                    if(HR_P != HR)
                    {
                      digitalWrite(dir2, HIGH);
                      if(HR == 0 || HR == 12)
                      {
                        for(int x = 0; x < 3500 && limit2 == 1; x++)
                        {
                          digitalWrite(step2, HIGH);
                          delayMicroseconds(1000);
                          digitalWrite(step2, LOW);
                          delayMicroseconds(1000);
                          limit2 = digitalRead(lms2);
                        }
                      }
                      else
                      {
                        for(int x = 0; x < (256*(HR-HR_P)); ++x)
                        {
                          digitalWrite(step2, HIGH);
                          delayMicroseconds(1000);
                          digitalWrite(step2, LOW);
                          delayMicroseconds(1000);
                        }
                      }  
                  
                      HR_P = HR;
                    }  
                  }
                  
                  void Show_USTime()                                               //Function to move the needles to show the EST
                  {
                    rotate_zero();
                    UpdateUSTime();
                  
                    int stephr = 0;
                    int stepmin = 0;
                  
                    if(stepmin != USMin)
                    {
                      digitalWrite(MS1, LOW);
                      digitalWrite(MS2, LOW);
                      digitalWrite(MS3, LOW);
                      
                      if(USMin <= 30)
                      {
                        digitalWrite(dir1, HIGH);
                        for(int x = 0; x < (51.2*USMin); x++)
                        {
                          digitalWrite(step1, HIGH);
                          delayMicroseconds(1000);
                          digitalWrite(step1, LOW);
                          delayMicroseconds(1000);
                        }
                      }  
                  
                      if(USMin > 30)
                      {
                        digitalWrite(dir1, LOW);
                        for(int x = 0; x < (51.2*(60-USMin)); x++)
                        {
                          digitalWrite(step1, HIGH);
                          delayMicroseconds(1000);
                          digitalWrite(step1, LOW);
                          delayMicroseconds(1000);
                        }  
                      }
                      stepmin = USMin;
                    }
                  
                    if(stephr != USHr)
                    {
                      digitalWrite(MS1, LOW);
                      digitalWrite(MS2, LOW);
                      digitalWrite(MS3, LOW);
                  
                      if((USHr > 0 && USHr <= 6) || (USHr > 12 && USHr <= 18))
                      {
                        digitalWrite(dir2, HIGH);
                        if(USHr > 0 && USHr <= 6)
                        {
                          for(int x = 0; x < (256*USHr); x++)
                          {
                            digitalWrite(step2, HIGH);
                            delayMicroseconds(1000);
                            digitalWrite(step2, LOW);
                            delayMicroseconds(1000);
                          }
                        }
                        else
                        {
                          for(int x = 0; x < (256*(USHr-12)); x++)
                          {
                            digitalWrite(step2, HIGH);
                            delayMicroseconds(1000);
                            digitalWrite(step2, LOW);
                            delayMicroseconds(1000);
                          }  
                        }
                      }
                  
                      if((USHr > 6 && USHr < 12) || (USHr > 18 && USHr < 24))
                      {
                        digitalWrite(dir2, LOW);
                        if(USHr > 6 && USHr < 12)
                        {
                          for(int x = 0; x < (256*(12-USHr)); x++)
                          {
                            digitalWrite(step2, HIGH);
                            delayMicroseconds(1000);
                            digitalWrite(step2, LOW);
                            delayMicroseconds(1000);
                          }
                        }
                        else
                        {
                          for(int x = 0; x < (256*(24-USHr)); x++)
                          {
                            digitalWrite(step2, HIGH);
                            delayMicroseconds(1000);
                            digitalWrite(step2, LOW);
                            delayMicroseconds(1000);
                          }  
                        }
                      }  
                      stephr = USHr;
                    }
                  }
                  
                  void Neo_IN()                                                 //Function for the neopixel to show the daylight
                  {
                    if(HR >=6 && HR <= 9)
                    {
                      for(int i=0;i<19;i++)
                      {
                        pixels.setPixelColor(i, pixels.Color( 174, 214, 241 )); // Light blue color.
                        pixels.show(); // This sends the updated pixel color to the hardware.
                      }
                    }
                    
                    if(HR >=10 && HR <= 12)
                    {
                      for(int i=0;i<19;i++)
                      {
                        pixels.setPixelColor(i, pixels.Color( 249, 231, 159 )); // Yellow color.
                        pixels.show(); // This sends the updated pixel color to the hardware.
                      }
                    
                    }  
                  
                    if(HR >=13 && HR <= 16)
                    {
                      for(int i=0;i<19;i++)
                      {
                        pixels.setPixelColor(i, pixels.Color( 250, 215, 160 )); // Orange color.
                        pixels.show(); // This sends the updated pixel color to the hardware.
                      }
                    }  
                  
                    if(HR >=17 && HR <= 19)
                    {
                      for(int i=0;i<19;i++)
                      {
                        pixels.setPixelColor(i, pixels.Color(247,220,143)); // Moderately dark blue color.
                        pixels.show(); // This sends the updated pixel color to the hardware.
                      }
                    }  
                  
                    if(HR >=20 && HR <= 22)
                    {
                      for(int i=0;i<19;i++)
                      {
                        pixels.setPixelColor(i, pixels.Color( 36, 113, 163 )); // Dark blue color.
                        pixels.show(); // This sends the updated pixel color to the hardware.
                      }
                    }
                    
                    else
                    {
                      pixels.clear();  
                    }
                  }
                  
                  void Presence()                                               //Function for the ultrasonic sensor
                  {
                    long duration, distance;
                  
                    digitalWrite(trigPin, LOW);
                    delayMicroseconds(2);
                    digitalWrite(trigPin, HIGH);
                    delayMicroseconds(10);
                    duration = pulseIn(echoPin, HIGH);
                    distance = (duration/2) / 29.1;
                    Serial.println(distance);
                    delay(1000);
                    
                    if(distance > 20 && distance <=50)
                    {
                      Serial.println("Human within 10cm");
                      Show_USTime();
                      delay(10000);
                      Set_Needle(); 
                    }
                  
                    else if(distance > 100)
                    {
                      if(HR < 6 && HR >= 23)
                      {
                        Serial.println("Human within 30cm");
                        Night_light();   
                      }
                    }  
                  }
                  
                  void Night_light()                                         //Function to turn ON the night light
                  {
                    for(int i=0;i<19;i++)
                    {
                      pixels.setPixelColor(i, pixels.Color(255, 244, 229)); // warm light color.
                      pixels.show(); // This sends the updated pixel color to the hardware.
                    }
                    delay(10000);
                    pixels.clear();
                  }
                

System Integration

After manufacturing all the required parts, I assembled all the parts together.


Glued the neopixel strip around the clock


Connected all the wires to the board.


Screwed the Circuit Board on to the casing.


Now the clock is ready.


Machines and Tools used

  • ShopBot
  • 3D printer
  • Laser cutter
  • Sand Blaster
  • Roland MDX 20
  • Soldering

Softwares used

  • Autodesk Fusion 360
  • Vcarve
  • Inkscape
  • GIMP
  • Autodesk Eagle
  • Cure Slicer
  • MODS
  • Arduino


Design Files



Licence

Creative Commons License
Time Keeper by Anooj Jacob is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.


Credits and Thanks

I take this oppertunity to thank our Lab Instructors Yadhu Sharon, Jogin Francis and Joel George Alex for their constant support and advise through out the academy, And big thanks and hugs to my collegues Tom, Eldho, Nanditha and Nibin who where there always with me brainstorming new ideas and possiblites and constantly behind the works I did. I would also like to thank the Fab Academy Team for letting me join the academy that have showed me the new possiblities in Digital Fabrication.