Project 06 - A Traditional Analog Clock

<< Click to Display Table of Contents >>

Navigation:  Projects >

Project 06 - A Traditional Analog Clock

This project demonstrates drawing a traditional analog clock using strings, needles and line segments.The Wire Arduino library is used to interact with the RTC hardware to read the time and update the clock.

 

 

Component(s)

 

This project uses the following component(s) -

 

PCF8523T RTC

56mm, 80hm 0.5W(RMS) / 1W(Peak) Internal CleO speaker

 

 

Wiring Diagram

 

 

Project 06-Traditional Analog Clock

 

 

Code

 

The following code shows how the setup() routine checks for the Oscillator Stop Flag (OSF) in PCF8523T RTC component. Based on the OSF flag, it shows the SetDateTime() widget or continues with the current date and time value from the RTC hardware. It then loads the play and setting icons. If the RTC is not present, then it uses the software timer to display the selected date and time.

 

The 1-wire library is first initialized to interact with the PCF8523T RTC component. Later the SetDateTime() widget is invoked which prompts the users to select the date and time. This information will is stored in the epoch time. The Epoch time is converted to year, month, day of month, hour, minute, second and day of week using the EpochToDate() routine.

 

void setup() {

Serial.begin(115200);

 

 #ifdef _VARIANT_ARDUINO_DUE_X_

   Wire1.begin(); // Shield I2C pins connect to alt I2C bus on Arduino Due

 #else

   Wire.begin();

 #endif

 

 if (rtc.begin() == -1) // RTC is not detected

   software_timer = true;

 

/* Initialize CleO */

 CleO.begin();

 int16_t s = 0, m = 0, h = 0, dayOfWeek = 0, dayOfMonth = 0, mon = 0, y = 0;

 if (software_timer == true) {

   CleO.SetDateTime("Date/Time", t0);

   CleO.EpochToDate(t0, y, mon, dayOfMonth, h, m, s, dayOfWeek);

  }

 

else if (!rtc.isrunning() || rtc.checkOSF() == 1) {

   CleO.SetDateTime("Date/Time", t0);

   CleO.EpochToDate(t0, y, mon, dayOfMonth, h, m, s, dayOfWeek);

   rtc.set_time(y, mon + 1, dayOfMonth, h, m, s, dayOfWeek);

 }

 else { /* RTC is running fine */

   rtc.get_time(y, mon, dayOfMonth, h, m, s, dayOfWeek);

   CleO.DateToEpoch(t0, y, mon - 1, dayOfMonth, h, m, s);

 }

 

 startTime = millis();

 

 icon_play[0] = CleO.LoadIcon("@Icons/m48.ftico", ICON_PLAY_CIRCLE_OUTLINE);

 icon_play[1] = CleO.LoadIcon("@Icons/m48.ftico", ICON_PLAY_CIRCLE_FILLED);

 icon_setting = CleO.LoadIcon("@Icons/m48.ftico", ICON_SETTINGS);

 

/* Set screen coordinate in portrait mode */

 CleO.DisplayRotate(2, 0);

}

 

The loop routine() checks for touch events and then updates the display based on the date and time values.

 

void loop() {

 touchControl();

 updateTimer();

}

 

The updateTimer() routine checks if the software timer should be used or not. Once the updated values of hour and seconds are received, it displays the seven segment fonts and icons.

 

void updateTimer() {

 int16_t s, m, h, dayOfWeek, dayOfMonth, mon, y;

 

 if (software_timer == false) {

rtc.get_time(y, mon, dayOfMonth, h, m, s, dayOfWeek);

 else {

   elapsedTime = millis() - startTime;

   CleO.EpochToDate(t0 + elapsedTime / 1000L, y, mon, dayOfMonth, h, m, s, dayOfWeek);

   mon += 1; /* CleO.EpochToDate() gives January month as 0 */

   dayOfWeek += 1; /* CleO.EpochToDate() gives Sunday as 0 */

 }

 

 /* Update speaking time */

 speak_hour = h, speak_min = m;

 

/* Start drawing the screen shot */

 CleO.Start();

 

/* Draw hour, min and sec with digits */

 drawDigitalClock(h, m, s);

 

/* Analog clock will need 12 hours format */

 if (h > 12)

   h -= 12;

 

 /* Draw date with digits by passing day of week, day of month, month and year values */

 drawDate(dayOfWeek, dayOfMonth, mon, y);

 

 /* Draw analog watch with hour, min and second hands */

 drawClock(h, m, s);

 

 /* Draw play icon */

 CleO.Tag(100);

CleO.Bitmap(icon_play[playing], DATE_TIME_X - 170, SCREEN_HEIGHT - 150);

 

 /* Draw setting icon */

 CleO.Tag(101);

 CleO.Bitmap(icon_setting, DATE_TIME_X + 130, SCREEN_HEIGHT - 150);

 

/* Show the screenshot */

 CleO.Show();

}

 

The touchControl() routine checks for the play or setting button press events. This project has the capabilities of presenting the time as sound. The sound plays the current time displayed on the screen. On pressing the play icon, it plays the WAV files stored on the device based on the hour and minutes value. For various values of hour and minute, the AudioPlay() command is used to play the relevant WAV files. The following code snippet shows how to play the different sounds on touch -

 

/* UpdateTimer() routine is called to update UI while speaking time */

void processTags(int16_t tag) {

 char buf[40];

 if (tag == 100) {

   CleO.AudioPlay("@Music/Sounds/TheTimeIsNow.wav", PLAY_WAIT);

   updateTimer();

 

   if (speak_hour > 12)

     speak_hour -= 12;

 

   if (speak_hour == 0 || speak_hour == 12)

     CleO.AudioPlay("@Music/Sounds/12.wav", PLAY_WAIT);

   else {

     sprintf(buf, "@Music/Sounds/%d.wav", speak_hour);

     CleO.AudioPlay(buf, PLAY_WAIT);

   }

 

   updateTimer();

   CleO.AudioPlay("@Music/Sounds/oClock.wav", PLAY_WAIT);

   updateTimer();

 

   if (speak_min > 0) {

     if (speak_min < 21 || speak_min == 30 || speak_min == 40 || speak_min == 50) {

       sprintf(buf, "@Music/Sounds/%d.wav", speak_min);

       CleO.AudioPlay(buf, PLAY_WAIT);

       updateTimer();

     } else {

       if (speak_min < 30)

         CleO.AudioPlay("@Music/Sounds/20.wav", PLAY_WAIT);

       else if (speak_min < 40)

         CleO.AudioPlay("@Music/Sounds/30.wav", PLAY_WAIT);

       else if (speak_min < 50)

         CleO.AudioPlay("@Music/Sounds/40.wav", PLAY_WAIT);

       else

         CleO.AudioPlay("@Music/Sounds/50.wav", PLAY_WAIT);

 

       updateTimer();

       sprintf(buf, "@Music/Sounds/%d.wav", speak_min % 10);

       CleO.AudioPlay(buf, PLAY_WAIT);

       updateTimer();

     }

 

     CleO.AudioPlay("@Music/Sounds/minutes.wav", PLAY_WAIT);

     updateTimer();

   }

 } else if (tag == 101) { /* Setting button is pressed */

   int16_t s = 0, m = 0, h = 0, dayOfWeek = 0, dayOfMonth = 0, mon = 0, y = 0;

   elapsedTime = (millis() - startTime) / 1000L;

   uint32_t t1 = t0 + elapsedTime;

   int16_t ret = CleO.SetDateTime("Date/Time", t1);

   if (0 == ret) {

     CleO.EpochToDate(t1, y, mon, dayOfMonth, h, m, s, dayOfWeek);

     setDS3231time(s, m, h, dayOfWeek, dayOfMonth, mon, y);

     startTime = millis();

     t0 = t1;

   }

 }

}

 

The following code snippet shows how to draw the time in digital style at the bottom of the screen:

 

void drawDigitalClock(int h, int m, int sec) {

 char buffer[10];

 sprintf(buffer, "%02d:%02d:%02d", h, m, sec);

 

/* Draw time string */

 CleO.StringExt(FONT_SANS_5, DATE_TIME_X, SCREEN_HEIGHT - 50, WHITE, MM, 0, 0, buffer);

}

 

Similarly, the date is drawn on top of the screen using the following code snippet:

 

void drawDate(int dayOfWeek, int dayOfMonth, int mon, int y) {

 

  /* Convert bytes to strings */

 char strDayOfWeek[][10] = {"", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };

 

 /* Prepare display string */

 char buffer[30];

 sprintf(buffer, "%s %02d/%02d/%02d", strDayOfWeek[dayOfWeek], dayOfMonth, mon, y);

 

/* Draw date string */

 CleO.StringExt(FONT_SANS_5, DATE_TIME_X, 80, WHITE, MM, 0, 0, buffer);

}

 

The primary clock is drawn using the following routine:

 

void drawClock(int h, int m, int sec) {

 CleO.PointExt(CENTRE_X, CENTRE_Y, RADIUS, WHITE, 0, 0);

 drawClockDigits();

 drawClockMinuteLines();

 drawClockHands(h, m, sec);

}

 

 

Output

 

Project06-ATraditionalAnalogClock