gmag11 / ESPNtpClient

High accuracy NTP library for ESP32 and ESP8266
MIT License
116 stars 25 forks source link

sync with TimeLib.h #35

Closed RobInSaratoga closed 1 year ago

RobInSaratoga commented 2 years ago

Reference issue #26

I was able to stop the segfaults by changing the code in the loop to remove the delays. (I think someone else on this list mentioned a similar problem...).

Anyhow, I have the code working, partially. Thank you for the help. I have pasted my current version below. A few notes, then a question or two...

The code outputs on the serial port and the LCD screen. I added an IF statement in the loop to check that it was time (i.e. at the "top" of a new second) to print the time to the serial port. This was successful; here is some output;

09/01/2022 09:25:53.000036 PST 09/01/2022 09:25:54.000047 PST 09/01/2022 09:25:55.000063 PST 09/01/2022 09:25:56.000041 PST

You can see that the NTP.getTimeDateStringUs () print out is at (nearly) the top of the new second (or close enough for me). I would like to enable a similar condition to the update of the LCD displayed time.

(I set a pin high and then low to give me a reference pulse to measure using an oscilloscope.)

At first, the LCD display was one second behind reference time (* in this case that means listening to the shortwave broadcast of the atomic clock in the USA), and one second behind the serial output. So I added +1 to the seconds in the setTime() statement. However, after running overnight, the LCD display output and serial output were one second fast.

Can you help me understand what I am doing wrong?

My goal here is to set the time in the TimeLib at the top of the second after the NTP update has occurred. I was not successful using the "if NTP.millis() % 1000 == 0, then setTime(etc...)" approach in the event handler. I made a note in the code below. I was thinking that this condition would make the code wait until the top of the second to update the TimeLib (which can only count in whole seconds, not milliseconds or microseconds).

Can you recommend a way to do this?

Also, is this 50000 in microseconds?

NTP.setMinSyncAccuracy (50000);

Many thanks.

#include <ESPNtpClient.h>
#include <TimeLib.h>
#ifdef ESP32
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
#endif
#include <LiquidCrystal_I2C.h>
#include <Wire.h>

#ifndef WIFI_CONFIG_H
#define YOUR_WIFI_SSID "ssid"
#define YOUR_WIFI_PASSWD "password"
#endif // !WIFI_CONFIG_H

int stepPin = D7;

#define SHOW_TIME_PERIOD 1000
LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  //some are 0x3F, some are 0x27
const PROGMEM char* ntpServer = "us.pool.ntp.org";
void ntpEventHandler (NTPEvent_t e) {
  //  Serial.printf("NTP Event: %s\n", NTPEventTypeToString (e).c_str ());
  Serial.println("getting time stamp...");
  if (e.event == timeSyncd || e.event == partlySync) {  // && NTP.millis() % 1000
    time_t utcTime = time (NULL);
    tm* currentTime = localtime (&utcTime);
    // Serial.printf ("Year: %d\n", currentTime->tm_year + 1900);
    // Serial.printf ("Month: %d\n", currentTime->tm_mon + 1);
    // Serial.printf ("Day: %d\n", currentTime->tm_mday);
    // Serial.printf ("Hour: %d\n", currentTime->tm_hour);
    // Serial.printf ("Minute: %d\n", currentTime->tm_min);
    // Serial.printf ("Second: %d\n", currentTime->tm_sec);

    setTime (currentTime->tm_hour, currentTime->tm_min, currentTime->tm_sec + 1, currentTime->tm_mday, currentTime->tm_mon + 1, currentTime->tm_year + 1900);

  }

  if (timeStatus() == timeSet) {
    Serial.println("arduino time has been set...");
  }
  else {
    Serial.println("arduino time has NOT been set...");
  }
}

void setup() {
  Serial.begin (115200);
  Serial.println ();
  lcd.begin(20, 4);
  Wire.begin();
  WiFi.begin (YOUR_WIFI_SSID, YOUR_WIFI_PASSWD);
  Serial.println();
  Serial.print("ESP8266 Board MAC Address:  ");
  Serial.println(WiFi.macAddress());
  Serial.print("Station IP Address: ");
  Serial.println(WiFi.localIP());
  Serial.print("Wi-Fi Channel: ");
  Serial.println(WiFi.channel());
  long rssi = WiFi.RSSI();
  Serial.print("RSSI:");
  Serial.println(rssi);
  Serial.println("******************************************************************");
  Serial.println(F(__FILE__));
  Serial.println(F(__DATE__ " " __TIME__));
  Serial.println("******************************************************************");
  NTP.setTimeZone (TZ_America_Los_Angeles);
  NTP.setMinSyncAccuracy (50000);
  NTP.begin (ntpServer);
  //setSyncProvider (getTime); // You can use this instead of next line but timeLib will not sync until second try. If you use this, you need to uncomment getTime() fuctions in the code upwards.
  NTP.onNTPSyncEvent (ntpEventHandler);
  pinMode(stepPin, OUTPUT);
}

void loop()
{
  if (NTP.millis() % 1000 == 0) {
    Serial.println(NTP.getTimeDateStringUs ());
    digitalClockDisplay();
    digitalWrite(stepPin, HIGH);
    digitalWrite(stepPin, LOW);
  }
}

void digitalClockDisplay()  {
  // Display time
  lcd.setCursor(3, 0);
  lcd.print(hourFormat12());
  printDigits(minute());
  printDigits(second());
  if (isAM() == true) {
    lcd.print(" AM ");
  }
  else {
    lcd.print(" PM ");
  }
  //  Display day/date/year
  lcd.setCursor(0, 1);
  lcd.print("  ");
  lcd.setCursor(2, 1);
  lcd.print(dayShortStr(weekday()));
  lcd.print(", ");
  lcd.print(monthShortStr(month()));
  lcd.print(" ");
  lcd.print(day());
  lcd.print(", ");
  lcd.print(year());
  lcd.print(" ");
}

void printDigits(int digits)   {
  lcd.print(":");
  if (digits < 10)
    lcd.print('0');
  lcd.print(digits);
}
JyunWei-Su commented 2 years ago

Hello, The NTP.setMinSyncAccuracy (5000); is in microseconds (us). You can find it in ESPNtpClient.h, the default value is DEFAULT_MIN_SYNC_ACCURACY_US.

RobInSaratoga commented 1 year ago

It's been a few months and I have learned a few things. First, there is little need to use the TimeLib.h library, when I can just use the regular C++ functions and access the time that ESPNtpClient is updating directly. TimeLib.h is very convenient to use, it's well supported and documented - perfect for someone new to programming. But as I learned more about time.h (ctime) I realized I could get what I wanted without the complexity of getting the SDK set accurately and then trying to synchronizing it to the TimeLib.h so that I can display it. Furthermore, the ESPNtpClient library is very convenient to use with regard to wifi, and initial synchronization (it syncs several times initially, but then less often once the clock is set).

I offer my experience here to others in case it may help them.

I learned how to grab the exact "top of the minute" (okay... not EXACTLY, but really, really close) and format the output to display it to an LCD. Now I have a very, very accurate desk clock that stays in synchrony to the atomic reference on my shortwave radio. Using TimeLib.h the clock was never spot-on as the library was never intended for precision beyond one second.

Here is a snip of the relevant code.

` if (syncEventTriggered == true) { //if the time has been set, then... syncEventTriggered == false; utc = (NTP.millis() / 1000); //this makes the clock display time at the very top of each second if (utc != prevDisplay) { //this makes the clock display change only if it's different from last time prevDisplay = utc; time_t now; char Dbuff[20]; char Tbuff[20]; strftime(Dbuff, 20, "%a, %b %d, %Y", localtime(&now)); //weekday, month, date, year strftime(Tbuff, 20, "%I:%M:%S %p %Z", localtime(&now)); //hour(12):minute:second, AM/PM, time zone

  // LCD output
  lcd.setCursor (3, 0);
  lcd.print(Tbuff);
  lcd.setCursor (2, 1);
  lcd.print(Dbuff);

`