@@ -112,7 +112,7 @@ const word optsMax[] = {0, 2, 2, 2, 1,50, 1, 6, 4,60,999, 2,1439,1439, 2, 6, 6,1
112112DS3231 ds3231; // an object to access the ds3231 specifically (temp, etc)
113113RTClib rtc; // an object to access a snapshot of the ds3231 rtc via now()
114114DateTime tod; // stores the now() snapshot for several functions to use
115- // byte toddow; //stores the day of week snapshot as now() doesn't capture it (bug report? TODO)
115+ byte toddow; // stores the day of week as calculated from tod
116116
117117// Hardware inputs and value setting
118118// AdaEncoder mainRot;
@@ -340,6 +340,7 @@ void ctrlEvt(byte ctrl, byte evt){
340340 ds3231.setYear (fnSetValDate[0 ]%100 ); // TODO: do we store century on our end? Per ds3231 docs, "The century bit (bit 7 of the month register) is toggled when the years register overflows from 99 to 00."
341341 ds3231.setMonth (fnSetValDate[1 ]);
342342 ds3231.setDate (fnSetVal);
343+ ds3231.setDoW (dayOfWeek (fnSetValDate[0 ],fnSetValDate[1 ],fnSetVal));
343344 clearSet (); break ;
344345 default : break ;
345346 } break ;
@@ -464,6 +465,7 @@ void initEEPROM(){
464465 ds3231.setYear (18 );
465466 ds3231.setMonth (1 );
466467 ds3231.setDate (1 );
468+ ds3231.setDoW (1 ); // 2018-01-01 is Monday. DS3231 will keep count from here
467469 ds3231.setHour (0 );
468470 ds3231.setMinute (0 );
469471 ds3231.setSecond (0 );
@@ -520,7 +522,17 @@ long dateToDayCount(word y, byte m, byte d){
520522 dc += d-1 ; // every full day since start of this month
521523 return dc;
522524}
523-
525+ byte dayOfWeek (word y, byte m, byte d){
526+ // DS3231 doesn't really calculate the day of the week, it just keeps a counter.
527+ // When setting date, we'll calculate per https://en.wikipedia.org/wiki/Zeller%27s_congruence
528+ byte yb = y%100 ; // 2-digit year
529+ byte ya = y/100 ; // century
530+ // For this formula, Jan and Feb are considered months 11 and 12 of the previous year.
531+ // So if it's Jan or Feb, add 10 to the month, and set back the year and century if applicable
532+ if (m<3 ) { m+=10 ; if (yb==0 ) { yb=99 ; ya-=1 ; } else yb-=1 ; }
533+ else m -= 2 ; // otherwise subtract 2 from the month
534+ return (d + ((13 *m-1 )/5 ) + yb + (yb/4 ) + (ya/4 ) + 5 *ya) %7 ;
535+ }
524536
525537
526538// //////// Clock ticking and timed event triggering //////////
@@ -543,10 +555,17 @@ void checkRTC(bool force){
543555 }
544556 // Update things based on RTC
545557 tod = rtc.now ();
546- // toddow = ds3231.getDoW();
558+ toddow = ds3231.getDoW ();
547559
548560 if (rtcSecLast != tod.second () || force) {
549561 rtcSecLast = tod.second ();
562+
563+ // check for timed trips
564+ if (!force && tod.second ()==0 ) { // new minute, not forced
565+ if (tod.minute ()==0 ) { // new hour
566+ if (tod.hour ()==2 ) autoDST ();
567+ }
568+ }
550569
551570 // trip/check for alarm TODO
552571 // decrement timer TODO
@@ -558,6 +577,45 @@ void checkRTC(bool force){
558577 } // end if force or new second
559578} // end checkRTC()
560579
580+ bool fellBack = false ;
581+ void autoDST (){
582+ // Call daily when clock reaches 2am.
583+ // If rule matches, will set to 3am in spring, 1am in fall (and set fellBack so it only happens once)
584+ if (fellBack) { fellBack=false ; return ; } // If we fell back at last 2am, do nothing.
585+ if (toddow==0 ) { // is it sunday? currently all these rules fire on Sundays only
586+ switch (readEEPROM (optsLoc[7 ],false )){
587+ case 1 : // second Sunday in March to first Sunday in November (US/CA)
588+ if (tod.month ()==3 && tod.day ()>=8 && tod.day ()<=14 ) setDST (1 );
589+ if (tod.month ()==11 && tod.day ()<=7 ) setDST (-1 );
590+ break ;
591+ case 2 : // last Sunday in March to last Sunday in October (UK/EU)
592+ if (tod.month ()==3 && tod.day ()>=25 ) setDST (1 );
593+ if (tod.month ()==10 && tod.day ()>=25 ) setDST (-1 );
594+ break ;
595+ case 3 : // first Sunday in April to last Sunday in October (MX)
596+ if (tod.month ()==4 && tod.day ()<=7 ) setDST (1 );
597+ if (tod.month ()==10 && tod.day ()>=25 ) setDST (-1 );
598+ break ;
599+ case 4 : // last Sunday in September to first Sunday in April (NZ)
600+ if (tod.month ()==9 && tod.day ()>=24 ) setDST (1 ); // 30 days hath September: last Sun will be 24-30
601+ if (tod.month ()==4 && tod.day ()<=7 ) setDST (-1 );
602+ break ;
603+ case 5 : // first Sunday in October to first Sunday in April (AU)
604+ if (tod.month ()==10 && tod.day ()<=7 ) setDST (1 );
605+ if (tod.month ()==4 && tod.day ()<=7 ) setDST (-1 );
606+ break ;
607+ case 6 : // third Sunday in October to third Sunday in February (BZ)
608+ if (tod.month ()==10 && tod.day ()>=15 && tod.day ()<=21 ) setDST (1 );
609+ if (tod.month ()==2 && tod.day ()>=15 && tod.day ()<=21 ) setDST (-1 );
610+ break ;
611+ default : break ;
612+ } // end setting switch
613+ } // end is it sunday
614+ }
615+ void setDST (char dir){
616+ if (dir==1 ) ds3231.setHour (3 ); // could set relatively if we move away from 2am-only sets
617+ if (dir==-1 && !fellBack) { ds3231.setHour (1 ); fellBack=true ; }
618+ }
561619
562620// //////// Display data formatting //////////
563621void updateDisplay (){
@@ -590,8 +648,8 @@ void updateDisplay(){
590648 case fnIsDate:
591649 editDisplay (EEPROM.read (optsLoc[2 ])==1 ?tod.month ():tod.day (), 0 , 1 , EEPROM.read (optsLoc[4 ]));
592650 editDisplay (EEPROM.read (optsLoc[2 ])==1 ?tod.day ():tod.month (), 2 , 3 , EEPROM.read (optsLoc[4 ]));
593- blankDisplay (4 , 5 );
594- // editDisplay(toddow, 4, 4 , false); //TODO is this 0=Sunday, 6=Saturday?
651+ blankDisplay (4 , 4 );
652+ editDisplay (toddow, 5 , 5 , false ); // TODO is this 0=Sunday, 6=Saturday?
595653 break ;
596654 case fnIsDayCount:
597655 long targetDayCount; targetDayCount = dateToDayCount (
0 commit comments