Example sketches for the new Arduino IDE for ESP8266

Moderator: igrr

User avatar
By alka79
#89714 Hello,
Thanks @RIN67630 for your work. It helped me to understand how time() works on ESP8266 arduino, better than the example provided on github.

There are plenty of tutorials on the web to use some external time and NTP libraries when it is actually not needed. All is included to have local time with Time Zone and Daylight Savings Time support.
I could not find a basic configTime documentation. Now I think I got it.

Below is my version of an example, inspired by the sketch published here. I made some changes and added a String formatting function I find useful.



Some more about time(). I could not find any simple documentation either, so maybe this will help others.

As I understand, configTime is only settings.
The real work is done by the time() function from espressif included in the arduino core.

time() always returns an internal time counter.

When the delay since prevous sync is reached (60 minutes by default), time() re-synchronises its internal counter with NTP server on the internet, as defined in configTime.

The NTP sync is performed in the background, when ESP do their magic. It takes a litte time to get the response from the NTP server. I measured from 15ms to 200ms. That obviously depends on network conditions.

When time() is called for the first time, the NTP sync is started immediately. It then takes 15 to 200ms or more to complete.

The trick is if you call time(nullptr) just after configTime in the setup() or in the loop() and check its value without letting enough time for NTP server response to arrive.
time(nulllptr) returns its internal counter wich, after reset and before NTP sync, is just a few miliseconds. Date will be 01.01.1970 until NTP sync is completed.

Thus this code after configTime
Code: Select all  while((NOW = time(nullptr)) < NTP_MIN_VALID_EPOCH) {
    delay(20);
    Serial.print(".");
  }
  Serial.println("");

This waits until time is properly synched before going on.
I chose august 1rst 2018 for NTP_MIN_VALID_EPOCH

In some publications, I saw this code : while(! time(nullptr)) { ...}
This obviously does not work.
time(nulltptr) will always return something. At first call, it will return a non null small value. This loop will always exit after first round and time will be at 1.1.1970 .

Some other people insert a 500ms delay after the first time() call. This will work in most cases.

My example code.

Code: Select all//dec 2020 by alka
// inspired from https://www.esp8266.com/viewtopic.php?p=86975#p86975
// tested on ESP8266 NodeMCU 1.0, arduino code 2.7.4

// all includes are from Arduino Core, no external lib
#ifdef ARDUINO_ARCH_ESP32
#include <WiFi.h>         
#include <time.h>
#else
#include <ESP8266WiFi.h>       
#include <TZ.h>
// <time.h> and <WiFiUdp.h> not needed. already included by core.         
#endif

// Update to your context here
#define WIFI_SSID  "..."
#define WIFI_PASS  "......"
#define NTP_SERVER "pool.ntp.org"   
#define HOST_NAME  "ESP-IoT"
   

#ifdef ARDUINO_ARCH_ESP32
// maintain for ESP32
#define TZ             1                // (utc+) TZ in hours
#define DST_MN         60               // use 60mn for summer time in some countries
#define GMT_OFFSET_SEC 3600 * TZ        // Do not change here...
#define DAYLIGHT_OFFSET_SEC 60 * DST_MN // Do not change here...
#else
// maintain for ESP8266
#define MYTZ TZ_Europe_Paris
#endif

// Global variables for Time
tm        *NOW_TM;                      // pointer to a tm struct;
time_t     NOW;                         // global holding current datetime as Epoch

#define NTP_MIN_VALID_EPOCH 1533081600  // August 1st, 2018

int Second;
int Minute;
int Hour;
int Day;
int Month;
int Year;
int Weekday;
char DayName[12];
char MonthName[12];
char Time[10];
char Date[12];

// functions declarations (prototypes)
void ConnectWiFi();
void initNTP();
void setNOW();
void setNOW_TM();
String dateTimeStr(time_t epochtime, char* pattern = (char *)"%d-%m-%Y %H:%M:%S");

void setup()
{

  Serial.begin(115200);
  delay(20); Serial.println("\n");   
 
  ConnectWiFi();

  initNTP();

  Serial.println("It is now " + dateTimeStr(NOW));       // prints current date wiht default format dd mm yyyy hh:mm:ss

  Serial.println("setup done. \n");

}

void loop()
{
 
  setNOW();          // sets NOW with Epoch datetime (Numbers of seconds since 1.1.1970 00:00:00)
  setNOW_TM();       // breaks down the now Epoch time into tm structure.


  // Examples with Strings
  Serial.print("Thanks NTP, it is ");   Serial.println( DayName );
  Serial.print("Date is ");   Serial.print( Date );
  Serial.print(" and Time is ");   Serial.println( Time );


  // Examples with Posix expressions
  char charbuff[80];
  strftime (charbuff, 80, "US and UK would say it is %I:%M%p. The rest of the world uses ISO 8601 : %T", NOW_TM);
  Serial.println(charbuff);
  Serial.println();
  Serial.println();
  delay(10000);

}

// **************** FUNCTIONS DEFINITIONS ******************

void ConnectWiFi() {
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  Serial.print("Connecting to Wifi ");
  while ( WiFi.status() != WL_CONNECTED ) {
    // warning : no time out. May loop here forever
    delay (500);
    Serial.print ( "." );
  }
  Serial.println();
  Serial.print("Connected with IP "); Serial.println(WiFi.localIP());
}

void initNTP()
{
  #ifdef ARDUINO_ARCH_ESP32
  configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER);
  #else
  configTime(MYTZ, NTP_SERVER);
  #endif

  unsigned long t0 = millis();

  Serial.print ("Synching Time over NTP ");
  while((NOW = time(nullptr)) < NTP_MIN_VALID_EPOCH) {
    // warning : no time out. May loop here forever
    delay(20);
    Serial.print(".");
  }
  Serial.println("");

  unsigned long t1 = millis() - t0;
  Serial.println("NTP time first synch took " + String(t1) + "ms");
}


void setNOW()
{
  NOW = time(nullptr);
}


// Breaks NOW into tm structure and updates NOW_TM global var
void setNOW_TM()
{
  NOW_TM  = localtime(&NOW);  // cf: https://www.cplusplus.com/reference/ctime/localtime/
  Second  = NOW_TM->tm_sec;
  Minute  = NOW_TM->tm_min;
  Hour    = NOW_TM->tm_hour;
  Weekday = NOW_TM->tm_wday + 1 ;
  Day     = NOW_TM->tm_mday;
  Month   = NOW_TM->tm_mon + 1;
  Year    = NOW_TM->tm_year + 1900;      // tm returns years since 1900
  strftime (DayName , 12, "%A", NOW_TM); // cf: https://www.cplusplus.com/reference/ctime/strftime/
  strftime (MonthName, 12, "%B", NOW_TM);
  strftime (Time,10, "%T", NOW_TM);
  strftime (Date,12, "%d/%m/%Y", NOW_TM);
}


// returns String with pattern from time_t time
// formats : https://www.cplusplus.com/reference/ctime/strftime/
// default pattern is "%d-%m-%Y %H:%M:%S"
String dateTimeStr(time_t epochtime, char* pattern)
{
    tm *lt;
    lt = localtime(&epochtime);
    char buff[30];
    strftime(buff, 30, pattern, lt);
    return buff;
}
User avatar
By sprior
#94654 Following up on Alka's work which I came across yesterday. I don't know if the libraries have improved since that example, but I was able to simplify it slightly (forgive the minor other changes for time/date format).

This method of defining the timezone works for both ESP8266 and ESP32
Code: Select all//#define MYTZ "EST5EDT,M3.2.0,M11.1.0"
#define MYTZ "EST5EDT"


And here is my modified initNTP(), I've added a timeout and use configTzTime on ESP32 which makes it a lot closer to the ESP8266 version:
Code: Select allvoid initNTP()
{
  #ifdef ARDUINO_ARCH_ESP32
     configTzTime(MYTZ, NTP_SERVER);
  #else
     configTime(MYTZ, NTP_SERVER);
  #endif

  unsigned long t0 = millis();

  Serial.print ("Synching Time over NTP ");
  time_t NOW;
  int ntp_retry=500;
  while( ((NOW = time(nullptr)) < NTP_MIN_VALID_EPOCH) && (ntp_retry>0)) {
    delay(20);
    Serial.print(".");
    --ntp_retry;
  }
  Serial.println("");
  if (ntp_retry == 0){
     Serial.println("ntp init timed out");
  }

  unsigned long t1 = millis() - t0;
  Serial.println("NTP time first synch took " + String(t1) + "ms");

  Serial.println("It is now " + dateTimeStr(NOW, "%m-%d-%Y %H:%M:%S"));
}