Skip to content

Commit

Permalink
fix Auto DST bugs - DST set incorrectly
Browse files Browse the repository at this point in the history
  • Loading branch information
wbphelps committed Nov 6, 2012
1 parent cbb924e commit ee39ee0
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 63 deletions.
4 changes: 2 additions & 2 deletions Time.c
Expand Up @@ -136,7 +136,7 @@
// leap year calulator expects year argument as years offset from 1970
#define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )

static const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; // API starts months from 1, this array starts from 0
static const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; // API starts months from 1, this array starts from 0

void breakTime(time_t time, tmElements_t* tm)
{
Expand Down Expand Up @@ -199,7 +199,7 @@ time_t makeTime(tmElements_t* tm){
time_t seconds;

// seconds from 1970 till 1 jan 00:00:00 of the given year
seconds= tm->Year*(SECS_PER_DAY * 365);
seconds = tm->Year*(SECS_PER_DAY * 365UL);
for (i = 0; i < tm->Year; i++) {
if (LEAP_YEAR(i)) {
seconds += SECS_PER_DAY; // add extra days for leap years
Expand Down
3 changes: 2 additions & 1 deletion Time.h
Expand Up @@ -53,7 +53,8 @@ typedef struct {
#define SECS_PER_DAY (SECS_PER_HOUR * 24UL)
#define DAYS_PER_WEEK (7UL)
#define SECS_PER_WEEK (SECS_PER_DAY * DAYS_PER_WEEK)
#define SECS_PER_YEAR (SECS_PER_WEEK * 52UL)
//#define SECS_PER_YEAR (SECS_PER_WEEK * 52UL) // is this correct ??? 05nov12/wbp
#define SECS_PER_YEAR (SECS_PER_DAY * 365UL) // 05nov12/wbp
#define SECS_YR_2000 (946684800UL) // the time at the start of y2k

/* Useful Macros for getting elapsed time */
Expand Down
54 changes: 24 additions & 30 deletions adst.c
Expand Up @@ -23,47 +23,46 @@ extern uint8_t g_DST_mode; // DST off, on, auto?
extern uint8_t g_DST_offset; // DST offset in hours
extern uint8_t g_DST_update; // DST update flag

// Number of days at the beginning of the month if not leap year
static const uint16_t monthDays[]={0,31,59,90,120,151,181,212,243,273,304,334};
// value used to prevent looping when "falling back"
static long seconds_last = 0;
uint8_t mDays[]={31,28,31,30,31,30,31,31,30,31,30,31};
uint16_t tmDays[]={0,31,59,90,120,151,181,212,243,273,304,334}; // Number days at beginning of month if not leap year

// Calculate day of the week - Sunday=1, Saturday=7 (non ISO)
uint8_t dotw(uint8_t year, uint8_t month, uint8_t day)
uint8_t dotw(uint16_t year, uint8_t month, uint8_t day)
{
uint16_t m, y;
m = month;
y = 2000 + year; // a reasonable assumption good for another 80+ years...
y = year;
if (m < 3) {
m += 12;
y -= 1;
}
return (day + (2 * m) + (6 * (m+1)/10) + y + (y/4) - (y/100) + (y/400) + 1) % 7 + 1;
}

long yearSeconds(uint8_t yr, uint8_t mo, uint8_t da, uint8_t h, uint8_t m, uint8_t s)
long yearSeconds(uint16_t yr, uint8_t mo, uint8_t da, uint8_t h, uint8_t m, uint8_t s)
{
long dn;
dn = monthDays[(mo-1)]+da; // # days so far if not leap year
if ((yr % 4 == 0 && yr % 100 != 0) || yr % 400 == 0) // if leap year
dn ++; // add 1 day
dn = dn * 86400 + h*3600 + m*60 + s;
unsigned long dn = tmDays[(mo-1)]+da; // # days so far if not leap year or (mo<3)
if (mo>2) {
if ((yr%4 == 0 && yr%100 != 0) || yr%400 == 0) // if leap year
dn ++; // add 1 day
}
dn = dn*86400 + (long)h*3600 + (long)m*60 + s;
return dn;
}

long DSTseconds(uint8_t year, uint8_t month, uint8_t doftw, uint8_t n, uint8_t hour)
long DSTseconds(uint16_t year, uint8_t month, uint8_t doftw, uint8_t week, uint8_t hour)
{
uint8_t dom = monthDays[month-1];
if ( (year%4 == 0) && (month == 2) )
uint8_t dom = mDays[month-1];
if ( (month == 2) && (year%4 == 0) )
dom ++; // february has 29 days this year
uint8_t dow = dotw(year, month, 1); // DOW for 1st day of month for DST event
uint8_t day = doftw - dow; // number of days until 1st dotw in given month
// if (day<1) day += 7; // make sure it's positive - doesn't work with uint!
int8_t day = doftw - dow; // number of days until 1st dotw in given month
if (day<1) day += 7; // make sure it's positive
if (doftw >= dow)
day = doftw - dow;
else
day = doftw + 7 - dow;
day = 1 + day + (n-1)*7; // date of dotw for this year
day = 1 + day + (week-1)*7; // date of dotw for this year
while (day > dom) // handles "last DOW" case
day -= 7;
return yearSeconds(year,month,day,hour,0,0); // seconds til DST event this year
Expand All @@ -76,31 +75,26 @@ long DSTseconds(uint8_t year, uint8_t month, uint8_t doftw, uint8_t n, uint8_t h
// 3,1,2,2, 11,1,1,2, 1
uint8_t getDSToffset(tmElements_t* te, DST_Rules* rules)
{
// uint16_t yr = 2000 + tmYearToY2k(te->Year); // convert tmElements_t Year to 20yy
uint16_t yr = 2000 + te->Year; // Year as 20yy; te.Year is not 1970 based
// if current time & date is at or past the first DST rule and before the second, return 1
// otherwise return 0
// seconds til start of DST this year
long seconds1 = DSTseconds(te->Year, rules->Start.Month, rules->Start.DOTW, rules->Start.Week, rules->Start.Hour);
long seconds1 = DSTseconds(yr, rules->Start.Month, rules->Start.DOTW, rules->Start.Week, rules->Start.Hour);
// seconds til end of DST this year
long seconds2 = DSTseconds(te->Year, rules->End.Month, rules->End.DOTW, rules->End.Week, rules->End.Hour);
long seconds_now = yearSeconds(te->Year, te->Month, te->Day, te->Hour, te->Minute, te->Second);
// NOTE - the following could be improved - if user sets date, or even time, reset "seconds_last" ???
if (seconds_now < seconds_last) // is time less than it was?
seconds_now = seconds_last; // prevent loop when setting time back
long seconds2 = DSTseconds(yr, rules->End.Month, rules->End.DOTW, rules->End.Week, rules->End.Hour);
long seconds_now = yearSeconds(yr, te->Month, te->Day, te->Hour, te->Minute, te->Second);
if (seconds2>seconds1) { // northern hemisphere
if ((seconds_now >= seconds1) && (seconds_now < seconds2)) // spring ahead
return(rules->Offset); // return Offset
else { // fall back
seconds_last = seconds_now;
else // fall back
return(0); // return 0
}
}
else { // southern hemisphere
if ((seconds_now >= seconds2) && (seconds_now < seconds1)) // fall ahead
return(rules->Offset); // return Offset
else { // spring back
seconds_last = seconds_now;
else // spring back
return(0); // return 0
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion adst.h
Expand Up @@ -29,7 +29,7 @@ typedef struct {
} DST_Rules;

char* dst_setting(uint8_t dst);
uint8_t dotw(uint8_t year, uint8_t month, uint8_t day);
uint8_t dotw(uint16_t year, uint8_t month, uint8_t day);
uint8_t getDSToffset(tmElements_t* te, DST_Rules* rules);
//void save_dstrules(uint8_t mods[]);
//long DSTseconds(uint8_t month, uint8_t doftw, uint8_t n, uint8_t hour);
Expand Down
2 changes: 1 addition & 1 deletion display.c
Expand Up @@ -618,7 +618,7 @@ void show_date(tmElements_t *te_, uint8_t region)
char sl;
char d[18];
d[0] = d[1] = ' ';
if (shield == SHIELD_IV6)
if (shield == SHIELD_IV17)
sl = '/';
else
sl = '-';
Expand Down
75 changes: 49 additions & 26 deletions main.c
Expand Up @@ -14,6 +14,9 @@
*/

/* Updates by William B Phelps
* 05nov12 fix DST calc bug
* speed up alarm check in rtc (not done)
*
* 03nov12 fix for ADST falling back
* change region to "DMY/MDY/YMD"
*
Expand Down Expand Up @@ -149,7 +152,7 @@ uint8_t g_gps_updating = 0; // for signalling GPS update on some displays
#if defined FEATURE_WmGPS || defined FEATURE_AUTO_DST
uint8_t g_DST_mode; // DST off, on, auto?
uint8_t g_DST_offset; // DST offset in Hours
uint8_t g_DST_update = 0; // DST update flag
uint8_t g_DST_updated = false; // DST update flag = allow update only once per day
#endif
#ifdef FEATURE_AUTO_DST
//DST_Rules dst_rules = {{10,1,1,2},{4,1,1,2},1}; // DST Rules for parts of OZ including NSW (for JG)
Expand All @@ -174,7 +177,8 @@ uint8_t g_alarming = false; // alarm is going off
uint8_t g_alarm_switch;
uint16_t g_show_special_cnt = 0; // display something special ("time", "alarm", etc)
//time_t g_tNow = 0; // current local date and time as time_t, with DST offset
tmElements_t* te; // current local date and time as TimeElements
tmElements_t* tm_; // current local date and time as TimeElements (pointer)
uint8_t alarm_hour = 0, alarm_min = 0, alarm_sec = 0;

extern enum shield_t shield;

Expand Down Expand Up @@ -236,6 +240,7 @@ void initialize(void)
display_init(g_brightness);

g_alarm_switch = get_alarm_switch();
rtc_get_alarm_s(&alarm_hour, &alarm_min, &alarm_sec);

// set up interrupt for alarm switch
PCICR |= (1 << PCIE2);
Expand Down Expand Up @@ -363,29 +368,37 @@ char* region_setting(uint8_t reg)
#endif

#if defined FEATURE_WmGPS || defined FEATURE_AUTO_DST
void setDSToffset(int8_t newOffset) {
void setDSToffset(uint8_t mode) {
int8_t adjOffset;
if (newOffset == 2) { // Auto DST
newOffset = getDSToffset(te, &dst_rules); // get current DST offset based on DST Rules
uint8_t newOffset;
if (mode == 2) { // Auto DST
if (g_DST_updated) return; // already done it once today
if (tm_ == NULL) return; // safet check
newOffset = getDSToffset(tm_, &dst_rules); // get current DST offset based on DST Rules
}
else
newOffset = mode; // 0 or 1
adjOffset = newOffset - g_DST_offset; // offset delta
if (adjOffset == 0) return; // nothing to do
g_DST_updated = true;
beep(400, 1); // debugging
time_t tNow = rtc_get_time_t(); // fetch current time from RTC as time_t
tNow += adjOffset * SECS_PER_HOUR; // add or subtract new DST offset
rtc_set_time_t(tNow); // adjust RTC
g_DST_offset = newOffset;
eeprom_update_byte(&b_DST_offset, g_DST_offset);
// save DST_updated in ee ???
}
#endif

void display_time(display_mode_t mode) // (wm) runs approx every 200 ms
{
static uint16_t counter = 0;
uint8_t hour = 0, min = 0, sec = 0;
// uint8_t hour = 0, min = 0, sec = 0;

#ifdef FEATURE_AUTO_DATE
if (mode == MODE_DATE) {
show_date(te, g_region); // show date from last rtc_get_time() call
show_date(tm_, g_region); // show date from last rtc_get_time() call
}
else
#endif
Expand All @@ -394,8 +407,8 @@ void display_time(display_mode_t mode) // (wm) runs approx every 200 ms
}
else if (mode == MODE_ALARM_TIME) {
if (g_alarm_switch) {
rtc_get_alarm_s(&hour, &min, &sec);
show_alarm_time(hour, min, 0);
rtc_get_alarm_s(&alarm_hour, &alarm_min, &alarm_sec);
show_alarm_time(alarm_hour, alarm_min, 0);
}
else {
show_alarm_off();
Expand All @@ -408,39 +421,44 @@ void display_time(display_mode_t mode) // (wm) runs approx every 200 ms
show_temp(t, f);
}
else {
te = rtc_get_time();
if (te == NULL) return;
tm_ = rtc_get_time();
if (tm_ == NULL) return;
#ifdef FEATURE_SET_DATE
g_dateyear = te->Year; // save year for Menu
g_datemonth = te->Month; // save month for Menu
g_dateday = te->Day; // save day for Menu
g_dateyear = tm_->Year; // save year for Menu
g_datemonth = tm_->Month; // save month for Menu
g_dateday = tm_->Day; // save day for Menu
#endif
#ifdef FEATURE_AUTO_DST
if (te->Second%10 == 0) { // check DST Offset every 10 seconds
if (tm_->Second%10 == 0) // check DST Offset every 10 seconds
setDSToffset(g_DST_mode);
}
if ((tm_->Hour == 0) && (tm_->Minute == 0) && (tm_->Second == 0))
g_DST_updated = false;
#endif
#ifdef FEATURE_AUTO_DATE
if (g_autodate && (te->Second == g_autotime) ) {
if (g_autodate && (tm_->Second == g_autotime) ) {
save_mode = clock_mode; // save current mode
clock_mode = MODE_DATE; // display date now
g_show_special_cnt = g_autodisp; // show date for g_autodisp ms
scroll_ctr = 0; // reset scroll position
}
else
#endif
show_time(te, g_24h_clock, mode); // (wm)
show_time(tm_, g_24h_clock, mode); // (wm)
}
counter++;
if (counter == 250) counter = 0;
}

#ifdef FEATURE_SET_DATE
void set_date(uint8_t yy, uint8_t mm, uint8_t dd) {
te->Year = yy;
te->Month = mm;
te->Day = dd;
rtc_set_time(te);
tm_ = rtc_get_time(); // refresh current time
tm_->Year = yy;
tm_->Month = mm;
tm_->Day = dd;
rtc_set_time(tm_);
#ifdef FEATURE_AUTO_DST
g_DST_updated = false; // allow automatic DST adjustment again
#endif
}
#endif

Expand Down Expand Up @@ -527,7 +545,7 @@ void main(void)
if (g_alarm_switch) {
menu_state = STATE_SET_ALARM;
show_set_alarm();
rtc_get_alarm_s(&hour, &min, &sec);
rtc_get_alarm_s(&alarm_hour, &alarm_min, &alarm_sec);
time_to_set = hour*60 + min;
}
else {
Expand Down Expand Up @@ -568,8 +586,12 @@ void main(void)
button_speed = 1;

// fixme: should be different in 12h mode
if (menu_state == STATE_SET_CLOCK)
if (menu_state == STATE_SET_CLOCK) {
rtc_set_time_s(time_to_set / 60, time_to_set % 60, 0);
#if defined FEATURE_AUTO_DST
g_DST_updated = false; // allow automatic DST adjustment again
#endif
}
else
rtc_set_alarm_s(time_to_set / 60, time_to_set % 60, 0);

Expand All @@ -590,7 +612,7 @@ void main(void)
// Left button enters menu
else if (menu_state == STATE_CLOCK && buttons.b2_keyup) {
menu_state = STATE_MENU_BRIGHTNESS;
if (get_digits() == 4) // only set first time flag for 4 digit displays
if (get_digits() < 8) // only set first time flag for 4 or 6 digit displays
menu_b1_first = true; // set first time flag
show_setting_int("BRIT", "BRITE", g_brightness, false);
buttons.b2_keyup = 0; // clear state
Expand Down Expand Up @@ -697,6 +719,7 @@ void main(void)
if (!menu_b1_first) {
g_DST_mode = (g_DST_mode+1)%3; // 0: off, 1: on, 2: auto
eeprom_update_byte(&b_DST_mode, g_DST_mode);
g_DST_updated = false; // allow automatic DST adjustment again
setDSToffset(g_DST_mode);
}
show_setting_string("DST", "DST", dst_setting(g_DST_mode), true);
Expand Down Expand Up @@ -855,7 +878,7 @@ void main(void)
}

// fixme: alarm should not be checked when setting time or alarm
if (g_alarm_switch && rtc_check_alarm())
if (g_alarm_switch && rtc_check_alarm_t(tm_))
g_alarming = true;

#ifdef FEATURE_WmGPS
Expand Down
25 changes: 23 additions & 2 deletions rtc.c
Expand Up @@ -98,7 +98,6 @@ uint8_t rtc_read_byte(uint8_t offset)
twi_begin_transmission(RTC_ADDR);
twi_send_byte(offset);
twi_end_transmission();

twi_request_from(RTC_ADDR, 1);
return twi_receive();
}
Expand Down Expand Up @@ -258,7 +257,7 @@ time_t rtc_get_time_t(void)
te.Day = bcd2dec(rtc[4]); // day 1-31
te.Month = bcd2dec(rtc[5]); // month 1-12
te.Year = bcd2dec(rtc[6]); // year 0-99
te.Year = y2kYearToTm(te.Year); // convert yy year to (yyyy-1970) (add 30)
te.Year = y2kYearToTm(te.Year); // convert yy year to (yyyy-1970) (add 30) for makeTime
tNow = makeTime(&te); // convert to time_t
return tNow;
}
Expand Down Expand Up @@ -666,3 +665,25 @@ bool rtc_check_alarm(void)
return val & 1 ? 1 : 0;
}
}

bool rtc_check_alarm_t(TimeElements* te)
{
if (s_is_ds1307) {
uint8_t hour = rtc_get_sram_byte(0);
uint8_t min = rtc_get_sram_byte(1);
uint8_t sec = rtc_get_sram_byte(2);
if (hour == te->Hour && min == te->Minute && sec == te->Second)
return true;
return false;
}
else {
// Alarm 1 flag (A1F) in bit 0
uint8_t val = rtc_read_byte(0x0f);

// clear flag when set
if (val & 1)
rtc_write_byte(val & ~0b00000001, 0x0f);

return val & 1 ? 1 : 0;
}
}
1 change: 1 addition & 0 deletions rtc.h
Expand Up @@ -105,5 +105,6 @@ void rtc_set_alarm_s(uint8_t hour, uint8_t min, uint8_t sec);
TimeElements* rtc_get_alarm(void);
void rtc_get_alarm_s(uint8_t* hour, uint8_t* min, uint8_t* sec);
bool rtc_check_alarm(void);
bool rtc_check_alarm_t(TimeElements* te);

#endif

0 comments on commit ee39ee0

Please sign in to comment.