POOR MAN'S RTC

Disclaimer: I'm pretty new to all this.

Hi, I'd like to make a time of day based event, such on a certain hour (or several), something happens. Down the road maybe an RTC makes sense, but it would be cool to make one from scratch.

Here's what I got so far (Thanks to guix to help me get started)

unsigned long  time_old;
unsigned long  time_new;
int hours = 0;

void setup()
{
  time_old = millis();
}

void loop()
{
  time_new = millis();  
   if(time_new - time_old >= (60 * 60 * 1000UL ))
   {
     hours++;
   }

   if(hours == 4)
   {
     //do something 
   } 

   if(hours>= 24)
   {
     time_old = time_new;
   }
  
}

Will this work? Will this keep time at all accurately? Will it not if I use delay() in my "do something" section?

Also, if I power cycle the Uno at noon, would it reset the clock?

cosmos275:
Will this work? Will this keep time at all accurately?/

Depends on how you define "accurately"

Will it not if I use delay() in my "do something" section?

Not likely, but it depends on how you are using delay and how you are using the time.

Here is a time library on the playground. I have not used this but it may be what you are looking for.

http://playground.arduino.cc/Code/Time

Hmm, a minute per day accurate maybe?

So, I reduced the counter so I could debug... doesn't seem to work. I'd expect every 10 seconds for hours to increment, then print to serial, etc. every 10 seconds, but what happens is after 10 seconds, it prints 1 to 24 really fast, then nothing. Any ideas? Thanks

unsigned long  time_old;
unsigned long  time_new;
int hours = 0;

void setup()
{
  time_old = millis();
  Serial.begin(9600);
}

void loop()
{
  time_new = millis();
  
   if(time_new - time_old >= (10 * 1000UL )) //shortened for debug
   {
     hours++;
     Serial.println(hours);
   }
 
   if(hours == 4)
   {
     //do something 
   } 
   
   if(hours>= 24)
   {
     time_old = time_new;
   }
  
}

Edit: adding " hours = 0;" to the last IF makes the same thing repeat every 10 seconds, not 240 :frowning:

Also, if I power cycle the Uno at noon, would it reset the clock

Everything gets reset in a power cycle. If it is not exactly at noon then hours will not match real time.

Ok, got it, needed to reset time_old each ~hour.

if(time_new - time_old >= (1 * 1000UL )) //shortened for debug
   {
     hours++;
     time_old = time_new;  //new line
     Serial.println(hours);
   }

cosmos275:
Will this work? Will this keep time at all accurately? Will it not if I use delay() in my "do something" section?

With the standard Uno, no it won't be accurate enough for a clock, since the Uno uses a
ceramic resonator rather than a quartz crystal as its time base. It is possible to resolder
a quartz crystal and caps in place of the resonator, but it is a pretty fiddly job. The
Duemilanova (previous standard version of Arduino) used a quartz crystal, but alas the
Uno doesn't.

Calling delay() has little effect on your code if the delays are short - the checks for the hour will be held up
but the time accounting will still work.

MarkT:
won't be accurate enough for a clock, since the Uno uses a
ceramic resonator rather than a quartz crystal as its time base....

.....the checks for the hour will be held up
but the time accounting will still work.

Cool, this will be enough to get me going and maybe later I can get a real RTC. Is there not a Arduino model currently being produced with a quartz?

Thanks!

Will this keep time at all accurately?

It will drift, by the duration of the main loop.

The best is through an interrupt.

If you have to use millis(), this is what I would do:

#define PR_1s 1000ul //duration of 1 second, in ms
#define PR_1m (60 * PR_1s) //duration of 1 minute
#define PR_1h (60 * PR_1m) //duration of 1 hour
#define PR_ERROR 0 //error term

#define PR PR_1m //duration set to 1 minute

void loop(void) {
  static time = millis(); //reset current time
  if (millis() - time >= PR) {//desired time has elapsed
    time += PR + PR_ERROR; //update time, with error correction
    //do something
  }
}

The code is self-contained and allows you to correct for timing errors.

Its accuracy is only determined by the crystal.

On the other hand, a DS1307 & crystal are pretty inexpensive
http://www.dipmicro.com/store/DS1307N
http://www.dipmicro.com/store/XC4-32768
Use two 4.7K pullups on the SCL, SDA lines

Battery & holder
http://www.dipmicro.com/store/BAT-CR2032
http://www.dipmicro.com/store/BH120591-1

So, about $1.25 total?

I would change it slightly:

#define PR_1s 1000ul //duration of 1 second, in ms
#define PR_1m (60 * PR_1s) //duration of 1 minute
#define PR_1h (60 * PR_1m) //duration of 1 hour
#define PR_ERROR 0 //error term

#define PR PR_1m //duration set to 1 minute

unsigned char rtc_tick(void) {
  static unsigned long time = millis(); //reset current time
  if (millis() - time >= PR) {//desired time has elapsed
    time += PR + PR_ERROR; //update time, with error correction
    return 1; //return a tick
  } else return 0;
}

Whenever the duration defined by PR has passed, rtc_tick() returns 1, making time counting quite easy.

CrossRoads:
On the other hand, a DS1307 & crystal are pretty inexpensive

they are expensive in terms of memory, everyone uses the wire library to control the things, which directly states "I am going to eat your ram!" just to return a string you could write on a band-aid

@dhenry
nice implementation !

minor point, I would use uint8_t instead of unsigned char as return type to indicate it is really an int. Or boolean true / false? ==> in short it depends.

CrossRoads:
On the other hand, a DS1307 & crystal are pretty inexpensive
DS1307 64 x 8 Serial Real-Time Clock DIP8 - dipmicro electronics
32.768kHz Crystal Cylinder - dipmicro electronics
Use two 4.7K pullups on the SCL, SDA lines

Battery & holder
Lithium Battery CR2032 Ф20mm 225mAh 3V CR2032 - dipmicro electronics
Plastic Battery Holder 1-cell ø20mm PC 120591-1 - dipmicro electronics

So, about $1.25 total?

It's the other $40 I spend to justify shipping that gets me....

1307 boards are cheap off ebay.
A bit over $2 USD including shipping:

The boards even come with an eeprom and have a spot for a 18sB20 1 wire temperature sensor.
(some even come with it already soldered in).

One of those, the Time library along with TimeAlarms library and you are good to go.

--- bill

And a DS3231 for less than 6$

http://www.ebay.com/itm/RTCpro-DS3231-Precision-Clock-Module-With-Temperature-Arduino-For-AVR-PIC-51-ARM-/150956915543?pt=LH_DefaultDomain_0&hash=item2325bbbb57

dipmicro has really inexpensive shipping. Can get a whole box of parts for just $3-4 in shipping.