Project 07 - A Modern Style Analog Clock

<< Click to Display Table of Contents >>

Navigation:  Projects >

Project 07 - A Modern Style Analog Clock

This project draws a modern style analog clock using the CleO primitives. It uses the ds3231Timer and Wire CleO library to interact with the PCF8523T RTC component.

 

 

Component(s)

 

This project uses the following component(s) -

 

PCF8523T RTC

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

 

 

Wiring Diagram

 

 

Project 07-Modern Style 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 be 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();

 

 CleO.SetBackgroundcolor(BGHEAVY);

 

 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);

}

 

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

 

void loop() {

 touchControl();

 updateTimer();

}

 

Screen frames are created using the Start() and Show() routines. "Clock" string is drawn at the top of the screen. The outer circle is drawn first followed by drawing the four digits. Using the Line R1R2() command another circle is drawn. The second hand is drawn using the commands NeedleR1R2() and CircleExt(). Finally, the hour and minutes hands are drawn using the LineR1R2() command.

 

The updateTimer() routine checks if software timer should be used or not. Once updated values of hour and seconds are received, it proceeds for displaying clock. The following sample code snippet shows the above operations in sequence -

 

void updateTimer() {

 int f = FONT_SANS_0;     // Font for numbers

 int cx = CLOCK_CENTRE_X, cy = CLOCK_CENTRE_Y;   // Clock center

 int r0 = 110, r1 = 125, r2 = 140;

 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);

 }

 

/* update speaking time */

 speak_hour = h, speak_min = m;

 

 currentElapsedTime = millis();

 

 int hour_degrees = (360L * ((h + 6) % 12)) / 12;

 int min_degrees = 180 + (360L * m) / 60;

 int sec_degrees = 180 + (360L * (currentElapsedTime % 60000L)) / 60000L;

 

 CleO.Start();

 

// Top menu

 CleO.Tag(2);

 CleO.StringExt(FONT_SANS_5, CLOCK_CENTRE_X, 30, WHITE, MM, 0, 0, "Clock");

 

 // Outer ring

 CleO.CircleExt(cx, cy, r2, r2 + 0.5, WHITE, MM, 0, 0);

 

 // gaps for the four numbers

 for (int a = 0; a < 360; a += 90)

   CleO.CircleExt(cx, cy, 0, 10, BGHEAVY, MM, a, r2);

 

 CleO.StringExt(f, cx, cy, WHITE, MM, 270, r2, "3");

 CleO.StringExt(f, cx, cy, WHITE, MM, 0, r2, "6");

 CleO.StringExt(f, cx, cy, WHITE, MM, 90, r2, "9");

 CleO.StringExt(f, cx, cy, WHITE, MM, 180, r2, "12");

 

// Outside rim

 for (int deg = 0; deg < 360; deg += 2) {

   int age = (360 + sec_degrees - deg) % 360;

   float fade = 1.2 - (age / 64.);

   CleO.LineWidth(max(0.25, min(1.0, fade)));

   CleO.LineR1R2(cx, cy, r0, r1, deg);

 }

 

// Draw the seconds hand as a small triangle

 CleO.NeedleWidth(12);

 CleO.NeedleR1R2(cx, cy, r0 - 15, r0 - 2, sec_degrees);

 CleO.CircleExt(cx, cy, 0, r0 - 15, BGHEAVY, MM, 0, 0);

 

// Hour and minute hands

 CleO.LineWidth(2.0);

 CleO.LineR1R2(cx, cy, 6, 2 * r0 / 3, hour_degrees);

 CleO.LineWidth(1.5);

 CleO.LineR1R2(cx, cy, 6, 4 * r0 / 5, min_degrees);

 

// Dial center knob

 CleO.CircleExt(cx, cy, 0, 6, WHITE, MM, 0, 0);

 CleO.CircleExt(cx, cy, 0, 3, BGHEAVY, MM, 0, 0);

 

 /* Draw play icon */

 CleO.Tag(100);

 CleO.Bitmap(icon_play[playing], 10, 360);

 

/* Draw setting icon */

 CleO.Tag(101);

 CleO.Bitmap(icon_setting, SCREEN_WIDTH - 60, 360);

 

 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 the various sounds are played 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);

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

     startTime = millis();

     t0 = t1;

   }

 }

}

 

 

Output

 

Project 7 - A Modern Style Analog Clock