I am working on a project running on an ESP32 which opens a pet door in the morning at sunrise and closes 30 minutes after sunset. This has been working fine until I have decided to make it battery powered and as such want to utilize deep sleep. I now have a large amount of clock drift. It appears that there is no NTP updates occurring after initial boot
I have simplified my code to just the time and deep sleep to remove other variables but still have the issue. What I want to know is how can I force a NTP update at some sort of regular interval.
I don't need it to be hugely accurate but ideally to stay within 5 minutes of the correct time. As such if I could force the NTP to refresh every 3 to 4 hours should easily manage this.
#include <WiFi.h>
#define uS_TO_S_FACTOR 1000000
#define TIME_TO_SLEEP 60
RTC_DATA_ATTR long bootcnt = 0;
long startTime;
const char* ssid = "myssid";
const char* password = "mypasswd";
const char* ntpServer = "pool.ntp.org";
const char* TZ_INFO = "EST-10EDT-11,M10.1.0/02:00:00,M4.1.0/03:00:00";
void TimeCalcs(); //Needed for compiling in VSCode.
void TimeCalcs()
{
struct tm time;
if(!getLocalTime(&time))
{
Serial.println("Could not obtain time info");
return;
}
Serial.print("Current Time: "); Serial.println(asctime(&time));
Serial.print("---------\n");
}
void setup()
{
Serial.begin(115200);
bootcnt++;
Serial.println("Boot Count: " + String(bootcnt));
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
WiFi.begin(ssid, password);
startTime = millis();
while (WiFi.status() != WL_CONNECTED && (millis() - startTime) <= 8000) // try for 8 seconds
{
delay(500);
Serial.print(".");
}
if (WiFi.status() != WL_CONNECTED)
{
Serial.print("\nWiFi failed to Connect\n");
} else
{
Serial.print("\nWiFi Connected\n");
}
configTime(0, 3600, ntpServer);
setenv("TZ", TZ_INFO, 1);
TimeCalcs();
Serial.println("Going to sleep");
Serial.flush();
esp_deep_sleep_start(); //Anything after this will never run
}
void loop()
{
Serial.print("Code here should never run");
}
Not sure if this has pretty much fixed it or just greatly reduced it. I have had it running for over 12 hours and it is currently approximately 5 seconds off.
Is it that the 1 second delay has made the RTC more accurate or is it that the delay is giving it time to resync the time. If it is the first option whilst it means I won't have an issue after a few hours (be more like about once a month I still need to find out how to get it to resync with ntp to have a very long term solution.
I get how to set up a function and the various ways I could make a command run at certain times. I suppose to make my question clearer is "What is the command / function call to update it".
I have since found some more information which has helped me to find a solution (well kind of). I was using ESP32 core version 1.0.6 from expressif repository but found there was a Version 2.0.4 available through another repository.
This has some more functions in regards to SNTP however it appears still buggy (well for me anyway and found references to others having issues).
In particular there is a function
sntp_set_sync_interval(900*1000);
however that was failing to work for me but after updating to the 2.0 version core could at least compile it. I then found that adding the following few lines seemed to force it (however it happened intermittently (but at least it is working).
Overall I am now getting an intermittent output as follows when it does a sync. In this case it corrected time by 5 seconds
Boot Count: 159
.
WiFi Connected
Current Time: Wed Jul 13 09:02:12 2022
----Time Sync----- Time should have been verified
1657666927
Wed Jul 13 09:02:07 2022
Going to sleep
As such a lot closer to the solution. Feel the last part is refining it most likely due to the device being in deep sleep but in tests so far it appears to do a sync between 5 and 10 an hour. When I push my code out to deep sleep for 20 minutes I feel it will still update within the timeframe I need (+/-5minute accuracy). Just going to work on fine tuning it a bit and see if I can make it more consistent as to when it runs
Seem to have a solution that works as intended 97% of 300 resets and other 3% were either it didn't succeed in connecting to WiFi or some odd reason. Have changed the code around and added a series of delays which I am guessing has been the key detail that has made it work so consistently. I would have expected though that NTP commands would have waited for them to complete before proceeding to the next line. Also added sntp_stop() with a delay
My next step will be to modify the code by reducing / removing some of these delays until it starts to affect the consistency that it works.
My code currently for those that have similar issues is as follows
#include <WiFi.h>
#include <esp_sntp.h>
#include <time.h>
#define uS_TO_S_FACTOR 1000000
#define TIME_TO_SLEEP 90
RTC_DATA_ATTR long bootcnt = 0;
RTC_DATA_ATTR long ClockCnt = 0;
long startTime;
const char* ssid = "myssid";
const char* password = "myPassword";
const char* ntpServer = "pool.ntp.org";
const char* TZ_INFO = "EST-10EDT-11,M10.1.0/02:00:00,M4.1.0/03:00:00";
void setup()
{
bootcnt++;
ClockCnt++;
struct tm timeinfo;
sntp_set_time_sync_notification_cb(timeSyncCallback);
Serial.begin(115200);
Serial.println("Boot Count: " + String(bootcnt) + ". ClockCnt: " + String(ClockCnt));
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
WiFi.begin(ssid, password);
startTime = millis();
while (WiFi.status() != WL_CONNECTED && (millis() - startTime) <= 8000) // try for 8 seconds
{
delay(500);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED)
{
Serial.print("\nWiFi Connected\n");
sntp_stop();
delay(2000);
if (bootcnt == 1)
{
TimeSync();
} else
{
if (ClockCnt > 3)
{
TimeSync();
}
}
} else
{
if (bootcnt == 1)
{
Serial.print("\nWiFi failed to Connect. About to reboot\n");
ESP.restart();
}
}
if(!getLocalTime(&timeinfo))
{
Serial.println("Could not obtain time info");
return;
}
Serial.print("Current Time: "); Serial.println(asctime(&timeinfo));
delay(5000);
Serial.println("Going to sleep. Current ClockCnt: " + String(ClockCnt));
Serial.flush();
delay(1000); //Added as per advice on forum. With this had about 5 seconds in 12 hours
esp_deep_sleep_start(); //Anything after this will never run
}
void timeSyncCallback(struct timeval *tv)
{
Serial.println("\n----Time Sync----- Time should have been verified and updated if needed");
Serial.println(tv->tv_sec);
Serial.println(ctime(&tv->tv_sec));
ClockCnt = 0;
}
void TimeSync()
{
configTime(0, 3600, ntpServer);
delay(1000);
setenv("TZ", TZ_INFO, 1);
delay(1000);
tzset();
delay(1000);
}
void loop()
{
Serial.print("Code here should never run");
}
Ok. Happy I have now solved it. Solution was to put this in the setup routine (Note: No NTP server) and then have the same in the called routine on first boot and every 4th boot but with a NTP server stated in this called routine.
Got another solution which I have now tested and also appears to work just as well.
Remove the setenv() and tzset() calls from timeSync() function, and place them in setup() before calling getLocalTime().