Introduction: 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
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?
"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.
Attachments
Step 3: 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 ;)
44 Comments
5 years ago
Very interesting article.
How does temperature variations (the most important variable in electronics) affect the accuracy? As I understand, these MCUs all use ceramic resonators instead of quality crystals! Although I could've bet my life, that what I was looking at, were square crystal cans, and not 3-legged resonators.
If that is the case, ceramic resonators change their frequency a lot with temperature fluctuation.
Reply 3 months ago
I couldn't help but respond since you hit it right on. A few comments talk about polling the internet, but if that is the case then you can just do that as often as needed. This individual was trying to do this without any polling. And I'm thinking they didn't care about the actual date/time rather that a minute was a minute or an hour is an hour type accuracy. But, in the end the temperature wrecks this whole thing. Unless you use a temperature compensated oscillator your accuracy will fluctuate making the 'adjustment' irellevant. Because if you adjust it at say 70F and then place the unit in a garage where it gets to say 40F then your adjustment will no longer be correct and of course the actual results will no longer be correct. I had a situation where my MCU communicated with a DS18B20 and it worked flawlessly. But, the moment the temperature dropped the oscillator changed just enough that the Arduino no longer communicated correctly with the DS18B20. Using an external, accurate temperature compensated oscillator resolved the problem. But it shows that this method will be an issue if the ambient temperatures are going to vary.
Question 1 year ago
hi, i have issue during compiling with the
int startingHour = 15;
int hours = startingHour;
it throws "initialization isnt constant expression"
can i do
int startingHour = 15;
int hours = 15;
?
so its the same value for both?
Question 2 years ago
How Can I get value from the web and plug it into the code …I wan build a pay as you App or Pay per use .when it reaches a specific time it will shut down and wait to get values from a web server …pls I don’t mind links to this project thanks in advance
3 years ago
Nice like it
microchip pic16c84 like 25 years ago
https://youtu.be/lny7J7KXk9w
4 years ago on Introduction
I having it synch up with NTP daily would keep the time accurate. Would also have it synch with ntp during setup().
Reply 3 years ago
Like so https://tttapa.github.io/ESP8266/Chap15%20-%20NTP.html
Should do the trick for most of us. Time zones is the next smartie pants step.
5 years ago
I'm afraid that your conclusions and method are flawed at so many levels. I admire your attempt though. I am a bit of a time geek as my partner would testify. Accuracy depends on so many factors such as the time source you use to verify the time, the accuracy you track the time at and the hardware/software you produce.
The nearest I have come to an accurate clock uses an NTP server and it is compared against the DCF77 signal pulses for comparison and it uses an RTC to generate 1 second pulses synchronised an NTP time server. I have a clock which is within 2 mS of the NTP server at all times. Trust me, this has taken a lot of work but, the RTC is vital to provide a synchronisable 1 second pulse.
The unfortunate fact is that you cannot judge the accuracy of any clock without a reference which is why I use the DCF77 signal pulses to compare accuracy and this requires a Logic Analyser.
Reply 3 years ago
I am getting the impression that 90% of readers are wanting 1 second accuracy. I'm looking for about 60 seconds, with an actual sync service every 24h kind of accuracy. So I am in on this, and bugger the 24h wrapping code fiddle, just sync once a day inside of that I say.
That however is the trick here.
3 years ago
What about adding milliseconds after the seconds for display?
Is displaying milliseconds accurate enough?
Reply 3 years ago
I mean "next to" on the display or the string that sent to the serial.
4 years ago
How do you setup the time whenever you power on the device?
How do you handle the leap years?
What about the on-chip RTC alarms?
What if you want/need an interrupt every 1 precise second or so?
The RTC is good at all that ;)
4 years ago on Step 2
Hi. You really didn't think this through. Here are few obvious flaws:
- if you turn your device off for a time period and then on again, time will be wrong
- you don't want your device to be on all the time. Typical application cycle is:
do something
send results to server
deep sleep
- RTC modules have other functions like, alarms, calendars, ...
Cheers
Robert
Reply 4 years ago
I do agree with totally with you.
Besides, it was mention that this same technique works for Arduino UNO. Well, here we have a problem. The system tick on it is 998ms.
4 years ago
Clock restarts at set time everytime you open the serial-monitor
Question 4 years ago on Introduction
#include <ESP8266WiFi.h>
#include <MPU6050_tockn.h>
#include <Wire.h>
MPU6050 mpu6050(Wire);
const char* ssid = "TP-LINK";
const char* password = "tplink";
// Create an instance of the server
// specify the port to listen on as an argument
WiFiServer server(80);
void setup() {
delay(1000);
Wire.begin(0,2);
Serial.begin(9600);
delay(1000);
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
// Start the server
server.begin();
Serial.println("Server started");
// Print the IP address
Serial.println(WiFi.localIP());
delay(1000);
mpu6050.begin();
delay(1000);
mpu6050.calcGyroOffsets(true);
delay(1000);
}
void loop() {
// Check if a client has connected
WiFiClient client = server.available();
if (!client) {
Serial.print("Client has not connected");
delay(1000);
return;
}
// Wait until the client sends some data
Serial.println("new client");
while (!client.available()) {
delay(1);
}
mpu6050.update();
client.print(mpu6050.getGyroAngleX());
client.print(",");
client.print(mpu6050.getGyroAngleY());
client.print(",");
client.println(mpu6050.getGyroAngleZ());
delay(100);
delay(1);
Serial.println("Client disonnected");
// when the function returns and 'client' object is destroyed
}
code end----
Connection as per the image attached.
My problem is Iam unable to get the actual gyro angle readings instead it goes on incrementing the values by0.1 or so.
I have to get the actual gyro angle
but the mpu6050.tockn.h library works fine in Arduino UNO but same code fails in esp 01.
this library uses millis() function, is it because of this.
5 years ago
I think this article about frustration in all kind of matter instead of it is a proof. There are some creative ways to overcome the problem(s) you tried to prove. It is mostly a problem how to KEEP time and you don't provide this with this 'solution' (like you mention in the last alinea's). Title is misleading, you don't provide a solution.
6 years ago
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.....
6 years ago
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.
6 years ago
@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.