Correctly checking tm_wday for start of new day

If I understand things correctly it is only a few microseconds after prev_Day is given the value of tm_wday when the code check is made to see if tm_wday is no longer equal to prev_Day. I have a 100 ms delay to give a brief pause before the comparison is made. This may not be necessary but I thought it might help. The check is failing right now so would increasing the delay make sense?

void RequestLocalTime() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }
  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S zone %Z %z ");
  hour = timeinfo.tm_hour;
  minute = timeinfo.tm_min;
  second = timeinfo.tm_sec;
  day = timeinfo.tm_mday;
  monthadd1 = timeinfo.tm_mon;  // January is month 0 in the structure
  monthplus1 = monthadd1 + 1;
  month = monthplus1;
  prevDay = timeinfo.tm_wday;
  delay(100);
  if (timeinfo.tm_wday != prevDay) {     // check for new day    
    midnite_ctr_reset();                 // reset counters at start of new day
    initTime("CST6CDT,M3.2.0,M11.1.0");  // update processor time with NTP request
    if (error1 == true) {
      strcpy(error_status, "Get NTP Time FAIL.");
    }
    prevDay = timeinfo.tm_wday;
  }
}

Don't understand why you're doing it that way. Why not move the assignment after the comparison? And, why do you need to call initTime() with the time zone information every day?

1 Like

To me logically that does not make sense. You assign prev_Day before
the comparison and then check to see if tm_wday has changed since the assignment was made. I might be all wet about that. Wouldn't be the 1st time.

I do a daily NTP request at midnight to sync up the time. Is this unnecessary in your view?

void initTime(const char* timezone) {
  struct tm timeinfo;
  Serial.println(F("Getting time from NTP server"));
  configTime(0, 0, "pool.ntp.org");  // First connect to NTP server, use 0 TZ offset
  if (!getLocalTime(&timeinfo)) {
    Serial.println(F("  Failed to obtain time"));
    error1 = true;  // NTP time update request failure
    return;
  }
  Serial.println(F("OK, Got the time from NTP"));
  setTimezone(timezone);
}

Take a look at the 'StateChangeDetection' example. It's the exact same concept.

Assuming ESP32 / ESP8266, it's completely unnecessary. NTP synching happens automatically in the background.

Time to delete some superfluous code..... lol

Is this a reference to the IDE Examples or something in the Arduino Doc? Everything I have found related is in reference to a digital input.

edit - Found it!

Right. Exactly the same concept. Instead of looking for when a digital input changes value, you're looking for when the tm_wday changes value.

Ok, I understand how state change detection works. I believe where I was getting hung up was with prev_Day. I realized after compiling the code I never defined prev_Day , as an integer or whatever. But the compiler did not care. So that has to mean prev_Day is a part of the library associated with the tm struct. Please say 'yes!'....lol


```cpp
if (timeinfo.tm_wday != prevDay) {     // check for new day    
    midnite_ctr_reset();                 // reset counters at start of new day 
    }
    prevDay = timeinfo.tm_wday;
  }

I highly doubt it.

Rightly so.... no underscore on the 'find' search function and there it was. :flushed: So if I only define it as an integer with no value set does the compiler make it a '0' by default?

It seems that you also here have gone the long and complicated way.
Half of the code is not needed, because it's already part of the ESP core.
NTP for the ESP8266 can be as easy as I explained to you in an ESP32 NTP thread.
I would dump the code, and re-write it all.

Example sketch for the ESP8266 attached.
It includes a "wait for NTP sync".

Do you store 'now' in your log?
"preferences" (also part of the core) could be easier for that than SPIFFS/LittleFS.
Leo..

#include <ESP8266WiFi.h>  // ESP8266
#include <coredecls.h>
unsigned long prevTime;
bool sync;
time_t now;  // epoch
tm tm;       // time struct

void time_is_set() {
  Serial.println(F("TimeSync"));
  sync = true;
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin("SSID", "PASS");  // credentials
  settimeofday_cb(time_is_set); // enable callback
  configTime("CST6CDT,M3.2.0,M11.1.0", "pool.ntp.org");  // Chicago
  while (!sync) yield();                                 // wait here for time sync
}

void loop() {
  time(&now);                // now
  if (now != prevTime) {     // if time has changed
    prevTime = now;          // remember
    localtime_r(&now, &tm);  // convert 'now' to the local time elements
    printf("%02u:%02u:%02u\n", tm.tm_hour, tm.tm_min, tm.tm_sec);
  }
}

As gfvalvo told me earlier. I now am aware of this:

I do use an ESP8266 for a power outage monitor so your code is helpful there and on the contingent of ESP32s I employ.

In my SPIFFS file on the 8266 I make a 'current time' entry for the outage start and then use 'current time'/'now' again to log the power return time.

Not sure what the 'preferences' reference is to but you have piqued my interest about it.

Is there a good website to read up on time and NTP syncing for neophytes?
I really need to understand it better because code snippets like 'time_t now = time(nullptr)' and how that is different from just 'time(&now)' is foreign to me currently.
The code you offered is very streamlined in comparison to what I have been using. And I have to admit I am now using Google Chrome's AI feature to help explain more of the code I encounter. For instance the line 'settimeofday_cb(time_is_set)' was not understood but I the AI took me to a helpful website:(ESP8266 tips and tricks

Learning curves become more challenging as we get a lot older.... just trying to keep my head abouve the waves....lol

Thanks for the help Leo.. :+1:

Just ask.
The optional callback function void time_is_set() just lets you know if there was an NTP update. I have a print line in that function that prints "TimeSync" every hour (default update time), and a "sync" variable.

The callback function is activated by settimeofday_cb(time_is_set);
The library call for that is #include <coredecls.h> (comes with the ESP8266 core)

The "sync" variable stops setup() from completing until a valid time is received.
That might be important for your progam.

"now" is a variable that contains the seconds since 1-1-1970, so if you store/retrieve that, you have every UTC time variable back, not just the time.
localtime_r(&storednow, &tm); converts that into all the local time elements.

Preferences (included in the ESP32 core).
Leo.. (70+)

Uh, yes indeed. Knowing the processor has accurate time for logging purposes is essential.
So that feature alone makes scrubbing my code all the more important. :white_check_mark: :white_check_mark:

edit - is 'preferences' volatile....

No, preferences is permanent, like SPIFFS/LittleFS.
It's default size is 20k, and meant for single variables.
It has AFAIK wear levelling (safer).
ESP32-only.
Leo..
https://docs.espressif.com/projects/arduino-esp32/en/latest/tutorials/preferences.html

a simple example and some more hints regarding NTP from my page:

for the ESP8266
https://werner.rothschopf.net/202011_arduino_esp8266_ntp_en.htm

for the ESP32
https://werner.rothschopf.net/microcontroller/202103_arduino_esp32_ntp_en.htm

P.S.: there is a preferences.h library also for the smaller ESP8266.
name=Preferences
version=2.2.0
author=Volodymyr Shymanskyy

All my "time" experience in the Arduino ecosystem is with the ESP32 given that platform's advantages over ESP8286. So with that in mind ... here's a good place to start with the Time Functions as Implemented on ESP32. Many of the functions mentioned there are part of either the Standard C Language Library or specified by POSIX.

On peculiarity I've noted for ESP32 is that even after sntp_sync_time() returns and sntp_get_sync_status() reports SNTP_SYNC_STATUS_COMPLETED, the reported time will be wildly wrong (by decades). I call this condition "false synch". It resolves itself (usually in a few seconds) and the reported time snaps to the correct value. So, I check for this condition after boot up and wait for the time to be correct. Here's a bare bones example of the code I use. The file 'TZ.h' is attached. The ESP32 core doesn't come with one, but the ESP8286 core does.

#include "Arduino.h"
#include <WiFi.h>
#include "esp_sntp.h"
#include "TZ.h"

void printTimeTask(void *pvParameters);

void setup() {
  const uint32_t syncInterval { 60UL * 1000 };
  const char ssid[] { "xxxxx" };
  const char password[] { "xxxxx" };
  const char ntpServer1[] { "pool.ntp.org" };
  uint8_t attemptsBeforeReset { 5 };
  uint8_t retryTest { 0 };
  timeval tv;

  Serial.begin(115200);
  vTaskDelay(2000);
  Serial.println("Starting");

  sntp_set_sync_interval(syncInterval);
  configTzTime(TZ_America_New_York, ntpServer1);

  while (WiFi.status() != WL_CONNECTED) {
    if (retryTest == 0) {
      WiFi.disconnect();
      WiFi.begin(ssid, password);
      retryTest = 5;
    } else {
      Serial.println("Waiting for WiFi Connection");
      retryTest--;
      delay(1000);
    }
  }
  auto const address {WiFi.localIP()};
  Serial.printf("Connected with IP Address: %hhu.%hhu.%hhu.%hhu\n", address[0], address[1], address[2], address[3]);

  retryTest = 0;
  sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED);  // Set for immediate synch
  while (true) {                 // Wait for NTP sync to take effect ... don't be fooled by 'false synch'
    if (retryTest == 0) {
      if ((--attemptsBeforeReset) == 0) {
        ESP.restart();
      }
      Serial.println("Waiting for NTP Sync");
      sntp_sync_time(&tv);
      retryTest = 10;
    } else {
      retryTest--;
    }
    delay(2000);
    time_t now {time(nullptr)};
    tm *timeinfo {localtime(&now)};
    if (timeinfo->tm_year >= (2024 - 1900)) {  // check for 'false synch'
      break;  // synch is good, get on with things.
    }
  }
  Serial.println("NTP Synch Established");
  sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH);  // Set for smooth synch

  BaseType_t returnCode { xTaskCreatePinnedToCore(printTimeTask, "Print Task", 1900, NULL, 5, NULL, CONFIG_ARDUINO_RUNNING_CORE) };
  assert(returnCode == pdTRUE && "Failed to create Print Task");
}

void loop() {
}

void printTimeTask(void *pvParameters) {
  const TickType_t delayTime { 1000 };
  TickType_t wakeTime { xTaskGetTickCount() };
  char timeString[100];

  for (;;) {
    time_t now {time(nullptr)};
    tm *timeinfo {localtime(&now)};

    strftime(timeString, 100, "Local Time: %A, %B %d %Y %H:%M:%S %Z", timeinfo);
    Serial.println(timeString);
    xTaskDelayUntil(&wakeTime, delayTime);
  }
}

TZ.h (22.2 KB)

Is the 'yield' function useful in conjunction with a while statement when dealing with a temp sensor that every once in a 'blue moon' value drops out and the input goes to its default value (-193). It normally lasts 3 -4 seconds and returns. I thought about holding the processor up and wait for a good value to return. You would have to cover the contingency of it not returning though.
Otherwise I need to create an if statement that will compare the most recently scanned value to the previous value and ignore the new value if it is drastically different.

I don't see why you want all that NTP code.
All it takes is one location/server line in setup(). The ESP32 will do the rest.

I currently work with the Seeed XIAO ESP32C3. Single-core, low power, LiPo battery connection, built-in charger. Great little board if you don't need a lot of I/O. It usually gets an NTP sync within seconds. I never have seen it fail.

The yield() is there to give the processor time to attend to WiFi during the blocking while().
In my experience it was needed.

What temp sensor. For a DS18B20 you could set setWaitForConversion to false in setup().
Then a once a second read in loop is instantly.
Leo..

I display the new time when the time has actually changed.
So not with a delay(1000), but by looking if 'now' is different.
The delay() way can be out by up to one second.
Leo..

Excellent example, but the ESP32 code could be simplified by replacing the three green lines with a single line: configTzTime();
Leo..