Three Zone Clock
| |||
/************************************************************** Name : Three Time Zone Clock Author : Jack Date : 16 May 2016 Repo : https://github.com/dhakajack/TimeZoneClock License : MIT License, (c) 2016 Jack Welch; see LICENSE file RTClib from Adafruit /**************************************************************** #include#include "RTClib.h" //Adafruit RTClib library for DS3231 //************************* DEFINES ************************************ //************************* PROTOTYPES ************************************ void initNumber(); void fixPeriod( int *unitval, int maxval); void blankBlinky( int units ); void stuffBuffer( int pattern); void clearBuffer(); void displayDelay(); void updateDisplay(); void updateTime(); //************************* VARIABLES ************************************ RTC_DS3231 rtc; //switches - all are active low #define SLEEP_WAKE 0 #define SET_HOURS 1 #define SET_MINUTES 2 #define SET_ZONE2 3 #define SET_ZONE3 4 #define EDIT_MODE 5 //control lines for shift registers #define SER 7 #define SRCK 8 #define RCK 9 //lines controlling individual segments; switched through PNP, so active low #define SEG_E 10 #define SEG_F 11 #define SEG_G 12 #define SEG_DP 13 // dpi isn't used but is retained for future use #define SEG_A 14 #define SEG_B 15 #define SEG_C 16 #define SEG_D 17 // SDA a4/d18/pin27 // SCL a5/d19/pin28 //index values for each segment #define TOP 0 // A #define UPPER_R 1 // B #define LOWER_R 2 // C #define BOTTOM 3 // D #define LOWER_L 4 // E #define UPPER_L 5 // F #define MIDDLE 6 // G #define DECIMAL 7 // dp - although dp isn't used for the clock, it is wired up //index values for zone #define ZONE1 0 #define ZONE2 1 #define ZONE3 2 //all times in milliseconds #define REFRESH_TIME 500 // ms, clock refresh and blink rate #define RESPONSE_TIME 300 // how often to respond to button presses #define BLINK_TIME 100 // how fast to blink #define DELAY_TIME 1 // 1-2 seems to work well, with low flicker //rollover values #define HOURS_MAX 24 #define MINUTES_MAX 60 const int clock_address = 0x68; // I2C write address for clock const int clock_statusRegister = 0x0F; //digital outputs driving the respective segments int segments[8] = {SEG_A,SEG_B,SEG_C,SEG_D,SEG_E,SEG_F,SEG_G,SEG_DP}; boolean number[11][7]; // holds information about which segments are needed for each of the 10 numbers // holds values of the display digits - ZONE1, ZONE2, ZONE3 int digits[3][4]; int hour; // reference time, zone 1 int minute; // same for all time zones; would need modification to accommodate fractional time zones, e.g., India int editZone = ZONE1; int offset[3]; // offet[timezone] boolean editMode = false; boolean sleepMode = false; boolean blinkStatus = false; unsigned long lastRefresh = 0; unsigned long lastResponse = 0; unsigned long lastBlink = 0; /*F******************************************************************** * **********************************************************************/ void setup() { rtc.begin(); if( rtc.lostPower()) rtc.adjust( DateTime( F(__DATE__), F(__TIME__))); pinMode( RCK, OUTPUT ); pinMode( SRCK, OUTPUT ); pinMode( SER, OUTPUT ); pinMode( SLEEP_WAKE,INPUT_PULLUP ); pinMode( SET_MINUTES,INPUT_PULLUP ); pinMode( SET_HOURS,INPUT_PULLUP ); pinMode( SET_ZONE2,INPUT_PULLUP ); pinMode( SET_ZONE3,INPUT_PULLUP ); pinMode( EDIT_MODE,INPUT_PULLUP ); for( int segIndex = 0; segIndex < 8; segIndex++ ) { pinMode( segments[segIndex], OUTPUT); digitalWrite( segments[segIndex], HIGH );// turn off, segs driven by high-side PNP initNumber(); } offset[ZONE1] = 0; //UTC /*adjust these +/- 23h */ offset[ZONE2] = 3; //mada offset[ZONE3] = -4; //Eastern } /*F******************************************************************** * **********************************************************************/ void loop() { DateTime now; now = rtc.now(); //snapshot of the current time - one atomic read for hour, minutes, etc. if( (millis() - lastRefresh) >= REFRESH_TIME) { // how often to update the display lastRefresh = millis(); if( !editMode) { hour = now.hour(); minute = now.minute(); } } if( (millis() - lastResponse) >= RESPONSE_TIME) { // button presses are relatively slow, so not specifically debounced if( digitalRead( EDIT_MODE ) == LOW) { editMode = true; sleepMode = false; } else editMode = false; if( editMode ) { if( digitalRead( SET_HOURS) == LOW) { // bumping zone one up, bumps up all the other zones as well editZone = ZONE1; hour++; fixPeriod( &hour, HOURS_MAX); } else if( digitalRead( SET_MINUTES) == LOW) { // minutes are the same across all zones editZone = ZONE1; minute++; fixPeriod( &minute,MINUTES_MAX); } else if( digitalRead( SET_ZONE2) == LOW) { // adjust hours only for zone 2 editZone = ZONE2; offset[ZONE2]++; fixPeriod( &offset[ZONE2],HOURS_MAX); } else if( digitalRead( SET_ZONE3) == LOW) { // adjust hours only for zone 3 offset[ZONE3]++; editZone = ZONE3; fixPeriod( &offset[ZONE3], HOURS_MAX); } rtc.adjust( DateTime( 2016, 1, 1, hour, minute, 0)); // dummy vals for year, month, day } if( !editMode ) { if( digitalRead( SLEEP_WAKE) == LOW) // don't want sleep/wake edit mode sleepMode = !sleepMode; } lastResponse = millis(); } updateTime(); // stick new time in the zone arrays if( (millis() - lastBlink) >= BLINK_TIME) { blinkStatus = !blinkStatus; lastBlink = millis(); } if( editMode && blinkStatus) blankBlinky( editZone); if( !sleepMode ) updateDisplay(); } /*F******************************************************************** * **********************************************************************/ void updateTime() { int temphr; int tempmintens; int tempminones; tempmintens = ( minute / 10 ) % 10; tempminones = minute %10; for( int zone =ZONE1; zone < ZONE3 + 1; zone++) { temphr = hour + offset[zone]; fixPeriod( &temphr,HOURS_MAX); digits[zone][0] = (temphr/10) %10; digits[zone][1] = temphr %10; digits[zone][2] = tempmintens; digits[zone][3] = tempminones; } } /*F******************************************************************** * **********************************************************************/ void updateDisplay() { int reg; //register for( int segIndex = 0; segIndex < 7; segIndex++) { reg = 0; clearBuffer(); // blanks the display digitalWrite(segments[segIndex],LOW); for( int units = ZONE3; units > -1; units--) { for( int index = 3; index > -1; index--) { reg = reg << 1; if( number[digits[units][index]][segIndex]) reg += 1; // set up shift register pattern } } stuffBuffer( reg ); displayDelay(); digitalWrite( segments[segIndex],HIGH); // turn off segment } } /*F******************************************************************** * **********************************************************************/ void displayDelay() { delay( DELAY_TIME ); // time to persist after lighting up a segment } /*F******************************************************************** * **********************************************************************/ void clearBuffer() { digitalWrite( RCK, LOW); shiftOut( SER, SRCK, MSBFIRST, 0); shiftOut( SER, SRCK, MSBFIRST, 0); digitalWrite( RCK, HIGH); } /*F******************************************************************** * **********************************************************************/ void stuffBuffer( int pattern) { digitalWrite( RCK, LOW); shiftOut( SER, SRCK, MSBFIRST, pattern >> 8); shiftOut( SER, SRCK, MSBFIRST, pattern & 255); digitalWrite( RCK, HIGH); } /*F******************************************************************** * **********************************************************************/ void blankBlinky( int units ) { digits[units][0] = digits[units][1] = digits[units][2] = digits[units][3] = 10; } /*F******************************************************************** * **********************************************************************/ void fixPeriod( int *unitval, int maxval) { if( *unitval > (maxval - 1)) *unitval -= maxval; if( *unitval < 0) *unitval += maxval; } /*F******************************************************************** * **********************************************************************/ void initNumber() { number[0][MIDDLE] = false; number[0][UPPER_L] = true; number[0][LOWER_L] = true; number[0][BOTTOM] = true; number[0][LOWER_R] = true; number[0][UPPER_R] = true; number[0][TOP] = true; number[1][MIDDLE] = false; number[1][UPPER_L] = false; number[1][LOWER_L] = false; number[1][BOTTOM] = false; number[1][LOWER_R] = true; number[1][UPPER_R] = true; number[1][TOP] = false; number[2][MIDDLE] = true; number[2][UPPER_L] = false; number[2][LOWER_L] = true; number[2][BOTTOM] = true; number[2][LOWER_R] = false; number[2][UPPER_R] = true; number[2][TOP] = true; number[3][MIDDLE] = true; number[3][UPPER_L] = false; number[3][LOWER_L] = false; number[3][BOTTOM] = true; number[3][LOWER_R] = true; number[3][UPPER_R] = true; number[3][TOP] = true; number[4][MIDDLE] = true; number[4][UPPER_L] = true; number[4][LOWER_L] = false; number[4][BOTTOM] = false; number[4][LOWER_R] = true; number[4][UPPER_R] = true; number[4][TOP] = false; number[5][MIDDLE] = true; number[5][UPPER_L] = true; number[5][LOWER_L] = false; number[5][BOTTOM] = true; number[5][LOWER_R] = true; number[5][UPPER_R] = false; number[5][TOP] = true; number[6][MIDDLE] = true; number[6][UPPER_L] = true; number[6][LOWER_L] = true; number[6][BOTTOM] = true; number[6][LOWER_R] = true; number[6][UPPER_R] = false; number[6][TOP] = true; number[7][MIDDLE] = false; number[7][UPPER_L] = false; number[7][LOWER_L] = false; number[7][BOTTOM] = false; number[7][LOWER_R] = true; number[7][UPPER_R] = true; number[7][TOP] = true; number[8][MIDDLE] = true; number[8][UPPER_L] = true; number[8][LOWER_L] = true; number[8][BOTTOM] = true; number[8][LOWER_R] = true; number[8][UPPER_R] = true; number[8][TOP] = true; number[9][MIDDLE] = true; number[9][UPPER_L] = true; number[9][LOWER_L] = false; number[9][BOTTOM] = true; number[9][LOWER_R] = true; number[9][UPPER_R] = true; number[9][TOP] = true; //the value 10 is treated as a blank number[10][MIDDLE] = false; number[10][UPPER_L] = false; number[10][LOWER_L] = false; number[10][BOTTOM] = false; number[10][LOWER_R] = false; number[10][UPPER_R] = false; number[10][TOP] = false; }