Function to retrieve current time on ESP8266?

I want to include a function on my ESP8266 project, which can return the current UTC time in time_t format.
My idea is to call a nist server on startup after the ESP8266 has connected to a WiFi router and retrieve the current UTC time. Then it will store this as well as the return of millis().
Then my currenttime() function will return a value that is the time retrieved from the NTP server plus the difference between millis() and the stored value of millis() at the time of UTC retrieval from the NTP server.

I have googled a lot for this but all I get are complete sketches (ino files) which do a lot of printing to serial and repeatedly call the NTP server inside the loop() function. That is not what I want, instead I need something like the following functions:

bool GetUTCFromNTP(); //Retrieve the UTC time from an NTP server, save value and millis()
long GetCurrentTimeUTC(); //Return current UTC based on data saved during last use of GetUTCFromNTP()

All of the needed code should reside in a regular cpp file ready to be called from any ino sketch. I want to be able to include this file (and the h file) into any project where I need the time.
In order to keep the time updated I plan on using a check in the loop() function where the GetUTCFromNTP() function will be called once per 24 hours.

Any suggestions?

Already found these pages but they are all ino-centric, noone isolates the functionality into a separate file:
Arduino Time Sync from NTP server
Arduino/NTPClient.ino at master
Arduino/NTP-TZ-DST.ino at master

Is there anywhere any function like the GetUTCFromNTP() I need?

which do a lot of printing to serial and repeatedly call the NTP server inside the loop() function.

If you don't want to print, then don't. It is no mystery which function(s) cause Serial data to be print()ed.

If you want to do something once, there is a function where things happen once. Do whatever you want to do once in that setup() function.

In order to keep the time updated I plan on using a check in the loop() function where the GetUTCFromNTP() function will be called once per 24 hours.

So, that was a load of crap up there where you said you wanted to do it ONCE.

Regardless, what you want to do is trivial. Just do it.

my sketch with ntp time

Juraj:
my sketch with ntp time

What purpose is the TelnetStream object? It looks like the GIT example is in an example suite for TelnetStream...
Is it needed for NTP to function?
Apart from that, which confuses me, the library NtpClientLib is really what I was trying to locate, one file that handles everything (except the TelnetStrea...).
Can the NTP object be started and forgotten about afterwards; i.e. it will update its time every 24 hours or so?
Don't need to check more often than that. And I don't need to send the result anywhere either, just time for internal use.

I just happened to have this to hand, but I haven’t used it for quite a while. It did work…

#include <TimeLib.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

#include <SD.h>
#include <SPI.h>  

#include <PCD8544.h>
static PCD8544 lcd;

const char ssid[] = "Snookypoo";  //  your network SSID (name)
const char pass[] = "++++++++++";       // your network password

// NTP Servers:
static const char ntpServerName[] = "us.pool.ntp.org";
//static const char ntpServerName[] = "time.nist.gov";

const int timeZone = 10;     // AEST

WiFiUDP Udp;
unsigned int localPort = 8888;  // local port to listen for UDP packets

time_t getNtpTime();
void digitalClockDisplay();
void printDigits(int digits);
void sendNTPpacket(IPAddress &address);
time_t prevDisplay = 0; // when the digital clock was displayed

char filename[] = "00000000.CSV";
File myFile;
char dumpName[] = "00000000.CSV";
File dumpFile;
char dailydate[] = "00000000.CSV";

void setup()
{
  Serial.begin(115200);
  delay(250);
  Serial.println("TimeNTP Example");
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, pass);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    lcd.begin(84, 48);
    lcd.setCursor(0,0);
    lcd.print("Today it is");
  }

  Serial.print("IP number assigned by DHCP is ");
  Serial.println(WiFi.localIP());
  Serial.println("Starting UDP");
  Udp.begin(localPort);
  Serial.print("Local port: ");
  Serial.println(Udp.localPort());
  Serial.println("waiting for sync");
  setSyncProvider(getNtpTime);
  setSyncInterval(300);
/*
  pinMode(10, OUTPUT);

  Serial.println("Init SD CARD");
    
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) 
  {
    lcd.println("Card failed");
    return;
  }
  lcd.println("CARD OK");
  */
}

void loop()
{
  if (timeStatus() != timeNotSet) {
    if (now() != prevDisplay) { //update the display only if time has changed
      prevDisplay = now();
      digitalClockDisplay();
      lcddisplay();
          getFileName();
    Serial.println(filename); 
    }
  } 
}

void digitalClockDisplay()
{
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(".");
  Serial.print(month());
  Serial.print(".");
  Serial.print(year());
  Serial.println();
}

void printDigits(int digits)
{
  // utility for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if (digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

void lcddisplay{
  lcd.setCursor(0,4); 
  lcd.print(day());
  lcd.print("/");
  lcd.print(month());
  lcd.print("/");
  lcd.print(year());
  
  lcd.setCursor(0,5);
  
   if( second==0)
  {
   lcd.print("         ");
   lcd.setCursor(0,5);
   } 
  
  lcd.print(hour);
  lcd.print(":");
  lcd.print(minute);
  lcd.print(":");
  lcd.print(second);   
}
/*-------- NTP code ----------*/

const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets

time_t getNtpTime()
{
  IPAddress ntpServerIP; // NTP server's ip address

  while (Udp.parsePacket() > 0) ; // discard any previously received packets
  Serial.println("Transmit NTP Request");
  // get a random server from the pool
  WiFi.hostByName(ntpServerName, ntpServerIP);
  Serial.print(ntpServerName);
  Serial.print(": ");
  Serial.println(ntpServerIP);
  sendNTPpacket(ntpServerIP);
  uint32_t beginWait = millis();
  while (millis() - beginWait < 1500) {
    int size = Udp.parsePacket();
    if (size >= NTP_PACKET_SIZE) {
      Serial.println("Receive NTP Response");
      Udp.read(packetBuffer, NTP_PACKET_SIZE);  // read packet into the buffer
      unsigned long secsSince1900;
      // convert four bytes starting at location 40 to a long integer
      secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
      secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
      secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
      secsSince1900 |= (unsigned long)packetBuffer[43];
      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
    }
  }
  Serial.println("No NTP Response :-(");
  return 0; // return 0 if unable to get the time
}

// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12] = 49;
  packetBuffer[13] = 0x4E;
  packetBuffer[14] = 49;
  packetBuffer[15] = 52;
  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

void getFileName(){
sprintf(filename, "%02d%02d%02d.csv", year(), month(), day());
}

BosseB:
What purpose is the TelnetStream object? It looks like the GIT example is in an example suite for TelnetStream...
Is it needed for NTP to function?
Apart from that, which confuses me, the library NtpClientLib is really what I was trying to locate, one file that handles everything (except the TelnetStrea...).
Can the NTP object be started and forgotten about afterwards; i.e. it will update its time every 24 hours or so?
Don't need to check more often than that. And I don't need to send the result anywhere either, just time for internal use.

TelnetStream is a library for debuging over-the-air, it is not important here.

OK, thanks!
I have now tried to use the NtpClientLib library according to the example.
But now I am stuck at this in the ino file:

void processSyncEvent (NTPSyncEvent_t ntpEvent) {
    if (ntpEvent) {
    	SerialDebug.print ("Time Sync error: ");
        if (ntpEvent == noResponse)
        	SerialDebug.println ("NTP server not reachable");
        else if (ntpEvent == invalidAddress)
        	SerialDebug.println ("Invalid NTP server address");
    } else {
    	SerialDebug.print ("Got NTP time: ");
    	SerialDebug.println (NTP.getTimeDateString (NTP.getLastNTPSync ()));
    }
}

I get errors as follows: Type ‘’ could not be resolved for these identifiers:
NTPSyncEvent_t
noResponse
invalidAddress
NTP
getTimeDateString
getLastNTPSync

But I have set the #include as follows:

/* The NTP time retrieval needs this: */
#include <TimeLib.h>
#include "NtpClientLib.h"

I copied in the two NtpClientLib files downloaded from GIT into the main project dir, hence the “” rather than <>.

Why can’t it resolve these identifiers?
Will it matter if I don’t include the function void processSyncEvent (NTPSyncEvent_t ntpEvent) ?
Note that in Sloeber IDE if I right-click the NTPSyncEvent_t word and select “Open declaration” it jumps to the location inside the NtpClientLib.h file where it is declared. So Eclipse can find it in that case…

the library is in Library Manager. try to delete the copied files and install the library in Library Manager

I did so already but it changed nothing. Same errors displayed and I have used F5 in the Project explorer to refresh stuff...
Then as a final test I used F5 in the code editor window and now the errors went away!
Strange, shouldn't Eclipse be able to do this by itself?
Anyway, errors gone so I will continue copying sample code....

Now working as basic system.
Questions:

    if (wifiFirstConnected) {
        wifiFirstConnected = false;
        NTP.begin ("pool.ntp.org", timeZone, true, minutesTimeZone);
        NTP.setInterval (63);
    }

What is the purpose of the 4th parameter in begin()?
What is the unit of the setInterval() parameter? milliseconds, seconds, minutes or what?
Is there no way to retrieve the current time from the NTP object following its first sync with an NTP server?
The function getTime() actually runs a connection against the server, when it could calculate current time internally.
Is there no way to have GetDateStr() return the string in ISO format?

Regarding the update interval I found this inside the NtpClientLib.h file:

#define DEFAULT_NTP_INTERVAL 1800 // Default sync interval 30 minutes 
#define DEFAULT_NTP_SHORTINTERVAL 15 // Sync interval when sync has not been achieved. 15 seconds
...
    /**
    * Changes sync period.
    * @param[in] New interval in seconds.
    * @param[out] True if everything went ok.
    */
    bool setInterval (int interval);

    /**
    * Changes sync period in sync'd and not sync'd status.
    * @param[in] New interval while time is not first adjusted yet, in seconds.
    * @param[in] New interval for normal operation, in seconds.
    * @param[out] True if everything went ok.
    */
    bool setInterval (int shortInterval, int longInterval);

    /**
    * Gets sync period.
    * @param[out] Interval for normal operation, in seconds.
    */

This does not make sense to me because the example uses NTP.setInterval(63), which implies that the server comm interval is 63 seconds (why?). Such a strange value where one would think that a value close to 86400 (24 hours) would be more appropriate.

But another thing is that I tried to get the time as string in a function that is executed once every 2 minutes:

            SerialDebug.print ("Current time: ");
            SerialDebug.println (NTP.getTimeDateString ());

Accordig to the header file when calling getTimeDateString() without parameter it will be executed with the parameter now() which is handled by library Time.cpp:

time_t now() {
	// calculate number of seconds passed since last call to now()
  while (millis() - prevMillis >= 1000) {
		// millis() and prevMillis are both unsigned ints thus the subtraction will always be the absolute value of the difference
    sysTime++;
    prevMillis += 1000;	
#ifdef TIME_DRIFT_INFO
    sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift     
#endif
  }
  if (nextSyncTime <= sysTime) {
    if (getTimePtr != 0) {
      time_t t = getTimePtr();
      if (t != 0) {
        setTime(t);
      } else {
        nextSyncTime = sysTime + syncInterval;
        Status = (Status == timeNotSet) ?  timeNotSet : timeNeedsSync;
      }
    }
  }  
  return (time_t)sysTime;
}

I don’t understand a lot of what is happening here but I see no NTP call in any case…

What actually happens when I call the function with no parameter is that the NTP server gets called to supply the current time!!!
So I get an NTP call every time I need to use the current time in any way…
Is there a way to fix this? It seems unnecessary to call NTP server with only minutes intervals…

PS: I had to copy the library files into my project so I could change the date format into ISO instead of the strange default format used. So now I can do also other changes if needed/suggested. DS

the time is kept by TimeLib locally using millis() and the Ntp lib sets the initial time and syncs the time in set interval

My system seems to be running OK now with the time retrieved at boot. :slight_smile:
I tried to capture the traffic from the ESP8266 using Wireshark to verify that it is not doing additional updates except for the ones I have set in these commands:

            NTP.begin ("pool.ntp.org", 1, true, 0);
            NTP.setInterval (60, 86400);    //Initial NTP call repeat 1 min, then 24h between calls

But I can see no traffic at all for the ESP8266 IP address 192.168.119.133.
My Win7 laptop is connected by wire to the same router as the ESP is connected to via WiFi, so they are on the same network (the laptop has last byte=241 and the ESP is 133.
Apparently one cannot catch packets with Wireshark in this situation, only when the ESP and PC are communication with each other...
Is there some way to verify that the ESP is only talking to NTP on startup and after that every 24 hours?