Step 4: Time keeping

To keep track of time I used only the arduino, and the standard arduino function millis(). This method won't be completely exact, because the 16Mhz crystal oscillator, that decides the clock frequency of the arduinos CPU, probably  won't be exactly 16 000 000 Hz. The good news is that you can measure how inexact your clock is and compensate for it in your code, because the offset should be constant for a given period of time.

Measure how inexact your arduino is:
As said earlier, the arduino will have a small time error, this error depends on the crystal oscillator and will be different for every arduino, to measure how much my arduino clock differed from the correct time, I programmed it to print out the time ( the hour, minute and second variables) via serial. I then let it run for a long time (Over night and more) and compared the arduino time with a clock I knew was exact, at the start and the end of my measuring period. Divide the time difference with the time the test took to calculate the error each hour. I found that my arduino is about 0.4 seconds to fast every hour. I used exacttimenow.com to get exact time, but if you have a watch you know is very exact, feel free to use that instead.
The code I used to keep the time is an adaptation of some code I found on the arduino forums. I rewrote it with if-statements instead of division and modulo, to see if there would be any speed difference and found that the if-version is more than 15 times faster (although both are still quite fast, more info about the test here).

Since I want the other stuff in my main loop (like checking the touch sensor, checking for button presses, etc.) to happen as often as possible, I used the faster version.

The code:

Every time the clock function is called it adds the time in milliseconds since last it was called to a variable m, when one second has passed (m>999) the second variable is increased by one and m is reset. When the seconds variable reaches 60, the minute variable will be increased by one, and seconds will be set to zero. The same thing happens with the minutes variable; when it reaches 60, add 1 to hours and reset minutes. The hour variable will be reset when it reaches 24.

To compensate that my arduino is 0.4 seconds faster evey hour, I decrease the seconds with two seconds every fifth hour.


The clock() function:

#define MAX_MILLIS_VALUE 34359738
unsigned long current_millis_value = 0;
unsigned long previous_millis_value = 0;
unsigned long m = 0;
int seconds = 0;
int minutes = 0;
int hours = 0;

void clock()
  current_millis_value = millis();
  if (current_millis_value < previous_millis_value) // if millis overflows
    m += MAX_MILLIS_VALUE - previous_millis_value + current_millis_value;
  else // if millis has not overflown
    m += current_millis_value - previous_millis_value;
  if (m>999)
    m = m-1000;
  if (seconds>59) // if seconds == 60
    seconds = 0;
  if (minutes>59) // if minutes == 60
    minutes = 0;
    if(hours%5==0) // adjust the time with -2 seconds every 5th hour.
      seconds = seconds - 2;  // this will cause seconds to be -2,
                             // therefore seconds can't be unsigned.
  if (hours>23) // if hours == 24
    hours = 0;
  previous_millis_value = current_millis_value;


if i want to add mp3 tones, can I do it ?!
<p>The code is not compiling. I've put both files in the library folder like you said. But I'm getting these errors...</p><p>binary_clock_v1:24: error: 'CapSense' does not name a type</p><p>binary_clock_v1.pde: In function 'void loop():</p><p>binary_clock_v1:123: error: 'touch_sensor' was not declared in this scope</p><p>d_button_code.pde: In function 'void update_buttons_state()':</p><p>d_button_code:8: error:'touch_sensor' was not declared in this scope</p><p>I would appreciate any help that can be given. I've been working on this all day. Thanks.</p>
<p>Hi, I'm thinking about making this project but I don't quite understand the touch sensor. Do I have to buy the pins? or is it only made of the auliminium foil, etc? How can I look for those pins in amazon, because when I look for touch sensor other stuff appear... Thanks btw</p>
<p>this is a great instructable! Very well documented, with exception that a schematic is missing, which makes it hard to understand how you hooked up the buttons. I was wondering, how would you modify the code for a momentary switch for the snooze. I'm building this in an old alarm clock housing that has the snooze switch built into the feet of the clock. A reply back would be greatly appreciated. </p>
This could be useful to detect individual differences from crystal to crystal, however, a crystal oscillator is affected by environmental factors as well. Mainly temperature, power supply ripples, even age of the crystal :( <br>I found this informative article http://kunz-pc.sce.carleton.ca/thesis/CrystalOscillators.pdf if you want super details. My latest arduino clock uses the DS3231SN chip (ChronoDot RTC product) which (in reasonable temperatures) is accurate to better than one minute per year drift. And the battery backup means you don't have to set the clock every time you loose power.
Really great project! Thanks a lot, this one inspired me to do my <a href="http://www.instructables.com/id/Arduino-Binary-Alarm-Clock-pocket-sized/ ">pocket binary alarm clock</a> !
hi, <br>when i go to upload to my arduino and it verifys it first it comes up with an error saying 'CapSense' does not name a type. i am only trying to upload the binary_clock_v1 file am i doing something wrong or missing something?
Do you have the CapSense library?<br><br>&quot;Step 13: Upload the arduino sketch.<br><br>Make shure you have the CapSense Library and the Tone Library, they can be downloaded here, and here.<br>Unzip and place the folders in the folder libraries in your sketchbook folder. &quot;
Works like a charm.
Where are the wires from the buttons going? Great project!
To the arduino, look at the 5th picture in step 14. <br>From step 3:<br>&quot;Here are the pins I used for the different parts:<br><br>- 2 and 4: Touchsensor (4 is the send pin, 2 is recieve)<br>- 6 Hour button<br>- 5 Minute button<br>- 7 Switch<br>- 14-18 Display hour pins<br>- 8-13 Display minute pins&quot;
how can I not use the touch sensitive on/off/snooze feature? I am pretty new to electronics, so any help would be great.<br><br>Thank!, and great instructable
Where did you get that box from?
I got it when I bought a tie + napkin + cufflink kit. The little window was so you could see the color of the tie.
When ever you do find out please post it so it would be possible to&nbsp; compensate for it every 6 or so months.&nbsp; =-)
&nbsp;I'm not shure what you mean, but if you mean the clock error, then I have as you can read already compensated for it in the code, the discussion below is just about other possible ways to do it. I have however not tested a very long run of the clock (several weeks) without resetting yet because I have had some problems with the touchsensor being oversensitive and after a couple of days trigger alarm on/off constantly. I think I may have to ground the arduino better, the <a href="http://www.arduino.cc/playground/Main/CapSense" rel="nofollow">capsense page</a> say that it's important.
I&nbsp;just want to say how lovely this is, both the clock (which is aesthetically pleasing and clever), and the instructable. I'm a writer for my day job and I do textile crafting for my hobbies (sewing, knitting, spinning, etc.) but I actually understood what you were doing here, which means you did a great job.&nbsp; I used to know a little C++ and this and that, when I was way, way younger, and I followed along the code bits you show surprisingly well, too, because you were so clear what it was all about.<br /> <br /> I've always wanted to learn this kind of thing but don't know a single soul who knows much beyond not using the wrong adaptor for the printer (and some don't know that much!).&nbsp; The more instructables I read, though, the more convinced I am that I'll have to make one, some day.&nbsp; It's getting harder to resist.&nbsp; :)&nbsp; <br /> <br />
Thank you for the kind comment, I'm glad you liked it!<br /> <br /> I didn't know anyone that could help me much when I started learning about electronics etc. either, but the internet is full of information and you can ask at forums if you get stuck.<br />
Have you thought about using ternary operators?&nbsp; They're basically if() statements, but use a slightly lower-level structure.&nbsp; I'm not sure if they run faster under the Arduino, they may actually end up compiled the same since it's an embedded environment, but basically, it goes like this:<br /> <br /> condition ? run if true : otherwise run this;<br /> <br /> So you could do that first if() statement like this:<br /> <br /> current_millis_value &lt; previous_millis_value ? m += MAX_MILLIS_VALUE - previous_millis_value + current_millis_value : m += current_millis_value - previous_millis_value;<br /> <br /> It's not as pretty, but as always, C ignores whitespace, so you can write it like this if you want to organize it a little:<br /> <br /> current_millis_value &lt; previous_millis_value ?<br /> m += MAX_MILLIS_VALUE - previous_millis_value + current_millis_value :<br /> m += current_millis_value - previous_millis_value;<br /> <br /> It might run faster and eliminate some of the timing error.<br /> <br /> Also, you might be able to spread out the time correction a little more evenly than 2 seconds every 5 hours.&nbsp; Since it's .4 seconds, that's 400 milliseconds every 60 minutes.&nbsp; That goes down to 20 milliseconds every 3 minutes.&nbsp; Since you're counting milliseconds directly, and the overall count of seconds is a multiple of 3, it might be overall a little more accurate to the user to do the correction more often.<br /> <br /> Great tutorial though, I might have to try this!<br />
No I haven't thought about using ternary operators there. However I think if-statemets may be a bit easier to read. Maybe one should do a test of ifs vs. ternary, but I think they are equally fast.<br /> <br /> About the time compensation, yes I have thought about that and maybe I will add it later. Since the clock only shows minutes I don't think it matter that much. The advantage with that method is that it's easier to compensate for cases when you have for example 0.43 instead. <br /> <br /> Hmm, I somehow thought It would be more complicated to compensate the milliseconds, but now when I think about it, it should be as easy, one just have to make the millis variable signed, so the subtraction won't cause it to underflow. <br /> <br /> Thanks for the suggestions, I'm glad you like the instructable!<br />
Leave it unsigned.&nbsp; You could just as easily have it count up to 1001 instead of 999.&nbsp; This would save issues of the clock wondering what time it really is.<br /> <br /> Yeah, I'll have to see how ternary operators and if() statements compare when I finally get an Arduino (Christmas is coming, as my plan currently stands).&nbsp; For a lot of embedded environments, all those conditional structures get compiled down the same way anyway.&nbsp; I think&nbsp;I recall that the C18 compiler (for PIC microcontrollers, like the one in my VEX robot) compiles if() and switch() statements to the same thing.&nbsp; I wouldn't be surprised if it takes while() loops down to if()-goto structures.&nbsp; Or I guess I could just look at the avr-gcc manual, but that would be too easy :D<br /> <br /> I think I might eventually try this project, I've wanted a binary clock for a while, and I love the AVR line.&nbsp; One thing that I found last night is that the Arduino bootloader still allows you to use the lower-level avr-gcc commands.&nbsp; It turns out that digitalWrite() takes about 10 times longer to run than accessing the PORT register directly, because with each call of digitalWrite(), it turns off all of the PWM&nbsp;timers (and in the comments in the code, the guy is asking why that couldn't be moved up to pinMode(), which is interesting).&nbsp; Basically, digitalWrite() is just some safety mechanisms and a parser for the PORT register.&nbsp; It sets the PORT of your choice (lets say pin 13, which is under PORT B, pin 5) to an entire byte all at once (you can toggle 8 pins just about simultaneously).&nbsp; This would work like this, IIRC:<br /> <br /> PORT_B = 0b00010000;<br /> <br /> Or, to only affect that pin, you can use the | operator:<br /> <br /> PORT_B |= 0b00010000;<br /> <br /> You don't have to make these modifications, I realize it would take a long time to recalibrate, just thought you might be interested.&nbsp; I still love the tutorial.&nbsp; I think it might also be interesting to make a multiplexed or charlieplexed version, just for funsies.<br />
<em>&quot;Leave it unsigned.&nbsp; You could just as easily have it count up to 1001 instead of 999.&nbsp; This would save issues of the clock wondering what time it really is.&quot;</em><br /> <br /> Wouldn't that just work if you want to correct at least 1 millisecond every second (60ms per minute) wich is more than I want to?<br /> <em><br /> &quot;[...]<br /> It turns out that digitalWrite() takes about 10 times longer to run than accessing the PORT register directly, because with each call of digitalWrite(), it turns off all of the PWM&nbsp;timers (and in the comments in the code, the guy is asking why that couldn't be moved up to pinMode(), which is interesting).&nbsp; <br /> PORT_B = 0b00010000;<br /> [...]&quot;</em><br /> <br /> Actually there have been much discussion about how to make digitalWrite faster on the arduino developers mailinglist lately (<a href="http://arduino.cc/pipermail/developers_arduino.cc/2009-November/001764.html" rel="nofollow">link</a>), maybe digitalWrite compiled to one instruction will come in the next version of the arduino ide.<br /> I think I'll just wait for that since using digitalWrite makes the code cleaner and easier to understand*, and It works&nbsp; good as it is. I don't have much time now anyway to tinker with the clock, because I have calculus exam soon :S.<br /> <br /> * = for me at least, I'm not used to the PORT stuff since I have mostly used the arduino. I would like to learn more how microcontrollers really work sometime though. The arduino hides all complexity, which is really nice, and makes it easy to make stuff with it,&nbsp; but it would be intresting to learn how it works low-level.<br /> <br /> <em>&quot;You don't have to make these modifications, I realize it would take a long time to recalibrate&quot;</em><br /> <br /> I don't think I would have to recallibrate, the time error should come from the crystal oscillator frequency and not from how long it takes to execute the loop. <br /> <em><br /> &quot;I think it might also be interesting to make a multiplexed or charlieplexed version, just for funsies.&quot;</em><br /> <br /> Yes, I would like to experiment with charlieplexing too sometime. <br />
Oh, yeah, with the &quot;1001 instead of 999&quot; thing, I was thinking 2 milliseconds instead of 20.&nbsp; And actually, you could tack on 7 milliseconds every minute, and then 6 every 3 minutes, like so:<br /> <br /> if(seconds==59 &amp;&amp; m &gt; (!minutes%3 ? 999+6 : 999+7))&nbsp;{&nbsp; //If divisible by 3, add <br /> &nbsp;&nbsp;&nbsp; seconds++;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; //18 ms, otherwise 19<br /> &nbsp;&nbsp;&nbsp; m = 0;<br /> }<br /> else if(m&gt;999) {&nbsp; //Normally, a second is just 1000ms<br /> &nbsp;&nbsp;&nbsp; seconds++;<br /> &nbsp;&nbsp;&nbsp; m = 0;<br /> }<br /> <br /> I&nbsp;think how you do it with the 2 seconds every 5 hours is fine by the way, this is more just a point of discussion.
Just wondering, why didn't you use a RTC for timekeeping? Nice guide btw.<br /> <br /> -Simon<br />
I didn't use one because I didn't have one, and it's cheaper to use just code :) but maybe I will add it in a later version if I can't get the clock to be as exact as I want, but It seems to work OK without for now.<br />
Yeah, it'll start drifting by a lot after a while. You should use <a href="http://macetech.com/store/index.php?main_page=product_info&amp;cPath=5&amp;products_id=8" rel="nofollow">this</a>. I read that it is very good. You can even get one that is the electrical equivalent, but the headers are soldered on the wrong way, for half of the price.<br />
It will drift a bit, but as you can see in the instructable I have compansated for the drift I could measure in the code so I don't think it will be more than a couple of minutes per year. I'll have to wait and see I guess. <br />
&nbsp;This is so cool! Thanks for posting this, this is amazing. I definitely like this binary clock system rather than Binary Coded Decimals.
Thank you! I'm glad that you liked it.<br />

About This Instructable


86 favorites


More by njakol: Arduino Binary Alarm Clock
Add instructable to: