Introduction: Timekeeping on ESP8266 & Arduino Uno WITHOUT an RTC (Real Time CLock)?

Picture of Timekeeping on ESP8266 & Arduino Uno WITHOUT an RTC (Real Time CLock)?

Like the title says, I have tested keeping track of time on my arduino uno and the ESP8266 / Wemos D1 boards(programmed with arduino) WITHOUT the use of libraries, internet or an RTC unit. A lot of great instructables here on the site tell you to use an RTC for time related projects. I wanted to test if that was really an added value and share the results with you guys. I wrote a simple time keeping sketch that will work on ANY arduino compatible microcontroller. The sketch is attached and fully explained in this instructable. I ran the sketch for about 72 hours to see how far off my boards would be on timekeeping without all on their own. I added an extra piece of code afterwards to correct for any error a board may have (running fast/slow). The conclusion: for 99 procent of time related arduino projects here on instructables YOU DO NOT NEED anything else than a microcontroller. To see how I wrote the code, get the code, got to my conclusion on accuracy and to learn more about time keeping accuracy in arduino, read on!

Step 1: PRO's and Cons of RTC Units

Picture of PRO's and Cons of  RTC Units

As an electronics, IOT and pyro enthousiast, I do a lot of projects that involve time keeping. Conventional wisdom on instructables and other tutorial sites is that for accurate time keeping, you need an RTC unit (see pic, click here to read more on RTC's). An RTC (Real time clock) is a seperate chip with a battery that keeps track of time for years.

The pros are clear:

- Time keeping is really precise
- RTC's have their own battery and remember time even when your project is powered off
- RTC's keep track of time even when your arduino crashes or gets reset

But what many instructables fail to mention is that there are also a lot of cons and "meh"s:

- You lose two pins you could have used for something else, when working with esp8266 based boards this really matters
- You need to buy yet another part and wait for it to ship. It can easily delay your project if you order from the far east
- RTC's are relatively bulky. I do most of my projects in 10 x 4 x 6 cm x cases. The RTC does NOT fit.
- It's extra work: you need to solder it, program it seperately. install libraries, cram it in your project.
- when you open the serial monitor, the RTC resets in the basic time set program, a nuissance
- Many of them are not that precise either, the cheaper ones are known to be off several seconds a day, source
- Most RTC libraries and instructables used here do NOT include code to change time back/from day light savings, meaning you have to manually reset time every six months anyways.

Step 2: THE ACTUAL TEST: How Accurate Is an Arduino Uno / ESP8266 Without RTC?

Picture of THE ACTUAL TEST: How Accurate Is an Arduino Uno / ESP8266 Without RTC?

"But the arduino uno / ESP8266 / Wemos is inaccurate without RTC and I need accuracy above all else!" - says the skeptic. Almost all arduino clock instructables therefore feature an RTC unit, even if they are never going to be switched off and dont require the back up function.

Well I was skeptic too. But I was frustrated with complexly coded internet applications, bulky RTC units , librairies that werent universally compatible, and frustrated with the sloppyness of it all. So I wrote my own sketch to keep time that will work on ANY arduino compatible microcontroller. The sketch is based only on the millis() function. This function works on any microcontroller and all it does is count the milliseconds that have elapsed since your last reset. You will find the sketch below including comments explaing everything and I also attached it to this instructable in a seperate .ino file. It is dirt simple to make if you follow my comments and you will easily be able to make your own custom version based on your time keeping needs:

//Written by Ruben Marc Speybrouck

unsigned long timeNow = 0;

unsigned long timeLast = 0;

//Time start Settings:

int startingHour = 12;

// set your starting hour here, not below at int hour. This ensures accurate daily correction of time

int seconds = 0;

int minutes = 33;

int hours = startingHour;

int days = 0;

//Accuracy settings

int dailyErrorFast = 0; // set the average number of milliseconds your microcontroller's time is fast on a daily basis

int dailyErrorBehind = 0; // set the average number of milliseconds your microcontroller's time is behind on a daily basis

int correctedToday = 1; // do not change this variable, one means that the time has already been corrected today for the error in your boards crystal. This is true for the first day because you just set the time when you uploaded the sketch.

void setup() { // put your setup code here, to run once:

Serial.begin(9600); }


void loop() { // put your main code here, to run repeatedly:

timeNow = millis()/1000; // the number of milliseconds that have passed since boot

seconds = timeNow - timeLast;

//the number of seconds that have passed since the last time 60 seconds was reached.

if (seconds == 60) {

timeLast = timeNow;

minutes = minutes + 1; }

//if one minute has passed, start counting milliseconds from zero again and add one minute to the clock.

if (minutes == 60){

minutes = 0;

hours = hours + 1; }


// if one hour has passed, start counting minutes from zero and add one hour to the clock


if (hours == 24){

hours = 0;

days = days + 1;

}

//if 24 hours have passed, add one day

if (hours ==(24 - startingHour) && correctedToday == 0){

delay(dailyErrorFast*1000);

seconds = seconds + dailyErrorBehind;

correctedToday = 1; }

//every time 24 hours have passed since the initial starting time and it has not been reset this day before, add milliseconds or delay the program with some milliseconds.

//Change these varialbes according to the error of your board.

// The only way to find out how far off your boards internal clock is, is by uploading this sketch at exactly the same time as the real time, letting it run for a few days

// and then determining how many seconds slow/fast your boards internal clock is on a daily average. (24 hours).

if (hours == 24 - startingHour + 2) {

correctedToday = 0; }

//let the sketch know that a new day has started for what concerns correction, if this line was not here the arduiono // would continue to correct for an entire hour that is 24 - startingHour.

Serial.print("The time is: ");

Serial.print(days);

Serial.print(":");

Serial.print(hours);

Serial.print(":");

Serial.print(minutes);

Serial.print(":");

Serial.println(seconds);

}


I ran the code on both a wemos D1 and an arduino uno. I set the time equal with the time from:http://www.timeanddate.com/ on midnight day zero (you obviously dont have to start at midnight! - I included time set code above void setup). After one day and 9 hours the difference between the time on my microcontrollers and this website was about one second. I checked this by taking a screenshot of my serial monitor on one side of the screen and having the webpage open on the other side. (see pic).


After about 3 days, on both my arduino and wemos the clock was about 2 to 2,5 sec fast. that is 274 seconds a year off or just over four and a half minutes. That's unacceptable!!! No, not really. I just added a line of code to my sketch that corrects the time on my microcontrollers either by delaying the program just a little every 24 hours or by adding a few milliseconds every day. In my case about 0,75 seconds every day. And there you have it, long term near second precise timekeeping with no fuss on any board!

Here is the correction code all together in practice once again so you could easily see the whole picture

//Accuracy settings

int dailyErrorFast = 0; // set the average number of milliseconds your microcontroller's time is fast on a daily basis int dailyErrorBehind = 0; // set the average number of milliseconds your microcontroller's time is behind on a daily basis int correctedToday = 1; // do not change this variable, one means that the time has already been corrected today for the error in your boards crystal. This is true for the first day because you just set the time when you uploaded the sketch.:

if (hours ==(24 - startingHour) && correctedToday == 0){

delay(dailyErrorFast*1000);
seconds = seconds + dailyErrorBehind; correctedToday = 1; }

//every time 24 hours have passed since the initial starting time and it has not been reset this day before)

if (hours == 24 - startingHour + 2) { correctedToday = 0; }

//let the sketch know that a new day has started for what concerns correction, if this line was not here the arduiono
// would continue to correct for an entire hour that is 24 - startingHour.

Step 3: Conclusion: You Just Dont Need an RTC, Seriously.

Picture of Conclusion: You Just Dont Need an RTC, Seriously.

You only need an RTC if you are going to keep track of time while your device is switched off completely. Otherwise using an RTC is a huge HASSLE WITHOUT ANY added value. The ESP8266, arduino uno and most likely many other boards are perfectly capable of keeping track of time all on their own. What inaccuracies they do have can easily be corrected through my sketch by adding / reducing a few milliseconds every 24 hour hours.

Get out there, build clocks, dont waste time and money where you dont have to!

Oh, and please check out my other instructables and vote for me if you liked this one ;)

Comments

banjabi1 (author)2017-07-21

hi
does this actually work at all?
i have a "delay(30000);" function at the end of the code to repeat the loop in every 30 seconds, but now it seems to be counting seconds up to xxxxx instead of adding +1 to the minutes...
so is there a way to fix this?
i tried to replace "== 60" with "> 60" and ">= 60", but then i did not get any updates to serial monitor at all anymore... and when i now changed it back to "== 60", i still get nothing.....

zencuke (author)2017-03-06

For example I made a simple digital clock with the Adafruit Feather Huzzah (their fancy esp8266) and a 4 digit 7 segment display feather wing. No RTC, no battery. Just a little software to make NTP requests. It is ALWAYS right. The only problem is that NTP returns UTC time. Timezone correction needs to be added to the software (using the Timezone library) but only if you want to display the time to a human in their own timezone. Most devices use UTC internally and only convert to local time if/when a human needs it. UTC is always correct everywhere in the world. A common variation is to primarily use your method (millis() with a correction factor) but automatically re-calibrate current time and correction factor once in a while with NTP.

zencuke (author)2017-03-06

@Eric Brouwer The 4th method for a network device like esp8266 is to look up the time on the network. There are a bunch of ways to do that but the most common way is to use NTP (Network Time Protocol) and ask the ntp server pool (pool.ntp.org.) That is what your desktop computer does. I'm sure there is an instructable for it. Google "esp8266 ntp time) certainly shows lots of examples.It is a simple piece of code.

AChynchenko (author)2017-02-03

I have used your code and in half an hour got an error of 10seconds. I know what will you propose, correct the time every hour?

seyozen (author)2016-12-25

Thank you very much! Now i got a few battery holder with your favor :)

kluzz (author)2016-11-27

This is all well and good if all your micro controller ever does is keep track of time, but once you start interacting with peripherals, it all goes sideways. One example is the Adafruit NeoPixel library, which is used to control strands of WS2811/2812 LEDs (and compatibles). Because of the tight timing needed to interact with these LED controllers, the library disables all interrupts, which basically means that time stops. Depending on the length of the LED strands, this could run up in the millisecond range every time the LEDs are updated, potentially causing quite significant time drift. Combine this with interactions with other non-deterministic peripherals (such as reading from an ultrasonic distance sensor, or a DHT11/22), the time drift could be very unpredictable, and any attempt to compensate for this in software would fail. Hence the need for external time keeping, or in the case of network enabled controllers, such as the esp8266; NTP.

onion2 (author)2016-05-22

I use this code too, but use "seconds" in "long" var and don't use millis()/1000, just millis(). The program will be better with it

Could you elaborate? I didnt entirely get what you meant

Long seconds=0;

.......

timeNow=millis();
second=timeNow-timeLast

if (second>=60000){........



//I think it's better to do it, because when you do millis()/1000, the result is not perfect on my arduino...
When millis() is to big for the arduino, you must restart it.

It is a great instructable, I learnt a lot. Thank you !
(and sorry for my poor english)

Thank you so much for your response.
I havent found any noticable improvements doing what you did, but maybe some future readers will!

I believe the arduino just resets millis by iteself and starts from zero without resetting completely. Unfortunately I have not found away to code a solution for that that prevents data loss.

The counter holding the millis value would roll over.

You would do this... On power up set an 'overflow' variable to zero and a 'last millis' variable to zero.

When you read millis you check to see if the current value is less than 'last millis' and if it is you increment the 'overflow' variable by 1. You then set 'last millis' to the current millis value.

When you want to work out the total uptime you multiply the 'overflow' value by the maximum value that millis could be then add the current millis value and return that.

Thank you so much for your response.
I havent found any noticable improvements doing what you did, but maybe some future readers will!

I believe the arduino just resets millis by iteself and starts from zero without resetting completely. Unfortunately I have not found away to code a solution for that that prevents data loss.

Thank you so much for your response.
I havent found any noticable improvements doing what you did, but maybe some future readers will!

I believe the arduino just resets millis by iteself and starts from zero without resetting completely. Unfortunately I have not found away to code a solution for that that prevents data loss.

diy_bloke (author)2016-10-03

interesting. and I agree with most of the pro and cons, but one I do not understand:

"- when you open the serial monitor, the RTC resets in the basic time set program, a nuissance"

I
do not see that happen. Once you set the time in an RTC it stays there.
Why would opening the serial monitor bring you back to the time set
menu.. that depends all on what you put in the setup.

'losing 2 pins' is relative. If one uses i2c peripherals an additional RTC doesnt make a difference.

RTC modules have an extra advantage: usually they come with an eeprom as well whereas the ds1307 also gives you some extra Non Volatile RAM

ArunG9 (author)2016-07-19

is it possible to emulate RTC in Arduino using a library to work with existing RTC related projects?

I m pretty syre it is possible, but I could not tell you how from the top of my head

ArunG9 (author)2016-07-19

I want to emulate ds3231 within arduino . Is it possible?

Eric Brouwer (author)2016-05-22

HI, and thanks for sharing this info. Done similar coding using timer interrupts on MicroChip controllers.

Timekeeping all depends on the application. And with almost all Arduino or uC projects, there is NO SINGLE WAY one must use to get to a specific result.

I agree with you that a lot more overhead is required to make use of a RTC (buttons and screen to set time, extra lines of code). However, after you have done it once, you will find that it is not that difficult to populate into designs already using some sort of display.

There are basically three methods to keep time, each method with it's own advantages/disadvantages as you mentioned.

Method 1 - Software using millis()

Good for everyday accuracy. But no backup on loss of power. Ideal for most applications not requiring the correct time, used for timing purposes only. Remember to check if millis() overflow. I think it is every 54 days. Average short term accuracy if each circuit is not tweaked in software. Accuracy varies between Arduinos or stand-alone circuits.

WHY: Exact timing is not required for my gate access control system. When turned on, it should stay activated for one hour only, and turn itself off afterwards. Or emergency lights to stay on or perform specific timing sequences when the power fail.

Method 2 - Line frequency

By using an interrupt pin connected to a 50Hz/60Hz signal, very accurate timing can be obtained. Will only be out a couple of seconds/year. Most utilities generating power, and have to stay withing a frequency band over a year period. Ideal for daily/weekly/monthly time delays. But no backup on loss of power.

WHY: I do not need the exact time for a pool filler which turn on a solenoid for 30 minutes every 7 days, or a charge controller which turns on for 1 hour every day to keep my power tool batteries charged.

Method 3 - Real Time Clock

Only really needed if the actual time and date is important. For instance a control timer, clock etc. Backup in event of loss of power. Accuracy not bad at all. With a standard DS1307 and standard 32kHz crystal without tuning capacitors, accuracy is within 2 minutes per year.

WHY: I do not want to set the time each time I turn on my big LED clock, loos power to a power fail monitor, or 8 channel programmable timer controlling my house lighting. By using I2C LCD backpacks with your LCD display, no extra pins are required for the RTC. Uses the same I2C lines as the display.

Hey Eric,

Sorry for the slow response.
You pretty much could have written this instructable better than I did.
Thank you for the wonderful contribution to this instructable. You are clearly more educated on the topic than I am.

I agree an RTC is not much overhead if you do really need it, but I wrote this instructable to show people that many projects simply dont need one

Hi Ruben.
Thanks for the nice words. It is not that I am more educated than you. I am simply too lazy to fine tune the timing using code :) :).
And since I moved from MicroChip assembler coding to Arduino C, it was so much simpler to add RTC's to almost any circuit.

Hey Eric,

Sorry for the slow response.
You pretty much could have written this instructable better than I did.
Thank you for the wonderful contribution to this instructable. You are clearly more educated on the topic than I am.

I agree an RTC is not much overhead if you do really need it, but I wrote this instructable to show people that many projects simply dont need one

gada888 (author)2016-05-28

Hi,Ruben

Thanks very much for your detailed explaination.I am posting one of my questions as below:

How to set correct time of each day from 8:00 AM to 20:00PM as daytime;and 20:00PM to nex day 8:00AM as sleep time?

You set the startinghour in var starting hour, starting seconds in seconds, starting minute in minutes. You upload your sketch and then you press the reset button on your board exactly when the real world time is eqaul to the starting time you just set.


You check with the serial monitor if this worked. You let the code run for a few days and check the serial monitor again. You will now likely see a difference between the time on your arduino and the real world time. Taking a screenshot is the easiest way to spot this.

Divide the difference in secconds by the number of cays you have been running the code. This is the value you should enter in either dailyErrorFast or dailyErrorBehid,

Upload again with correct time and new settings. The code will now adjust the time a little every day to make sure it stays equal to the real time.

Is that what you wanted to know?

You set the startinghour in var starting hour, starting seconds in seconds, starting minute in minutes. You upload your sketch and then you press the reset button on your board exactly when the real world time is eqaul to the starting time you just set.


You check with the serial monitor if this worked. You let the code run for a few days and check the serial monitor again. You will now likely see a difference between the time on your arduino and the real world time. Taking a screenshot is the easiest way to spot this.

Divide the difference in secconds by the number of cays you have been running the code. This is the value you should enter in either dailyErrorFast or dailyErrorBehid,

Upload again with correct time and new settings. The code will now adjust the time a little every day to make sure it stays equal to the real time.

Is that what you wanted to know?

prototype_mechanic (author)2016-05-23

i use additional quartz. in this method i have a few seconds in year

Hey,


Is there like a product yo ucan buy and hook up? How does that work?

http://www.ebay.com/sch/i.html?_odkw=quartz&_osacat=0&_from=R40&_trksid=p2045573.m570.l1311.R1.TR1.TRC0.A0.H0.Xquartz+os.TRS0&_nkw=quartz+oscillator&_sacat=0

connect quartz to interrupt pin.
and calculate imp....