For several months, I have been trying to get my Arduino sketch to connect to wifi, obtain time from an ntp server, update the ESP32 internal time, display the time on an LED clock, and disconnect from Wifi. I use Wifi manager initially to setup wifi credentials and then obtain the time as outlined previously. That has been working fine...time displayed and updated on clock. However, since internal timekeeping of ESP32 drifts a fair amount, I want to autoconnect to the internet when the day changes (i.e., midnight) and follow the previous steps to update the internal ESP32 to keep the clock more accurate. I thought this wouldn't be too difficult, but time displayed on my LED clock still drifts. I think the wifi connecting and disconnecting functions are working fine. I also think reaching the NTP server is working. The problem seems to exist with clearing/updating the internal ESP32 timekeeping mechanism. This is my first forum post, so please be gentle Thanks!
/* Use this sketch with Paul's LED Clock. Select the ESP32 Dev Module board with partition scheme for "Huge APP".
It uses the WifiManager library to enter wifi credentials over an internet connected device instead of
hard-coded credentials. The sketch also accesses the Internet daily to adjust the time. */
#include <WiFi.h>
#include "time.h"
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
#define MAXTIMEFAILCOUNT 5 // Maximum number of times the wireless time setting process can fail before giving up
#define WIFIFAILCOUNT 5 // Maximum number of tries to connect to the wireless network
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 8
#define CLK_PIN 14
#define DATA_PIN 13
#define CS_PIN 15
#define BUF_SIZE 45
#define SPEED_BASE 300
char curMessage[BUF_SIZE] = { "" };
char newMessage[BUF_SIZE] = { "" };
bool newMessageAvailable = true;
byte intensityDisplay;
byte speedIndex = 3;
bool dailyTimeCheck = false;
WiFiManager wm;
struct tm timeinfo;
const long checkinterval = 2000; // time to try again to obtain ntp time
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = -18000;
const int daylightOffset_sec = 3600;
int yr = 0;
int mt = 0;
int dy = 0;
int hr = 0;
int mi = 0;
int se = 0;
int prevMinute = -1; // // Capture the minute to check if it's time to update the ntp time
int prevHour = -1; // Capture the hour to check if it's time to update the ntp time
int prevDay = -1; // Capture the day to check if it's time to update the ntp time
MD_Parola P = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
// it is a good practice to make sure your code sets wifi mode how you want it.
//WiFiManager, Local intialization. Once its business is done, there is no need to keep it around
// WiFiManager wm;
/* Reset settings - For testing purposes, wipes eeprom credentials
stored by the ESP library.
Uncomment line below for testing wireless setup.
User must re-enter wireless credentials each time device is powered up.
*/
//wm.resetSettings();
// Automatically connect using saved credentials,
// if connection fails, it starts an access point with the specified name ( "AutoConnectAP"),
// if empty will auto generate SSID, if password is blank it will be anonymous AP (wm.autoConnect())
// then goes into a blocking loop awaiting configuration and will return success result
bool res;
// res = wm.autoConnect(); // auto generated AP name from chipid
res = wm.autoConnect("LEDClockConnectAP"); // anonymous ap
// res = wm.autoConnect("AutoConnectAP","password"); // password protected ap
if (!res) {
Serial.println("Failed to connect.");
// ESP.restart();
} else {
//if you get here you have connected to the WiFi
Serial.println("Success...connected to WiFi.");
}
//wifiConnect(); Don't need to connect to wifi, already connected
P.begin();
P.displayClear();
P.displayReset();
intensityDisplay = 2;
P.setIntensity(intensityDisplay);
fetchNTPTime();
strcpy(curMessage, newMessage);
newMessageAvailable = false;
P.displayText(curMessage, PA_CENTER, (SPEED_BASE / speedIndex), 0, PA_PRINT, PA_NO_EFFECT);
wifiDisconnect();
}
void loop() {
if (P.displayAnimate() && newMessageAvailable) {
strcpy(curMessage, newMessage);
newMessageAvailable = false;
P.displayText(curMessage, PA_CENTER, (SPEED_BASE / speedIndex), 0, PA_PRINT, PA_NO_EFFECT);
}
timeUpdate(); // Check every second to update time on the display
}
// This file contains the wifi and time code
/*
%A Full weekday name
%B Full month name
%d Day of the month
%Y Year
%H Hour in 24h format
%I Hour in 12h format
%M Minute
%S Second
*/
void timeUpdate() {
// struct tm timeinfo;
if (isNewDay()) {
dailyTimeCheck = true;
wifiConnect();
fetchNTPTime(); // Use a function to get NTP time
wifiDisconnect();
dailyTimeCheck = false;
} else {
if (getLocalTime(&timeinfo) && !dailyTimeCheck) {
int currentMinute = timeinfo.tm_min;
if (currentMinute != prevMinute) {
// Update the time on the display
strftime(newMessage, sizeof(newMessage), "%H : %M", &timeinfo);
newMessageAvailable = true;
// Update the previous minute
prevMinute = currentMinute;
}
}
}
}
// bool isNewHour() {
// struct tm timeinfo;
// if (getLocalTime(&timeinfo)) {
// int currentHour = timeinfo.tm_hour;
// if (currentHour != prevHour) {
// // Update the previous day
// prevHour = currentHour;
// return true;
// }
// }
// return false;
// }
bool isNewDay() {
// struct tm timeinfo;
if (getLocalTime(&timeinfo)) {
int currentDay = timeinfo.tm_mday;
if (currentDay != prevDay) {
// Update the previous day
prevDay = currentDay;
return true;
}
}
return false;
}
void fetchNTPTime() {
// Clear existing time on ESP32
struct timeval tv_reset;
tv_reset.tv_sec = 0;
tv_reset.tv_usec = 0;
settimeofday(&tv_reset, nullptr);
// Obtain time from NTP server
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); // Config time synchronization parameters
Serial.println("Waiting for time from NTP...");
time_t now = time(nullptr);
int retry = 0;
while (now < 24 * 3600) { // Time set to 1970, waiting for NTP sync
delay(500);
Serial.print(".");
now = time(nullptr);
retry++;
if (retry > 20) {
Serial.println("Failed to obtain time from NTP server.");
return;
}
}
// Update internal clock of ESP32
struct tm timeinfo;
gmtime_r(&now, &timeinfo);
struct timeval tv;
tv.tv_sec = now;
tv.tv_usec = 0;
settimeofday(&tv, nullptr);
yr = timeinfo.tm_year + 1900;
mt = timeinfo.tm_mon + 1;
dy = timeinfo.tm_mday;
hr = timeinfo.tm_hour;
mi = timeinfo.tm_min;
se = timeinfo.tm_sec;
Serial.println("Time obtained from NTP server:");
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
// Immediately update the LED clock display
strftime(newMessage, sizeof(newMessage), "%H : %M", &timeinfo);
newMessageAvailable = true;
P.displayText(newMessage, PA_CENTER, (SPEED_BASE / speedIndex), 0, PA_PRINT, PA_NO_EFFECT);
}
void wifiConnect() {
// Connect to WiFi
static int wifiFail = 0; // Count the number of failed attempts to connect to wifi
static unsigned long lastTimeCheck = 0;
unsigned long timeNow = millis();
Serial.println("Connecting to wifi.");
wm.setConfigPortalBlocking(false); // Per example, this should result in non-blocking code.
wm.setConfigPortalTimeout(120);
wm.setTimeout(20);
wm.autoConnect();
if (WiFi.status() != WL_CONNECTED) {
wifiFail++;
if (wifiFail < WIFIFAILCOUNT) {
Serial.println("Trying again to connect to WiFi.");
if ((timeNow - lastTimeCheck) >= checkinterval) {
wifiConnect();
lastTimeCheck = timeNow;
}
} else {
Serial.println("Unable to connect to WiFi after several attempts.");
}
} else {
Serial.println("Connected to WiFi.");
}
}
void wifiDisconnect() {
// Disconnect from Wifi
Serial.printf("Disconnecting from WiFi.");
Serial.println();
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF); // This command should actually turn off wifi
}