Go Down

Topic: Universal Countdown Clock (Read 4552 times) previous topic - next topic

wallaceb

Aug 16, 2013, 10:04 pm Last Edit: Sep 11, 2013, 09:35 pm by wallaceb Reason: 1
i am in the planning stages of making a "Universal Countdown Clock" using the following hardware:

7" LCD screen - http://www.ebay.com/itm/7-7-0-inch-TFT-LCD-module-Font-IC-800x480-SSD1963-arduino-DUE-MEGA2560-3-5-4-3-/111008432389?
LCD screen shield - http://www.ebay.com/itm/TFT-SD-Shield-for-Arduino-DUE-TFT-LCD-Module-SD-Card-Adapter-2-8-3-2-inch-Mega-/121057447668?
Arduino Due - http://www.amazon.com/Arduino-A000062-Due/dp/B00A6C3JN2/ref=wl_it_dp_o_pC_nS_nC?ie=UTF8&colid=2T79BVD0S9MWW&coliid=I2UODE0TCIYT07
RTC - http://www.amazon.com/ADAFRUIT-INDUSTRIES-DS1307-Clock-Breakout/dp/B00C9NWMJ8/ref=wl_it_dp_o_pC_nS_nC?ie=UTF8&colid=2T79BVD0S9MWW&coliid=I1OJ1VK96A9NRB
External EEPROM - http://www.amazon.com/microtivity-24LC256-256Kbit-Serial-EEPROM/dp/B0092CRK14/ref=wl_it_dp_o_pC_nS_nC?ie=UTF8&colid=2T79BVD0S9MWW&coliid=I1O789MJ03CX23
Enclosure - http://www.amazon.com/BUD-Industries-PN-1341-DG-High-Impact-Indoor/dp/B005T7BAGW/ref=wl_it_dp_o_pC_S_nC?ie=UTF8&colid=2T79BVD0S9MWW&coliid=I2CG7AAIMPYX2K

The project will allow for multiple simultaneous countdowns for the following:

Christmas Adjust
Cinco De Mayo Adjust
Valintine's' Day Adjust
Easter Adjust
Forth of July Adjust
Halloween Adjust
New Years Adjust
St Patric's Day Adjust
Thanksgiving Adjust
Birthday 1 Adjust
Birthday 2 Adjust
Anniversary Adjust
Custom 1 Adjust
Custom 2 Adjust

the pre-defined holidays, since their dates are always known will not have adjustable dates. the different birthdays will allow you to say the name of the individual who's birthday is bring counted down to, and obviously the date of their birthday. the custom countdowns allow you to count down to anything (like retirement for instance).

each pre-defined holiday, the birthdays, and custom countdowns ect.. allows you to change the color of the font, and the screen background color to be changed independently of one another. thanks to the LCD screen's ability to store images for me in its own 64mbit flash, each holiday will have its own image associated that will be displayed along with the count down.

the user will be able to enable or disable the different holidays independently, and if no countdowns are enabled at all, then the unit will act as a simple clock.

The system is powered by DueGUI library so that all user settings can be easily adjusted through the touch interface.

i will update this thread as the build progresses.


[edit] edited to reflect latest design information

int2str

Sounds like a cool project.

A few questions though:
1) Why a 7" LCD? Why not an LED sign like this (or the DIY equivalent) for example:
http://www.ebay.com/itm/New-Color-LED-Programmable-Scrolling-Message-Display-Sign-26-x4-FREE-SHIPPING-/320823352036?pt=LH_DefaultDomain_0&hash=item4ab28fdae4

2) Why an external EEPROM? Even the ATTiny85 has enough EEPROM space for various countdowns/config settings.

3) Speeking of EEPROM space, why the Due? Seems total overkill for something like this, no?

4) Maybe get a board with Ethernet already on-board?

5) Or why not Wifi?

wallaceb

please see my responses in red below


Sounds like a cool project.

A few questions though:
1) Why a 7" LCD? Why not an LED sign like this (or the DIY equivalent) for example:
http://www.ebay.com/itm/New-Color-LED-Programmable-Scrolling-Message-Display-Sign-26-x4-FREE-SHIPPING-/320823352036?pt=LH_DefaultDomain_0&hash=item4ab28fdae4 i want to be able to display full color background images  :smiley-mr-green:

2) Why an external EEPROM? Even the ATTiny85 has enough EEPROM space for various countdowns/config settings. the arduino due does not have eeprom, so i must use external eeprom

3) Speeking of EEPROM space, why the Due? Seems total overkill for something like this, no?  please refer to the following video, it clearly shows the MASSIVE performance difference between the due and the mega when using a color LCD http://www.youtube.com/watch?v=JXcVw8dwxPw

4) Maybe get a board with Ethernet already on-board? because i need the performance of the due, that is not an option  =(

5) Or why not Wifi? i like to make my projects as if they are a "commercial product" and since the wifi needs to be edited directly into the arduino sketch, i do not want to utilize that.

int2str

Alright, all good reasons I suppose.
I got one more though: Raspberri Pi?!

$35 = Rapberri Pi, including HDMI out, Ethernet & SD Card for image storage and settings
Just add RTC and you're done...

wallaceb


Alright, all good reasons I suppose.
I got one more though: Raspberri Pi?!

$35 = Rapberri Pi, including HDMI out, Ethernet & SD Card for image storage and settings
Just add RTC and you're done...


i thought of that, but i do not want to connect to a large screen as they draw more power than the arduino will, and i want to have the nice web-based interface. i used to do web-design with PHP, so i could easily make a PHP web-interface for the Pi...

i did not say it originally, but my wife and i want to attach the box that all the hardware is in to a picture frame, so people would only see the frame making it look much nice, using HDMI out to a larger screen will make that too difficult.

wallaceb

#5
Aug 17, 2013, 12:18 am Last Edit: Aug 17, 2013, 12:21 am by wallaceb Reason: 1
i have my code that calculates the remaining time based on the input target date. the code is more complicated than it has to be because i wanted the calculation to be exact based on the date. i did not want to use the method using the average hours per month as it will not be as accurate.

Code: [Select]
#include <SPI.h>
#include <Wire.h>
#include "Wire.h"
byte seconds, minutes, hours, dayOfWeek, dayOfMonth, months, years;
#define DS1307_I2C_ADDRESS 0x68 //address of the RTC on the SPI bus


void setup()  {
 Serial.begin(9600);  
 Wire.begin();//needed to comunicate with the real time clock  
}

double prevDisplay=0;
void loop()
{
 
 getDateDs1307(seconds, minutes, hours, dayOfWeek, dayOfMonth, months, years);
 char buffer[40];
 byte remaining_seconds, remaining_minuets, remaining_hours, remaining_days, remaining_months, remaining_years;
 if ((seconds + minutes + hours)  != prevDisplay) { //update the display only if time has changed
     prevDisplay = (seconds + minutes + hours);
     calculate_countdown(0,0,0,25,12,13, remaining_seconds, remaining_minuets, remaining_hours, remaining_days, remaining_months, remaining_years);
     sprintf (buffer, "%.2d Years : %.2d Months : %.2d Days", remaining_years, remaining_months, remaining_days);
     Serial.println(buffer);
     sprintf(buffer, "%d Hours : %d Minuets : %d Seconds", remaining_hours, remaining_minuets, remaining_seconds);
     Serial.println(buffer);
     Serial.println();
   }
}
void calculate_countdown(byte future_seconds,
byte future_minutes,
byte future_hours,
byte future_days,
byte future_months,
byte future_years,
byte &remaining_seconds,
byte &remaining_minuets,
byte &remaining_hours,
byte &remaining_days,
byte &remaining_months,
byte &remaining_years){

bool borrow_from_minutes = false;
bool borrow_from_hours = false;
bool borrow_from_days = false;
bool borrow_from_month = false;
bool borrow_from_years = false;



/******************************
/*CALCULATE REMIANING SECONDS
/*****************************/

if (future_seconds - seconds <0){
remaining_seconds = 60 - seconds;
borrow_from_minutes = true;
}else{
remaining_seconds = future_seconds - seconds;
       borrow_from_minutes = false;
}

/******************************
/*CALCULATE REMIANING MINUTES
/*****************************/

if (borrow_from_minutes == true){
if (future_minutes - minutes - 1 <0){
remaining_minuets = 60 - minutes - 1;
borrow_from_hours = true;
}else{
remaining_minuets = future_minutes - minutes - 1;
       borrow_from_hours = false;
}
}else{
if (future_minutes - minutes <0){
remaining_minuets = 60 - minutes;
borrow_from_hours = true;
       }else{
               remaining_minuets = future_minutes - minutes;
               borrow_from_hours = false;
       }
}

/******************************
/*CALCULATE REMIANING HOURS
/*****************************/

if (borrow_from_hours == true){
if (future_hours - hours - 1 <0){
remaining_hours = 24 - hours - 1;
borrow_from_days = true;
}else{
remaining_hours = future_hours - hours - 1;
borrow_from_days = false;
}
}else{
if (future_hours - hours <0){
remaining_hours = 24 - hours;
borrow_from_days = true;
}else{
remaining_hours = future_hours - hours;
borrow_from_days = false;
}
}

/******************************
/*CALCULATE REMIANING DAYS
/*****************************/

if (borrow_from_days == true){
if ((future_days - dayOfMonth - 1) <0){
if (months == 1){ //January has 31 days
remaining_days = 31 - dayOfMonth - 1;
borrow_from_month = true;
}else if (months == 2){ //February has 28 days
remaining_days = 28 - dayOfMonth - 1;
borrow_from_month = true;
}else if (months == 3){ //March has 31 days
remaining_days = 31 - dayOfMonth - 1;
borrow_from_month = true;
}else if (months == 4){ //April has 30 days
remaining_days = 30 - dayOfMonth - 1;
borrow_from_month = true;
}else if (months==5){ //May has 31 days
remaining_days = 31 - dayOfMonth - 1;
borrow_from_month = true;
}else if (months == 6){ //June has 30 days
remaining_days = 30 - dayOfMonth - 1;
borrow_from_month = true;
}else if (months == 7){ //July has 31 days
remaining_days = 31 - dayOfMonth - 1;
borrow_from_month = true;
}else if (months ==8){ //August has 31 days
remaining_days = 31 - dayOfMonth - 1;
borrow_from_month = true;
}else if (months == 9){ //September has 30 days
remaining_days = 30 - dayOfMonth - 1;
borrow_from_month = true;
}else if (months == 10){ //October has 31 days
remaining_days = 31 - dayOfMonth - 1;
borrow_from_month = true;
}else if (months == 11){ //February has 30 days
remaining_days = 30 - dayOfMonth - 1;
borrow_from_month = true;
}else if (months == 12){ //December has 31 days
remaining_days = 31 - dayOfMonth - 1;
borrow_from_month = true;
}
}else{
remaining_days = future_days - dayOfMonth - 1;
borrow_from_month = false;
}
}else{
if (future_days - dayOfMonth <0){
if (months == 1){ //January has 31 days
remaining_days = 31 - dayOfMonth;
borrow_from_month = false;
}else if (months == 2){ //February has 28 days
remaining_days = 28 - dayOfMonth;
borrow_from_month = false;
}else if (months == 3){ //March has 31 days
remaining_days = 31 - dayOfMonth;
borrow_from_month = false;
}else if (months == 4){ //April has 30 days
remaining_days = 30 - dayOfMonth;
borrow_from_month = false;
}else if (months ==5){ //May has 31 days
remaining_days = 31 - dayOfMonth;
borrow_from_month = false;
}else if (months == 6){ //June has 30 days
remaining_days = 30 - dayOfMonth;
borrow_from_month = false;
}else if (months == 7){ //July has 31 days
remaining_days = 31 - dayOfMonth;
borrow_from_month = false;
}else if (months ==8){ //August has 31 days
remaining_days = 31 - dayOfMonth;
borrow_from_month = false;
}else if (months == 9){ //September has 30 days
remaining_days = 30 - dayOfMonth;
borrow_from_month = false;
}else if (months == 10){ //October has 31 days
remaining_days = 31 - dayOfMonth;
borrow_from_month = false;
}else if (months == 11){ //February has 30 days
remaining_days = 30 - dayOfMonth;
borrow_from_month = false;
}else if (months == 12){ //December has 31 days
remaining_days = 31 - dayOfMonth;
borrow_from_month = false;
}
}else{
remaining_days = future_days - dayOfMonth;
borrow_from_days = false;
}
}

/******************************
/*CALCULATE REMIANING MONTHS
/*****************************/

if (borrow_from_month == true){
if ((future_months - months) - 1 <0){
remaining_months = 12 - abs(future_months - months) - 1;
borrow_from_years = true;
}else{
remaining_months = future_months - months - 1;
borrow_from_years = false;
}
}else{
if (future_months - months <0){
remaining_months = 12 - abs(future_months - months);
borrow_from_years = true;
}else{
remaining_months = future_months - months;
borrow_from_years = false;
}
}

/******************************
/*CALCULATE REMIANING YEARS
/*****************************/

if (borrow_from_years == true){
if (future_years - years - 1 <0){
remaining_years = 0;
}else{
remaining_years = future_years - years - 1;
}
}else{
if ((future_years - years) <0){
remaining_years = 0;
}else{
remaining_years = future_years - years;
}
}
}

// Gets the date and time from the ds1307
void getDateDs1307(byte &get_second,
byte &get_minute,
byte &get_hour,
byte &get_dayOfWeek,
byte &get_dayOfMonth,
byte &get_month,
byte &get_year)
{
 // Reset the register pointer
 Wire.beginTransmission(DS1307_I2C_ADDRESS);
 Wire.write(0);
 Wire.endTransmission();

 Wire.requestFrom(DS1307_I2C_ADDRESS, 7);

 // A few of these need masks because certain bits are control bits
 get_second     = bcdToDec(Wire.read() & 0x7f);
 get_minute     = bcdToDec(Wire.read());
 get_hour       = bcdToDec(Wire.read() & 0x3f);  // Need to change this if 12 hour am/pm
 get_dayOfWeek  = bcdToDec(Wire.read());
 get_dayOfMonth = bcdToDec(Wire.read());
 get_month      = bcdToDec(Wire.read());
 get_year       = bcdToDec(Wire.read());
}


// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
 return ( (val/16*10) + (val%16) );
}


int2str

Wallace,

That code is needlessly long & complicated and uses lots of variables.
I'm trying to simplify the code for you, but need to understand your requirements.

What are you going to display?

For example, given "now" being January 2st and the countdown going to March 29th of the same year. The difference is 29 + 28 (non leap-year) + 29 = 86 days. How are you going to display this?
a) 86 days
b) 2 month, 26 days (this would be based on 30 days per month)
c) 1 month, 58 days (February is the only full month)

Based on what you want to display, I can post up code here to simplify the calculation.

Also already a heads-up:
The DS1307 gives you years 0-99. So you have to add 2000 when you read it (and subtract 2000 on write).

wallaceb

#7
Aug 17, 2013, 03:35 am Last Edit: Aug 17, 2013, 03:40 am by wallaceb Reason: 1
thanks for the help.

you can see what i wish to display by this code:

Code: [Select]
sprintf (buffer, "%.2d Years : %.2d Months : %.2d Days", remaining_years, remaining_months, remaining_days);
     Serial.println(buffer);
     sprintf(buffer, "%d Hours : %d Minuets : %d Seconds", remaining_hours, remaining_minuets, remaining_seconds);
     Serial.println(buffer);


so i want it to say:
xx Years : xx Months : xx Days
xx Hours : xx Minutes : xx Seconds

for example, i entered the date for This year's xmas, and it says:
00 Years : 04 Months : 08 Days
3 Hours : 19 Minuets : 16 Seconds

i want to make sure that the countdown always takes into account the actual number of days that are in any particular month so the countdown is accurate

i have always been good at getting code to work how i want, but not really good as making the code the most efficient or elegant  :smiley-mr-green:

int2str

for example, i entered the date for This year's xmas, and it says:
00 Years : 04 Months : 08 Days
3 Hours : 19 Minuets : 16 Seconds

i want to make sure that the countdown always takes into account the actual number of days that are in any particular month so the countdown is accurate


This is where I'm still confused... Why does the actual number of days matter here?
It's not used in this calculation. Here you have 24-16 = 8 days; 12-8 = 4 month

The actual number of days in a month would only be used if you wanted to calculate the total number of days - which you're not.
But I think I have enough to propose a new algorithm. I'll post up some code in a bit...

wallaceb

#9
Aug 17, 2013, 04:35 am Last Edit: Aug 17, 2013, 04:43 am by wallaceb Reason: 1
i have tried many different date combinations and the code i have does what i want, i am not sure how else to explain what i need.

i did simplify the code by defining the following array:
Code: [Select]
byte days_per_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

i also removed the redundant "borrow" variables, as i realized i only need one.

Code: [Select]
void calculate_countdown(byte future_seconds,
byte future_minutes,
byte future_hours,
byte future_days,
byte future_months,
byte future_years,
byte &remaining_seconds,
byte &remaining_minuets,
byte &remaining_hours,
byte &remaining_days,
byte &remaining_months,
byte &remaining_years){
bool borrow = false;

/******************************
/*CALCULATE REMIANING SECONDS
/*****************************/

if (future_seconds - seconds <0){
remaining_seconds = 60 - seconds;
borrow = true;
}else{
remaining_seconds = future_seconds - seconds;
       borrow = false;
}

/******************************
/*CALCULATE REMIANING MINUTES
/*****************************/

if (borrow == true){
if (future_minutes - minutes - 1 <0){
remaining_minuets = 60 - minutes - 1;
borrow = true;
}else{
remaining_minuets = future_minutes - minutes - 1;
       borrow = false;
}
}else{
if (future_minutes - minutes <0){
remaining_minuets = 60 - minutes;
borrow = true;
       }else{
               remaining_minuets = future_minutes - minutes;
               borrow = false;
       }
}

/******************************
/*CALCULATE REMIANING HOURS
/*****************************/

if (borrow == true){
if (future_hours - hours - 1 <0){
remaining_hours = 24 - hours - 1;
borrow = true;
}else{
remaining_hours = future_hours - hours - 1;
borrow = false;
}
}else{
if (future_hours - hours <0){
remaining_hours = 24 - hours;
borrow = true;
}else{
remaining_hours = future_hours - hours;
borrow = false;
}
}

/******************************
/*CALCULATE REMIANING DAYS
/*****************************/

if (borrow == true){
if ((future_days - dayOfMonth - 1) <0){
                       remaining_days = days_per_month[months-1] - dayOfMonth - 1 + (days_per_month[months-1] - days_per_month[future_months-1]);
                       borrow = true;
}else{
remaining_days = future_days - dayOfMonth - 1;
borrow = false;
}
}else{
if (future_days - dayOfMonth <0){
                       remaining_days = days_per_month[months-1] - dayOfMonth + (days_per_month[months-1] - days_per_month[future_months-1]);
                       borrow = false;
}else{
remaining_days = future_days - dayOfMonth;
borrow = false;
}
}

/******************************
/*CALCULATE REMIANING MONTHS
/*****************************/

if (borrow == true){
if ((future_months - months) - 1 <0){
remaining_months = 12 - abs(future_months - months) - 1;
borrow = true;
}else{
remaining_months = future_months - months - 1;
borrow = false;
}
}else{
if (future_months - months <0){
remaining_months = 12 - abs(future_months - months);
borrow = true;
}else{
remaining_months = future_months - months;
borrow = false;
}
}

/******************************
/*CALCULATE REMIANING YEARS
/*****************************/

if (borrow == true){
if (future_years - years - 1 <0){
remaining_years = 0;
}else{
remaining_years = future_years - years - 1;
}
}else{
if ((future_years - years) <0){
remaining_years = 0;
}else{
remaining_years = future_years - years;
}
}
}


i corrected an issue where if the future date days was a lesser value than today's date's days.

the best way i think i can explain what i want is if you were to take the calculation's remaining months, flip that many months/pages on a calendar, then add on the remaining days to today's date (which should then land on the correct future month's future day) and then take the hours/minutes/seconds into account. when i do this, my calculation works exactly as i want...

i welcome help getting the code to be simplier, but i need to make sure i am relaying my desired properly...  :smiley-red:

int2str

Alright, I tested your function, and as I would expect from overly complex code like that, it doesn't seem to calculate the dates correctly.

Hopefully, this code is much easier to read and debug and also performs well on the device.
I did manage to reduce ~120 lines down to about 30 :)

Code: [Select]
const byte DAYS_IN_MONTH[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

struct TimeStamp
{
    unsigned int yy;
    byte mm;
    byte dd;
    byte hh;
    byte min;
    byte ss;
};

TimeStamp calcDiff(TimeStamp now, const TimeStamp &future)
{
    unsigned long sec_now = now.ss + (now.min * 60) + (now.hh * 3600);
    unsigned long sec_future = future.ss + (future.min * 60) + (future.hh * 3600);

    if (sec_future < sec_now)
    {
        sec_future += 86400;
        ++now.dd;
    }

    if (future.dd < now.dd)
    {
        now.dd = DAYS_IN_MONTH[now.mm-1] - now.dd + future.dd;
        ++now.mm;
    } else {
        now.dd = future.dd - now.dd;
    }

    if (future.mm < now.mm)
    {
        now.mm = future.mm + 12 - now.mm;
        ++now.yy;
    } else {
        now.mm = future.mm - now.mm;
    }

    sec_now = sec_future - sec_now;
    now.yy = future.yy - now.yy;
    now.hh = sec_now / 3600;
    now.min = (sec_now - (now.hh * 3600)) / 60;
    now.ss = sec_now % 60;

    return now;
}

wallaceb

#11
Aug 18, 2013, 04:14 am Last Edit: Aug 18, 2013, 04:23 am by wallaceb Reason: 1
i tested your code, and it made me look at mine a little more, and i discovered that both of our codes had issues. i was able to correct my code such that it works perfectly based on my test results. i did previously say i can get code to work, but getting it to be simple and elegant like yours is not my strong suit  :smiley-mr-green:  8)

Code: [Select]
void calculate_countdown(byte future_seconds,
byte future_minutes,
byte future_hours,
byte future_days,
byte future_months,
byte future_years,
byte &remaining_seconds,
byte &remaining_minuets,
byte &remaining_hours,
byte &remaining_days,
byte &remaining_months,
byte &remaining_years){
bool borrow = false;

/******************************
/*CALCULATE REMIANING SECONDS
/*****************************/

if (future_seconds - seconds <0){
remaining_seconds = 60 - seconds;
borrow = true;
}else{
remaining_seconds = future_seconds - seconds;
       borrow = false;
}

/******************************
/*CALCULATE REMIANING MINUTES
/*****************************/

if (borrow == true){
if (future_minutes - minutes - 1 <0){
remaining_minuets = 60 - minutes - 1;
borrow = true;
}else{
remaining_minuets = future_minutes - minutes - 1;
       borrow = false;
}
}else{
if (future_minutes - minutes <0){
remaining_minuets = 60 - minutes;
borrow = true;
       }else{
               remaining_minuets = future_minutes - minutes;
               borrow = false;
       }
}

/******************************
/*CALCULATE REMIANING HOURS
/*****************************/

if (borrow == true){
if (future_hours - hours - 1 <0){
remaining_hours = 24 - hours - 1;
borrow = true;
}else{
remaining_hours = future_hours - hours - 1;
borrow = false;
}
}else{
if (future_hours - hours <0){
remaining_hours = 24 - hours;
borrow = true;
}else{
remaining_hours = future_hours - hours;
borrow = false;
}
}

/******************************
/*CALCULATE REMIANING DAYS
/*****************************/

if (borrow == true){
if ((future_days - dayOfMonth - 1) <0){
                       
                       remaining_days = days_per_month[months-1] - dayOfMonth - 1 + future_days;
                       borrow = true;
}else{
remaining_days = future_days - dayOfMonth - 1;
borrow = false;
}
}else{
if (future_days - dayOfMonth <0){
                       remaining_days = days_per_month[months-1] - dayOfMonth + future_days;
                       borrow = false;
}else{
remaining_days = future_days - dayOfMonth;
borrow = false;
}
}

/******************************
/*CALCULATE REMIANING MONTHS
/*****************************/

if (borrow == true){
if ((future_months - months) - 1 <0){
remaining_months = 12 - abs(future_months - months) - 1;
                       if ((months + remaining_months) > 12){
                         remaining_days -= (days_per_month[months-1] - days_per_month[(months + remaining_months - 12)-1]);
                       }else{
                         remaining_days -= (days_per_month[months-1] - days_per_month[(months + remaining_months)-1]);
                       }
borrow = true;
}else{
remaining_months = future_months - months - 1;
                       if ((months + remaining_months) > 12){
                         remaining_days -= (days_per_month[months-1] - days_per_month[(months + remaining_months - 12)-1]);
                       }else{
                         remaining_days -= (days_per_month[months-1] - days_per_month[(months + remaining_months)-1]);
                       }
borrow = false;
}
}else{
if (future_months - months <0){
remaining_months = 12 - abs(future_months - months);
borrow = true;
}else{
remaining_months = future_months - months;
borrow = false;
}
}

/******************************
/*CALCULATE REMIANING YEARS
/*****************************/

if (borrow == true){
if (future_years - years - 1 <0){
remaining_years = 0;
}else{
remaining_years = future_years - years - 1;
}
}else{
if ((future_years - years) <0){
remaining_years = 0;
}else{
remaining_years = future_years - years;
}
}
}



the resulting remaining time for different date (please note i performed the tests below on 8/17/2013) is:

Code: [Select]
Time Until 1 / 10 / 2014
00 Years : 04 Months : 23 Days
3 Hours : 28 Minuets : 37 Seconds


Time Until 1 / 20 / 2014
00 Years : 05 Months : 02 Days
3 Hours : 26 Minuets : 51 Seconds


Time Until 2 / 10 / 2014
00 Years : 05 Months : 23 Days
3 Hours : 25 Minuets : 50 Seconds


Time Until 2 / 20 / 2014
00 Years : 06 Months : 02 Days
3 Hours : 24 Minuets : 30 Seconds


Time Until 3 / 10 / 2014
00 Years : 06 Months : 20 Days
3 Hours : 23 Minuets : 26 Seconds

Time Until 3 / 20 / 2014
00 Years : 07 Months : 02 Days
3 Hours : 22 Minuets : 21 Seconds

Time Until 4 / 10 / 2014
00 Years : 07 Months : 23 Days
3 Hours : 21 Minuets : 32 Seconds


Time Until 4 / 20 / 2014
00 Years : 08 Months : 02 Days
3 Hours : 20 Minuets : 14 Seconds




Time Until 5 / 10 / 2014
00 Years : 08 Months : 22 Days
3 Hours : 18 Minuets : 19 Seconds


Time Until 5 / 20 / 2014
00 Years : 09 Months : 02 Days
3 Hours : 16 Minuets : 51 Seconds


Time Until 6 / 10 / 2014
00 Years : 09 Months : 23 Days
3 Hours : 15 Minuets : 55 Seconds


Time Until 6 / 20 / 2014
00 Years : 10 Months : 02 Days
3 Hours : 14 Minuets : 52 Seconds


Time Until 7 / 10 / 2014
00 Years : 10 Months : 22 Days
3 Hours : 13 Minuets : 47 Seconds


Time Until 7 / 20 / 2014
00 Years : 11 Months : 02 Days
3 Hours : 12 Minuets : 43 Seconds


Time Until 8 / 10 / 2014
00 Years : 11 Months : 23 Days
3 Hours : 11 Minuets : 48 Seconds


Time Until 8 / 20 / 2014
01 Years : 00 Months : 02 Days
3 Hours : 10 Minuets : 20 Seconds


Time Until 9 / 10 / 2014
01 Years : 00 Months : 23 Days
3 Hours : 9 Minuets : 28 Seconds


Time Until 9 / 20 / 2014
01 Years : 01 Months : 02 Days
3 Hours : 7 Minuets : 18 Seconds


Time Until 10 / 10 / 2014
01 Years : 01 Months : 22 Days
3 Hours : 5 Minuets : 33 Seconds


i choose the days of "10" and "20" within each month to test both cases where "future days" is greater than and less than "today's day).



the main area that i had a correction was when "future_months - months < 0"
because the different months have different numbers of days within them, the code had to take that into account so it knew when the transition to the next month began. that was taken care of by this code:

Code: [Select]
if ((months + remaining_months) > 12){
                         remaining_days -= (days_per_month[months-1] - days_per_month[(months + remaining_months - 12)-1]);
                       }else{
                         remaining_days -= (days_per_month[months-1] - days_per_month[(months + remaining_months)-1]);
                       }



just to make sure i am not doing anything wrong with your code, would you be able to send me your entire code set you used to test your function?  :D

int2str

What issue did you find in my code?
I tried it on the Arduino and noticed there was a variable size issue, but the math still adds app as far as I can tell.

Here's the code I've been testing with:
Code: [Select]
const byte DAYS_IN_MONTH[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

struct TimeStamp
{
    unsigned int yy;
    byte mm;
    byte dd;
    byte hh;
    byte min;
    byte ss;
};

TimeStamp calcDiff(TimeStamp now, const TimeStamp &future)
{
    unsigned long sec_now = now.ss + (now.min * 60) + (now.hh * 3600);
    unsigned long sec_future = future.ss + (future.min * 60) + (future.hh * 3600);
   
    if (sec_future < sec_now)
    {
        sec_future += 86400;
        ++now.dd;
    }
   
    if (future.dd < now.dd)
    {
        now.dd = DAYS_IN_MONTH[now.mm-1] - now.dd + future.dd;
        ++now.mm;
    } else {
        now.dd = future.dd - now.dd;
    }
   
    if (future.mm < now.mm)
    {
        now.mm = future.mm + 12 - now.mm;
        ++now.yy;
    } else {
        now.mm = future.mm - now.mm;
    }
   
    sec_now = sec_future - sec_now;
    now.yy = future.yy - now.yy;
    now.hh = sec_now / 3600;
    now.min = (sec_now - ((unsigned long)now.hh * 3600)) / 60;
    now.ss = sec_now % 60;
   
    return now;
}


void setup()
{
    Serial.begin(115200);

    TimeStamp future = {2015, 2,  17, 18,  0,  0};
    TimeStamp now =    {2014, 2,  17, 18,  0,  1};
   
    TimeStamp diff = calcDiff(now, future);

    char buffer[100] = {0};
    sprintf(buffer, "%d years, %d month, %d days\r\n%d hours, %d minutes, %d seconds", diff.yy, diff.mm, diff.dd, diff.hh, diff.min, diff.ss);
    Serial.println(buffer);
}

void loop()
{
}


If you try the dates that I've put in my sample code, you'll not that your code still has issues with some hours and also days.

You're up to 130 lines of code in your function, including various state variables (like borrow) that are very hard to track in your head. I'd highly recommend you switch to a more math based approach to keep the code compact and make it much easier to debug...

wallaceb

i corrected the issues with hours and days in mine. 

here is side by side comparisons between our calculations:
mine:
Code: [Select]
Time Until 10 / 10 / 2014  - 0 : 0 : 0 hours
01 Years : 01 Months : 21 Days
13 Hours : 9 Minuets : 36 Seconds
yours:
Code: [Select]
Time Until 10 / 10 / 2014  - 0 : 0 : 0 hours
1 years, 1 month, 22 days
31 hours, 21 minutes, 15 seconds


both are counting down until  0:0:0 hours, the codes are using the real time clock on my setup for the current time and so will be off from one-another by around 1 or so minutes as time continues to move.

based on my calculation result , taking a calender, starting on today's date, "flipping " 13 months, that lands me on the 18th of September 2014. if i then add on the 13 hours, 9 minutes, 36 seconds to today, that brings me to midnight 19th of September. moving 21 days from the 19th, brings be to the 10th of October as i wanted

i am not sure how your code is calculating 31 hours, but it does not get the result i am looking for, this is the same issue i said i saw last night.

i understand mine is longer, but i wrote it based on how a person would perform the math on paper by performing date/time subtraction which to me is easy to follow...

i would gladly use your more efficient and elegant code, but i consistently cannot get it to calculate what i am expecting.

my final code appears to be this:

Code: [Select]
/*
#include <SPI.h>
#include <Wire.h>
#include "Wire.h"
byte seconds, minutes, hours, dayOfWeek, dayOfMonth, months, years;
#define DS1307_I2C_ADDRESS 0x68 //address of the RTC on the SPI bus
byte days_per_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};


void setup()  {
  Serial.begin(115200); 
  Wire.begin();//needed to comunicate with the real time clock 
}

double prevDisplay=0;
void loop()
{
  getDateDs1307(seconds, minutes, hours, dayOfMonth, dayOfMonth, months, years);
  char buffer[40];
  byte remaining_seconds, remaining_minuets, remaining_hours, remaining_days, remaining_months, remaining_years;
  if ((seconds + minutes + hours)  != prevDisplay) { //update the display only if time has changed
      prevDisplay = (seconds + minutes + hours);
      calculate_countdown(0,0,0,10,10,14, remaining_seconds, remaining_minuets, remaining_hours, remaining_days, remaining_months, remaining_years);
      sprintf (buffer, "Time Until %.d / %.d / %.d ", 10, 10, 2014);
      Serial.println(buffer);
      sprintf (buffer, "%.2d Years : %.2d Months : %.2d Days", remaining_years, remaining_months, remaining_days);
      Serial.println(buffer);
      sprintf(buffer, "%d Hours : %d Minuets : %d Seconds", remaining_hours, remaining_minuets, remaining_seconds);
      Serial.println(buffer);
      Serial.println();
    }
}
void calculate_countdown(byte future_seconds,
byte future_minutes,
byte future_hours,
byte future_days,
byte future_months,
byte future_years,
byte &remaining_seconds,
byte &remaining_minuets,
byte &remaining_hours,
byte &remaining_days,
byte &remaining_months,
byte &remaining_years){
bool borrow = false;

/******************************
/*CALCULATE REMIANING SECONDS
/*****************************/

if (future_seconds - seconds <0){
remaining_seconds = 60 - seconds;
borrow = true;
}else{
remaining_seconds = future_seconds - seconds;
        borrow = false;
}

/******************************
/*CALCULATE REMIANING MINUTES
/*****************************/

if (borrow == true){
if (future_minutes - minutes - 1 <0){
remaining_minuets = 60 - minutes - 1 + future_minutes;
borrow = true;
}else{
remaining_minuets = future_minutes - minutes - 1;
        borrow = false;
}
}else{
if (future_minutes - minutes <0){
remaining_minuets = 60 - minutes + future_minutes;
borrow = true;
        }else{
                remaining_minuets = future_minutes - minutes;
                borrow = false;
        }
}

/******************************
/*CALCULATE REMIANING HOURS
/*****************************/

if (borrow == true){
if (future_hours - hours - 1 <0){
remaining_hours = 24 - hours - 1 + future_hours;
borrow = true;
}else{
remaining_hours = future_hours - hours - 1;
borrow = false;
}
}else{
if (future_hours - hours <0){
remaining_hours = 24 - hours + future_hours;
borrow = true;
}else{
remaining_hours = future_hours - hours;
borrow = false;
}
}

/******************************
/*CALCULATE REMIANING DAYS
/*****************************/

if (borrow == true){
if ((future_days - dayOfMonth - 1) <0){
                       
                        remaining_days = days_per_month[months-1] - dayOfMonth - 1 + future_days;
                        borrow = true;
}else{
remaining_days = future_days - dayOfMonth - 1;
borrow = false;
}
}else{
if (future_days - dayOfMonth <0){
                        remaining_days = days_per_month[months-1] - dayOfMonth + future_days;
                        borrow = false;
}else{
remaining_days = future_days - dayOfMonth;
borrow = false;
}
}

/******************************
/*CALCULATE REMIANING MONTHS
/*****************************/

if (borrow == true){
if ((future_months - months) - 1 <0){
remaining_months = 12 - abs(future_months - months) - 1;
                        if ((months + remaining_months) > 12){
                          remaining_days -= (days_per_month[months-1] - days_per_month[(months + remaining_months - 12)-1]);
                        }else{
                          remaining_days -= (days_per_month[months-1] - days_per_month[(months + remaining_months)-1]);
                        }
borrow = true;
}else{
remaining_months = future_months - months - 1;
                        if ((months + remaining_months) > 12){
                          remaining_days -= (days_per_month[months-1] - days_per_month[(months + remaining_months - 12)-1]);
                        }else{
                          remaining_days -= (days_per_month[months-1] - days_per_month[(months + remaining_months)-1]);
                        }
borrow = false;
}
}else{
if (future_months - months <0){
remaining_months = 12 - abs(future_months - months);
borrow = true;
}else{
remaining_months = future_months - months;
borrow = false;
}
}

/******************************
/*CALCULATE REMIANING YEARS
/*****************************/

if (borrow == true){
if (future_years - years - 1 <0){
remaining_years = 0;
}else{
remaining_years = future_years - years - 1;
}
}else{
if ((future_years - years) <0){
remaining_years = 0;
}else{
remaining_years = future_years - years;
}
}
}

// Gets the date and time from the ds1307
void getDateDs1307(byte &get_second,
byte &get_minute,
byte &get_hour,
byte &get_dayOfWeek,
byte &get_dayOfMonth,
byte &get_month,
byte &get_year)
{
  // Reset the register pointer
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.write(0);
  Wire.endTransmission();

  Wire.requestFrom(DS1307_I2C_ADDRESS, 7);

  // A few of these need masks because certain bits are control bits
  get_second     = bcdToDec(Wire.read() & 0x7f);
  get_minute     = bcdToDec(Wire.read());
  get_hour       = bcdToDec(Wire.read() & 0x3f);  // Need to change this if 12 hour am/pm
  get_dayOfWeek  = bcdToDec(Wire.read());
  get_dayOfMonth = bcdToDec(Wire.read());
  get_month      = bcdToDec(Wire.read());
  get_year       = bcdToDec(Wire.read());
}


// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}




thanks for all your assistance!  :D

int2str

based on my calculation result , taking a calender, starting on today's date, "flipping " 13 months, that lands me on the 18th of September 2014. if i then add on the 13 hours, 9 minutes, 36 seconds to today, that brings me to midnight 19th of September. moving 21 days from the 19th, brings be to the 10th of October as i wanted


Your code is not taking into considerations the days in the current month. Try it with a current time in February and counting down until March. You'll see what I mean. As I mentioned previously, if you're just going to "flip the calendar" without counting the days remaining in the current month, you can remove the "days_in_month" part of your code completely.

Code: [Select]
i am not sure how your code is calculating 31 hours, but it does not get the result i am looking for, this is the same issue i said i saw last night.

It's not a calculation error, it's an integer overflow. I tested this on a non-avr compiler first and it looks like the avt-g++ is a little daft when it comes to casting variables. Below is the code with manual type casting to make the compiler happy.

Quote
i understand mine is longer, but i wrote it based on how a person would perform the math on paper by performing date/time subtraction which to me is easy to follow...


Hey, it's your project :)

Since software development is my bread & butter, I'd just like to point out that short, easy to maintain code is not only valuable in terms of ROM/RAM space, but much more importantly in portability and maintenance down the road.

Another tip, before I bow out of this thread:
You're mixing passing variables by value (future_*), passing variables by reference (remaining_*) and sneakily using global variables inside your function (minutes, hours,...). This is adding to the overall confusion, makes this function much less reusable for other programs and makes the code that much harder to follow. If nothing else, I recommend you adopt the struct and the function signature I proposed.

Here's the final revision of the code (same as above, just type casting):

Code: [Select]
const byte DAYS_IN_MONTH[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

struct TimeStamp
{
    unsigned int yy;
    byte mm;
    byte dd;
    byte hh;
    byte min;
    byte ss;
};

TimeStamp calcDiff(TimeStamp now, const TimeStamp &future)
{
    unsigned long sec_now = now.ss + ((unsigned long)now.min * 60) + ((unsigned long)now.hh * 3600);
    unsigned long sec_future = future.ss + ((unsigned long)future.min * 60) + ((unsigned long)future.hh * 3600);

    if (sec_future < sec_now)
    {
        sec_future += 86400;
        ++now.dd;
    }

    if (future.dd < now.dd)
    {
        now.dd = DAYS_IN_MONTH[now.mm-1] - now.dd + future.dd;
        ++now.mm;
    } else {
        now.dd = future.dd - now.dd;
    }

    if (future.mm < now.mm)
    {
        now.mm = future.mm + 12 - now.mm;
        ++now.yy;
    } else {
        now.mm = future.mm - now.mm;
    }

    Serial.println(sec_now);
    Serial.println(sec_future);
    sec_now = sec_future - sec_now;
    now.yy = future.yy - now.yy;
    now.hh = sec_now / 3600;
    now.min = (sec_now - ((unsigned long)now.hh * 3600)) / 60;
    now.ss = sec_now % 60;

    return now;
}

Go Up