Darkky Minecraft Server Forum

Full Version: DS3231 calendar clock with hourly chime
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I made a clock for my parents, and I decided to post the code here.
Required hardware:
  • DS3231 real-time clock
  • LCD 16x2 display (one that does not use I2C)
  • A speaker or buzzer for the chimes
  • EDIT: And of course an Arduino.
  • EDIT: You will also need a few resistors to hook everything up properly.
Optional hardware:
  • A toggle switch (to turn Daylight Saving Time on and off)
What it does:
  • Displays the date (month/day/year) and time (12 hour format)
  • Calculates and displays the day of the week and week number
  • Chimes every hour, on the hour (Westminster Chimes)
  • When the Daylight Saving Time switch is turned on, it adds 1 hour to the time to be displayed and chimed
What it doesn't do:
  • Allow you to set the time manually (you have to re-upload the sketch for that)
The code:

Code:
#include "Wire.h"
#include <LiquidCrystal.h>

// Make sure your pin numbers match these!
//                RS  EN  D4  D5  D6  D7
LiquidCrystal lcd( 7,  6,  5,  4,  3,  2);
const byte DST_SWITCH_PIN = 8;
const byte SPEAKER_PIN = 9;

// variables for the current date and time
byte yy=0, mo=1, dd=0, wd=6;
byte hh=0, mi=0, ss=0;
byte hhTwelve = 12;
byte wn=52;

// other helpful variables
bool gotTheTime = false;
byte old_ss = 99, halfSec = 198, old_halfSec = 198;
unsigned long microsNow = 0UL;
unsigned long microsAtLastSecond = 0UL;
bool dstOn = false, old_dstOn = false;

// a buffer for text to be displayed
char buf[20] = "";

void setup() {
 pinMode(DST_SWITCH_PIN, INPUT_PULLUP);
 pinMode(SPEAKER_PIN, OUTPUT);
 
 Wire.begin();
 lcd.begin(16, 2);
 
 // BEGINNING of code for setting the date and time
 
 // If you wish to set the date and time,
 // uncomment the following:
 
 /*
 // code to precisely set the external real-time clock
 Wire.beginTransmission(0x68); // address DS3231
 Wire.write(0x00); // select register
 // NOTE: before you run this code, you *must*
 // change the following numbers to the correct time!
 // (plus a few seconds to allow for compilation, etc.)
 Wire.write(numberToBcd( 0)); // seconds
 Wire.write(numberToBcd(21)); // minutes
 Wire.write(numberToBcd( 1)); // hours (use 24-hour format)
 Wire.write(numberToBcd( 6)); // day of week (I use Mon=1 .. Sun=7)
 Wire.write(numberToBcd( 9)); // day of month
 Wire.write(numberToBcd( 4)); // month
 Wire.write(numberToBcd(22)); // year (use only two digits)
 Wire.endTransmission();
 */
 
 // END of code for setting the date and time

 /*
 // define special characters for single cell numerals 10 through 12
 byte singleCellTen[]    = { 18, 21, 21, 21, 21, 21, 18,  0 };
 byte singleCellEleven[] = {  9, 27,  9,  9,  9,  9,  9,  0 };
 byte singleCellTwelve[] = { 22, 21, 17, 18, 20, 20, 23,  0 };
 lcd.createChar(10, singleCellTen);
 lcd.createChar(11, singleCellEleven);
 lcd.createChar(12, singleCellTwelve);
 */
 
 // play a short tone (for testing the speaker)
 tone(SPEAKER_PIN, 1000, 500);
 
 // display a demo pattern (for testing the display)
 lcd.setCursor(0, 0); // go to beginning of top line
 lcd.print("  Display test  ");
 lcd.setCursor(0, 1); // go to beginning of bottom line
 lcd.print("0123456789 (^_^)");

 for (int i = 5; i >= 1; i--) { // countdown from 5 to 1
   lcd.setCursor(0, 0); // go to beginning of top line
   lcd.print((char)('0' + i)); // print the digit
   lcd.setCursor(15, 0); // go to end of top line
   lcd.print((char)('0' + i)); // print the digit again
   delay(998);
 }
}

void loop() {
 // first, we (try to) read the time from the RTC
 // send request to receive data starting at register 0
 Wire.beginTransmission(0x68); // 0x68 is DS3231 device address
 Wire.write((byte)0); // start at register 0
 Wire.endTransmission();
 Wire.requestFrom(0x68, 7); // request seven bytes

 gotTheTime = false;
 while(Wire.available())
 {
   ss = bcdToNumber(Wire.read()); // get seconds
   mi = bcdToNumber(Wire.read()); // get minutes
   hh = bcdToNumber(Wire.read()); // get hours
   Wire.read(); // discard the day of the week (we will calculate it ourself)
   dd = bcdToNumber(Wire.read()); // get day of month
   mo = bcdToNumber(Wire.read()); // get month
   yy = bcdToNumber(Wire.read()); // get year
   gotTheTime = true;
 }

 microsNow = micros();
 
 // read the Daylight Saving Time on/off switch
 // NOTE: because we are using INPUT_PULLUP, LOW means on, and HIGH means off
 dstOn = (digitalRead(DST_SWITCH_PIN) == LOW);
 
 // adjust for Daylight Saving Time if applicable
 if (dstOn) {
   hh++;
   if (hh >= 24) {
     hh -= 24;
     dd++;
     if (dd > daysInMonth(yy, mo)) {
       dd = 1;
       mo++;
       if (mo > 12) {
         mo = 1;
         yy++;
       }
     }
   }
 }
 
 // try to figure out which half-second we are in
 // (this is important to making the striking work properly)
 if (ss != old_ss) microsAtLastSecond = microsNow;
 halfSec = ss * 2;
 if ((microsNow - microsAtLastSecond) >= 500000UL) halfSec++;
 
 // calculate day of the week
 wd = ymdToWeekday(yy, mo, dd);

 // calculate week number  
 wn = ymdToWeekNumber(yy, mo, dd);
 
 // convert hour to 12-hour format
 hhTwelve = hh;
 if (hhTwelve > 12) {
   hhTwelve -= 12;
 }
 if (hhTwelve == 0) {
   hhTwelve = 12;
 }
 
 if (gotTheTime) {
   // only if we have successfully read the time
   // do we then attempt to indicate the time
   
   if (halfSec != old_halfSec) { // do this only once every half-second
     // see if it is time for the clock to strike
     if (mi == 0) {  // strike on the hour, i.e. when minutes are 0
       if (halfSec < 26) {
         // play the Westminster Chimes
         switch (halfSec) {
           case 0:  tone(SPEAKER_PIN, 330, 420); break;
           case 1:  tone(SPEAKER_PIN, 415, 420); break;
           case 2:  tone(SPEAKER_PIN, 370, 420); break;
           case 3:  tone(SPEAKER_PIN, 247, 735); break;
           case 6:  tone(SPEAKER_PIN, 330, 420); break;
           case 7:  tone(SPEAKER_PIN, 370, 420); break;
           case 8:  tone(SPEAKER_PIN, 415, 420); break;
           case 9:  tone(SPEAKER_PIN, 330, 735); break;
           case 12: tone(SPEAKER_PIN, 415, 420); break;
           case 13: tone(SPEAKER_PIN, 330, 420); break;
           case 14: tone(SPEAKER_PIN, 370, 420); break;
           case 15: tone(SPEAKER_PIN, 247, 735); break;
           case 18: tone(SPEAKER_PIN, 247, 420); break;
           case 19: tone(SPEAKER_PIN, 370, 420); break;
           case 20: tone(SPEAKER_PIN, 415, 420); break;
           case 21: tone(SPEAKER_PIN, 330, 735); break;
           default: break;
         }
       }
       else if ((halfSec < (26 + 3 * hhTwelve)) && ((halfSec % 3) == 2)) {
         // bong the hours
         tone(SPEAKER_PIN, 415, 750);
       }
     }
   }
   
   if ((ss != old_ss) || (dstOn != old_dstOn)) { // only once every second
     // update the display to show the current date and time
     
     // build a string of text containing the weekday and the full date
     // (Hint: this code makes more sense if you read it vertically)
     buf[0]  = "BMTWTFSS"[wd];
     buf[1]  = "aouehrau"[wd];
     buf[2]  = "dneduitn"[wd];
     buf[3]  = ' ';
     buf[4]  = ' ';
     buf[5]  = ' ';
     buf[6]  = '0' + (mo/10);
     buf[7]  = '0' + (mo%10);
     buf[8]  = '/';
     buf[9]  = '0' + (dd/10);
     buf[10] = '0' + (dd%10);
     buf[11] = '/';
     buf[12] = '2';
     buf[13] = '0';
     buf[14] = '0' + (yy/10);
     buf[15] = '0' + (yy%10);
     buf[16] = 0;
     if (buf[6] == '0') buf[6] = ' ';
     // display the weekday and full date on the top line
     lcd.setCursor(0, 0); // move to beginning of top line
     lcd.print(buf); // print the text to the display
     
     // build a string of text containing the week number and the time
     buf[0]  = 'W';
     buf[1]  = 'k';
     buf[2]  = '0' + (wn/10);
     buf[3]  = '0' + (wn%10);
     buf[4]  = ' ';
     buf[5]  = ' ';
     buf[6]  = '0' + (hhTwelve/10);
     buf[7]  = '0' + (hhTwelve%10);
     buf[8]  = ':';
     buf[9]  = '0' + (mi/10);
     buf[10] = '0' + (mi%10);
     buf[11] = ':';
     buf[12] = '0' + (ss/10);
     buf[13] = '0' + (ss%10);
     buf[14] = ((hh<12) ? 'a' : 'p');
     buf[15] = 'm';
     buf[16] = 0;
     if (buf[6] == '0') buf[6] = ' ';
     // display the week number and the time on the bottom line
     lcd.setCursor(0, 1); // move to beginning of bottom line
     lcd.print(buf); // print the text to the display
   }  
 }
 
 else {
   // if we have failed to read the time,
   // then we will end up inside this "else"
   
   // indicate failure to read the time
   lcd.setCursor(0, 0); // go to beginning of top line
   lcd.print("Error:          ");
   lcd.setCursor(0, 1); // go to beginning of bottom line
   lcd.print("Can\'t read time ");
 }
These help me plan our activities out carefully and not miss anything
I like the idea and will use it. However, my parents are far from modern technology, so they use a wall-mounted calendar. I hope that someday I can teach them, because it will make their lives easier.