NTPClient
NTPClient copied to clipboard
.getEpochTime rollover?
I've been trying to use this library to monitor uptime on my esp projects. Basically, I have an unsigned long (startEpoch) that I populate in startup block. Every 60 seconds thereafter, I populate a local variable (currentEpoch) with timeclient.getEpochTime(). Next I subtract startEpoch from currentEpoch and get uptime in seconds. This works great for about 47 days 17 hours and 2 minutes... roughly. Then I see that currentEpoch jumps backwards to the same epoch of startEpoch. It suggests there is some millis overflow issue given the elapsed time. I have studied my code and cannot see anything in my code that would cause this but I'm amateur at best so I could be wrong. I have included the abridged version below.
I set the sketch to publish currentEpoch, startEpoch as well as strUptime to 3 mqtt topics which I logged. My timestamp log only shows changes and startEpoch didn't change which can be seen by subscribing via mosquitto_sub and it is still broadcasting the may 22 startEpoch:
uptime/state 00:00:10
debug/StartEpoch 1527022364
debug/CurrentEpoch 1527023000
2018-07-11 09:52:36.984 CurrentEpoch changed from 1531317120 to 1531317181
2018-07-11 09:53:36.982 Uptime changed from 49:17:00 to 49:17:01
2018-07-11 09:53:37.482 CurrentEpoch changed from 1531317181 to 1531317241
2018-07-11 09:54:37.482 Uptime changed from 49:17:01 to 49:17:02
2018-07-11 09:54:37.983 CurrentEpoch changed from 1531317241 to 1531317302
2018-07-11 09:55:37.983 Uptime changed from 49:17:02 to 00:00:00
2018-07-11 09:55:38.486 CurrentEpoch changed from 1531317302 to 1527022395
2018-07-11 09:56:38.484 Uptime changed from 00:00:00 to 00:00:01
2018-07-11 09:56:38.986 CurrentEpoch changed from 1527022395 to 1527022455
2018-07-11 09:57:38.986 Uptime changed from 00:00:01 to 00:00:02
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
unsigned long uptimeMillis = 0; //holds millis for uptime processing
unsigned long startEpoch = 0; //holds starting time epoch
WiFiUDP ntpUDP;
WiFiClient espClient;
NTPClient timeClient(ntpUDP, "us.pool.ntp.org");
void setup() {
timeClient.begin(); //start NTP, update and get start epoch
timeClient.update();
startEpoch = timeClient.getEpochTime();
void loop() {
//BEGIN UPTIME---------------------------------------------------
if ((unsigned long)(millis() - uptimeMillis) >= 60000 || uptimeMillis ==0)
{
unsigned long uptime = 0; //holds differential epoch
unsigned long currentepoch = timeClient.getEpochTime(); //this is for testing purposes
String strDays = "00"; //strings for time bits
String strHours = "00";
String strMinutes = "00";
uptime = timeClient.getEpochTime() - startEpoch;
int days = uptime / 60 / 60 / 24;
int hours = (uptime / 60 / 60) % 24;
int minutes = (uptime / 60) % 60;
if (days < 10)
{
strDays = "0" + String(days);
} else {
strDays = String(days);
}
if (hours < 10)
{
strHours = "0" + String(hours);
} else {
strHours = String(hours);
}
if (minutes < 10)
{
strMinutes = "0" + String(minutes);
} else {
strMinutes = String(minutes);
}
String strUptime = strDays + ":" + strHours + ":" + strMinutes;
uptimeMillis = millis(); //reset count
}
//END UPTIME------------------------------------------------------
}
Hey, Im using this lib for my FlipClock.
The clock runs very well for over a month and the time is getting wrong tonight (clock shows only hours:minutes).
I discovered that millis() is only a uint32_t which rolls over after 49.7 days. Because the complete lib is based on the millis() and an overflow is not handled, it stops working after this time and calculates bullshit instead.
It ridiculous that arduino doesn't use a uint64_t for millis() and that a time lib doesn't even work for 2 months.
Please fix this asap!
Best, Robert
The problem are you :-)
Depend on how you use millis() If you use it well the overflow never create problem. A tips: look at official "blink without delay" example, it is integrated in the Arduino Ide
OK, I see your point, the overflow shouldn't cause an issue when you use only the difference of unsigned 32bit integers. But anyway, this isn't a good programming style at all.
And no I'm definitely not a problem...
Maybe my problem is related to this issue https://github.com/arduino-libraries/NTPClient/issues/52 The clock was exact 30 minutes to early (shows 10:22 instead of 10:52) and my NTP update interval is 30 minutes...
All what I can say now, the time from the library switched to this wrong time this night. It has nothing to do with my (very little) sketch to display the time. I printed the time from the lib functions directly to the serial port as debug output. This time was wrong and my few lines displays exactly this time.
After a reboot everything is fine now. Here is my sketch, if yo can see any major mistake, please let me know:
/*
----- NTP FlipClock - modificated Siemens BT 6.59 BT 600 Type 5 -----
TODO: Add description
D4 is GPIO2 and controls onboard LED (low active)
----- Libraries -----
NTPClient from Library Manager
https://github.com/arduino-libraries/NTPClient
Not needed???:
Time from Library Manager
https://github.com/PaulStoffregen/Time
Timezone from Library Manager
https://github.com/JChristensen/Timezone
----- License -----
----- Arduino Board Settings -----
Board Manager URL:
http://arduino.esp8266.com/stable/package_esp8266com_index.json
Board: NodeMCU 1.0 (ESP-12E Module)
Flash Size: 4M (1M SPIFFS)
Debug port: Disabled
Debug Level: Keine
IwIP Variant: v2 Lower Memory
VTables: Flash
CPU Frequency: 80 MHz
Upload Speed: 115200 (check faster speeds)
Erase Flash: Only Sketch
----- Author -----
Robert Steigemann - RS-Elec 2018
*/
//----- Include Libraries -----
#include <ESP8266WiFi.h>
#include <WifiUDP.h>
#include <String.h>
#include <NTPClient.h>
//#include <Time.h>
//#include <TimeLib.h>
//#include <Timezone.h>
#include "secure_ssid_pw.h"
//----- Define used IO-Pins -----
const int CLKA = D7; // positive impuls output for clock
const int CLKB = D6; // negative impuls output for clock
const int IREN = D0; // activates IR sensors for absolute hour detection
const int IRA = D1; // IR sensor A input, outer sensor !!! Nochmal IRA und IRB getauscht wegen Lötfehler beim Zusammenbau auf Lochraster!!!
const int IRB = D2; // IR sensor B input, inner sensor
const int SUTI = D5; // Summertime selection
// SUTI = 0 -> Summertime
// SUTI = 1 -> Normal time
const int LED = D4; // onboard LED is on D4 (low active)
//----- Defines for clock hardware -----
const int CLK_PULSEWIDTH = 150; // pulse width in ms
const int CLK_PULSEBREAK = 150; // pulse pause in ms
const int CLK_WAIT_RANGE = 10; // Max minutes to wait for real time catches shown time, instead of fast forward almost a day
int minute_of_day = -1; // current minute of the day, should be shown by clock
int clk_minute = -1; // current shown minute by clock
//----- Defines for NTP -----
#define NTP_OFFSET 1 * 60 * 60 // In seconds, one hour for germany
#define NTP_INTERVAL 30 * 60 * 1000 // In miliseconds
#define NTP_ADDRESS "europe.pool.ntp.org" // change this to whatever pool is closest (see http://support.ntp.org/bin/view/Servers/NTPPoolServers)
const char* ssid = SECURE_SSID; // insert your own ssid
const char* password = SECURE_PW; // and password
int last_second = 99;
//----- Set up the NTP UDP client -----
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, NTP_ADDRESS, NTP_OFFSET, NTP_INTERVAL);
// put out a single pulse to clock, invert polarity on every pulse
void clk_pulse(void)
{
// polarity of next pulse true => CLKA=HIGH CLKB=LOW
// false => CLKA=LOW CLKB=HIGH
static bool next_pulse_pol = true;
if(next_pulse_pol) // make positive pulse
{
digitalWrite(CLKA, HIGH);
next_pulse_pol = false;
}
else // make negative pulse
{
digitalWrite(CLKB, HIGH);
next_pulse_pol = true;
}
delay(CLK_PULSEWIDTH);
digitalWrite(CLKA, LOW);
digitalWrite(CLKB, LOW);
delay(CLK_PULSEBREAK);
}
// turns clock forward by n minutes
void clk_next_minute(int n)
{
for(int i=0; i<n; i++)
{
clk_pulse();
clk_minute++;
if(clk_minute == 1440) // respect new day
{
clk_minute = 0;
}
Serial.print("Clock skipped to min: ");
Serial.println(clk_minute);
}
}
// rotates clock until an absolute position is reached and sets clk_minute
void clk_abs_position(void)
{
// turn IR sensor on
digitalWrite(IREN, HIGH);
delay(50);
int IRA_value = digitalRead(IRA);
int IRB_value = digitalRead(IRB);
// Seek for next change of marking
do // double the loop with extra delay for glitch filtering
{
do
{
clk_pulse();
}
while( (digitalRead(IRA)==IRA_value) && (digitalRead(IRB)==IRB_value) );
delay(250);
}
while( (digitalRead(IRA)==IRA_value) && (digitalRead(IRB)==IRB_value) );
// now we just reached a new marking and wie are at zero minutes of that hour
if( (digitalRead(IRA)==LOW) && (digitalRead(IRB)==LOW) ) // double marking
{
clk_minute = 19 * 60; // is 19:00
}
else if( (digitalRead(IRA)==LOW) && (digitalRead(IRB)==HIGH) ) // marking at A
{
clk_minute = 1 * 60; // is 1:00
}
else if( (digitalRead(IRA)==HIGH) && (digitalRead(IRB)==LOW) ) // marking at B
{
clk_minute = 13 * 60; // is 13:00
}
else if( (digitalRead(IRA)==HIGH) && (digitalRead(IRB)==HIGH) ) // double marking
{
clk_minute = 7 * 60; // is 7:00
}
else // no marking reached
{
clk_minute = -2; // fatal error
}
// turn IR sensor off to save current
digitalWrite(IREN, LOW);
}
void setup()
{
// ----- configure I/Os -----
pinMode(CLKA, OUTPUT);
pinMode(CLKB, OUTPUT);
digitalWrite(CLKA, LOW);
digitalWrite(CLKB, LOW);
pinMode(IREN, OUTPUT);
digitalWrite(IREN, LOW);
pinMode(IRA, INPUT);
pinMode(IRB, INPUT);
pinMode(SUTI, INPUT_PULLUP);
Serial.begin(115200);
// ----- configure WiFi -----
timeClient.begin(); // Start the NTP UDP client
// Connect to wifi
Serial.println("");
Serial.print("Connecting to ");
Serial.print(ssid);
WiFi.softAPdisconnect(true);
WiFi.begin(ssid, password);
// while (WiFi.status() != WL_CONNECTED)
// {
// delay(500);
// Serial.print(".");
// }
// Serial.println("");
// Serial.print("Connected to WiFi at ");
// Serial.print(WiFi.localIP());
Serial.println("");
}
// the loop function runs over and over again forever
void loop()
{
// check if clock display is known
if( (clk_minute<0) || (clk_minute>=1440) )
{
Serial.println("Find abs position");
clk_abs_position(); // try to reach an absolute position
Serial.println(clk_minute);
}
if (WiFi.status() == WL_CONNECTED) //Check WiFi connection status
{
digitalWrite(LED, LOW); // turn LED on
// update the NTP client and get the UNIX UTC timestamp
timeClient.update();
}
else // attempt to connect to wifi again if disconnected
{
digitalWrite(LED, HIGH); // turn LED off
WiFi.begin(ssid, password);
delay(10000);
}
if(digitalRead(SUTI)==LOW) // we have summertime enabled
{
timeClient.setTimeOffset(NTP_OFFSET + 3600); // add an extra hour for time offset
}
else
{
timeClient.setTimeOffset(NTP_OFFSET);
}
// print time every second just for debugging
if( last_second != timeClient.getSeconds())
{
last_second = timeClient.getSeconds();
Serial.print(timeClient.getHours());
Serial.print(":");
Serial.print(timeClient.getMinutes());
Serial.print(":");
Serial.println(last_second);
}
// calculate minutes of day
if(timeClient.getEpochTime() < (30 * 365 * 24 * 60 * 60)) // epoch < 30 years
{
// system haven't got any time yet
minute_of_day = -1;
}
else
{
minute_of_day = timeClient.getHours() * 60 + timeClient.getMinutes();
}
// Serial.print("Now: ");
// Serial.print(minute_of_day);
// Serial.print(" Diff: ");
// if clock display and minute of day are both valid
if( (clk_minute>=0) && (clk_minute<1440) && (minute_of_day>=0) && (minute_of_day<1440) )
{
// calculate time difference
int diff = minute_of_day - clk_minute;
// day overlap, add difference of a day
if(diff < 0)
{
diff += 1440;
}
//Serial.println(diff);
if(diff < (1440 - CLK_WAIT_RANGE))
{
//Serial.println("clk start");
clk_next_minute(diff);
//Serial.println("clk finished");
}
}
delay(100);
}
I don't know if this lib have a bug, but for the overflow of millis it is unusefull increase the variable type,because you only move the problem ahead, but it will remain and will lost some precious Ram.
The correct way, is the blink without delay way
Hi,
I could recognize the 30 min bug another time yesterday only some hours after the power cycle. After a second power cycle the clock works as expected until yet. It's really weird... I'm going to investigate that as sonn as I got more ESP8266 from eBay to made a test setup. Maybe I could figure out what is going wrong.
By the way, when you increase the type of the millis to uint64_t you need four extra bytes and you will postpone the rollover problem from 49.7 days (which is absolutely a usual power on-time or life time of an electric device) to ~585 million years. I don't think the device or mankind will last so long :-) So if it was my decision, I would say it is worth the four extra bytes...
But I think we should close this bug. The millis() overflow isn't the problem here and the lib has another bug, which is already opened here (https://github.com/arduino-libraries/NTPClient/issues/52).
Best, Robert