Lost in time

Hi

I’d like to use an ESP32 to display time as HH:MM (no seconds) as accurately as possible. But I want to save battery, so the idea is

  • use NTP server to set the time (once at the beginning, then as less as possible)
  • display the time on an e-paper display
  • go to deep sleep for (roughly) one minute
  • go back to step 2 unless a given interval passed (say one or two hours, the longer the better) and go to step 1 (this is to recover accuracy as the RTC of the ESP32 drifts)
    I don’t know if it’s the best way to do it. I’m open to any suggestion.

But my concern is more on the proper use of the Time library functions. Here is what I do:

Step 1:

/*
     Connects to ntp server and set the time in the RTC of the ESP32
*/
void SetTimeByWifi () {
  const char* ntpServer  = "fr.pool.ntp.org";
  //init and get the time
  const long  gmtOffset_sec = 0;
  const int   daylightOffset_sec = 0;
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
}

with

// Internet
#include <WiFi.h>
#include <HTTPClient.h>

Step 2: read the time

/*
      Update timeinfo
*/
void WhatTime () {
  time_t rawtime;
  time (&rawtime);
  rawtime += GMT * 3600;  // GMT is set to 1 for me
  timeinfo = localtime (&rawtime);
}

Step 2: use the time for display

  currentTime[0] = (timeinfo->tm_hour + timeinfo->tm_isdst) % 24 / 10 + '0';
  currentTime[1] = (timeinfo->tm_hour + timeinfo->tm_isdst) % 24 % 10 + '0';
  currentTime[3] = timeinfo->tm_min / 10 + '0';
  currentTime[4] = timeinfo->tm_min % 10 + '0';

Step 3: set the time to sleep

  uint64_t sleepPeriod1 =  60100000ull;  // 60 seconds sleep (plus an attempt to counter drift)
sleep_time = sleepPeriod1 - micros();

Step 4: after wakeup, the seconds should be zero, so I force them to 0

/*
   Resets seconds to zero if wake up by timer
*/
void updateTime () {
  getLocalTime(timeinfo);
  if (timeinfo->tm_sec < 41) timeinfo->tm_sec = 0;
  if (timeinfo->tm_sec > 40) timeinfo->tm_sec = 60;
  time_t t = mktime(timeinfo);
  struct timeval now0 = { .tv_sec = t };
  settimeofday(&now0, NULL);
  getLocalTime(timeinfo);
  Serial.printf ("Update time : %s\n", asctime(timeinfo));
}

It begins to look like a gas factory (French expression :slight_smile: ) so I’d like to know if there is any more accurate way to keep and update the time?
I don’t know much about ftp or udp: I’ve seen on the forum that udp was quicker. Shoud I use udp?

Salut :slight_smile:
Whilst you can access it using TCP, natively ntp is using UDP on port number 123. what's the code for configTime?

Don't know, it's in the WiFi library of the ESP32 I believe

EDIT : I found it here.

/*
 * configTime
 * Source: https://github.com/esp8266/Arduino/blob/master/cores/esp8266/time.c
 * */
void configTime(long gmtOffset_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3)
{
    if(sntp_enabled()){
        sntp_stop();
    }
    sntp_setoperatingmode(SNTP_OPMODE_POLL);
    sntp_setservername(0, (char*)server1);
    sntp_setservername(1, (char*)server2);
    sntp_setservername(2, (char*)server3);
    sntp_init();
    setTimeZone(-gmtOffset_sec, daylightOffset_sec);
}

And, before you ask:

static void setTimeZone(long offset, int daylight)
{
    char cst[17] = {0};
    char cdt[17] = "DST";
    char tz[33] = {0};

    if(offset % 3600){
        sprintf(cst, "UTC%ld:%02u:%02u", offset / 3600, abs((offset % 3600) / 60), abs(offset % 60));
    } else {
        sprintf(cst, "UTC%ld", offset / 3600);
    }
    if(daylight != 3600){
        long tz_dst = offset - daylight;
        if(tz_dst % 3600){
            sprintf(cdt, "DST%ld:%02u:%02u", tz_dst / 3600, abs((tz_dst % 3600) / 60), abs(tz_dst % 60));
        } else {
            sprintf(cdt, "DST%ld", tz_dst / 3600);
        }
    }
    sprintf(tz, "%s%s", cst, cdt);
    setenv("TZ", tz, 1);
    tzset();
}

I also found this which may be a solution to my problem. I'll study that tomorrow.

After adapting the code from here, I have done a few tests.
For 1 minute sleep, I have almost no drift. But for 5 minutes sleep, the ESP wakes up 3 seconds too soon. 5 seconds too soon for 3 periods of 5 minutes sleep and 6 seconds too soon for 4 periods.

Have anyone done such characterization and know how to correct the drift? My previous solution was to measure the drifts for 1, 2, 3, 4 and 5 minutes sleeps. Then, as I want to wakeup at plain minutes, to force the seconds to 0 at wake-up.

Is there a more optimal way to keep an accurate time for a long period (several hours)?