diff --git a/TEF6686_ESP32.ino b/TEF6686_ESP32.ino index d544c73..16c624d 100644 --- a/TEF6686_ESP32.ino +++ b/TEF6686_ESP32.ino @@ -10,6 +10,7 @@ #include // https://github.com/bbx10/Hash_tng/archive/refs/heads/master.zip #include #include +#include "src/NTPupdate.h" #include "src/WiFiConnect.h" #include "src/WiFiConnectParam.h" #include "src/FONT16.h" @@ -99,6 +100,7 @@ bool mwstepsize; bool airstepsize; #endif bool nobattery; +bool NTPupdated; bool optenc; bool rdsflagreset; bool rdsreset; @@ -200,6 +202,7 @@ byte memstoppos; byte menuitem; byte menupage; byte MSold; +byte NTPoffset = 1; byte poweroptions; byte rdsblockold; byte rdsqualityold; @@ -414,6 +417,7 @@ unsigned long lastTouchTime = 0; unsigned long lowsignaltimer; unsigned long ModulationpreviousMillis; unsigned long ModulationpeakPreviousMillis; +unsigned long NTPtimer; unsigned long peakholdmillis; unsigned long pslongticker; unsigned long pslongtickerhold; @@ -931,6 +935,11 @@ void setup() { void loop() { if (wifi) webserver.handleClient(); + if (wifi && millis() >= NTPtimer + 1800000) { + NTPupdate(); + NTPtimer = millis(); + } + if (hardwaremodel == PORTABLE_TOUCH_ILI9341 && touch_detect) { if (tft.getTouchRawZ() > 100) { // Check if the touch is active uint16_t x, y; @@ -5571,43 +5580,43 @@ bool addRowToCSV() { } String getCurrentDateTime() { - // Check if time has been set + // Check if the RTC has been set if (!rtcset) { - return "-,-"; // Return placeholder when time is not set + return "-,-"; // Return placeholder when the RTC is not set } - // Use the ESP32's time functions (assuming time is set correctly via RDS) + // Use the ESP32's time functions to retrieve the current time struct tm timeInfo; if (!getLocalTime(&timeInfo)) { - return "-,-"; // Return placeholder if time is not available + return "-,-"; // Return placeholder if local time is unavailable } // Adjust timeInfo using the GMT offset - time_t currentEpoch = mktime(&timeInfo); // Convert struct tm to time_t - currentEpoch += radio.rds.offset; // Apply the offset (in seconds) - localtime_r(¤tEpoch, &timeInfo); // Convert back to struct tm + time_t currentEpoch = mktime(&timeInfo); // Convert struct tm to time_t format + currentEpoch += (NTPupdated ? NTPoffset * 3600 : radio.rds.offset); // Apply GMT offset if NTPupdated, else RDS offset + localtime_r(¤tEpoch, &timeInfo); // Convert adjusted time back to struct tm format - // Format date-time based on the region - char buf[20]; // Buffer size for formatted date string + // Buffer for formatted date-time string + char buf[20]; if (radio.rds.region == 1) { // USA format: MM/DD/YYYY, HH:MM AM/PM - strftime(buf, sizeof(buf), "%m/%d/%Y", &timeInfo); // MM/DD/YYYY format + strftime(buf, sizeof(buf), "%m/%d/%Y", &timeInfo); // Format as MM/DD/YYYY // Format time in 12-hour format with AM/PM int hour = timeInfo.tm_hour; - String ampm = (hour >= 12) ? "PM" : "AM"; - if (hour == 0) hour = 12; // Midnight case - else if (hour > 12) hour -= 12; // Convert PM to 12-hour format + String ampm = (hour >= 12) ? "PM" : "AM"; // Determine AM or PM + if (hour == 0) hour = 12; // Convert 0 hour to 12 AM + else if (hour > 12) hour -= 12; // Convert to 12-hour format for PM String timeWithAMPM = String(hour) + ":" + (timeInfo.tm_min < 10 ? "0" : "") + String(timeInfo.tm_min) + " " + ampm; - // Return final formatted date-time for USA + // Return the final formatted date and time for the USA region return String(buf) + "," + timeWithAMPM; } else { - // European format: DD/MM/YYYY, HH:MM - strftime(buf, sizeof(buf), "%d-%m-%Y", &timeInfo); // DD/MM/YYYY format - String timeEuropean = String(timeInfo.tm_hour) + ":" + (timeInfo.tm_min < 10 ? "0" : "") + String(timeInfo.tm_min); // Add leading 0 for minutes if necessary + // European format: DD-MM-YYYY, HH:MM + strftime(buf, sizeof(buf), "%d-%m-%Y", &timeInfo); // Format as DD-MM-YYYY + String timeEuropean = String(timeInfo.tm_hour) + ":" + (timeInfo.tm_min < 10 ? "0" : "") + String(timeInfo.tm_min); // Format time with leading zero if needed return String(buf) + "," + timeEuropean; } } diff --git a/src/NTPupdate.cpp b/src/NTPupdate.cpp index fe33504..3784cb5 100644 --- a/src/NTPupdate.cpp +++ b/src/NTPupdate.cpp @@ -1,58 +1,84 @@ #include "NTPupdate.h" -// send an NTP request to the time server at the given address +// Sends an NTP request packet to the specified server address void sendNTPpacket(IPAddress &address) { - byte packetBuffer[NTP_PACKET_SIZE]; - // 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 + byte packetBuffer[NTP_PACKET_SIZE] = {0}; // Initialize buffer with zeros + + // Set NTP packet header fields as per NTP protocol + packetBuffer[0] = 0b11100011; // LI, Version, Mode + packetBuffer[2] = 6; // Polling interval + packetBuffer[3] = 0xEC; // Peer clock precision + + // Root Delay & Root Dispersion fields 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 + + // Send the NTP request to port 123 (NTP standard port) + Udp.beginPacket(address, 123); Udp.write(packetBuffer, NTP_PACKET_SIZE); Udp.endPacket(); } +// Retrieves the current time from an NTP server time_t getNtpTime() { - IPAddress ntpServerIP; // NTP server's ip address + IPAddress ntpServerIP; byte packetBuffer[NTP_PACKET_SIZE]; - while (Udp.parsePacket() > 0) ; // discard any previously received packets - WiFi.hostByName(ntpServerName, ntpServerIP); + // Clear any previously received UDP packets + while (Udp.parsePacket() > 0); + + // Resolve the NTP server's hostname to its IP address + if (!WiFi.hostByName(ntpServerName, ntpServerIP)) { + return 0; // Return 0 if hostname resolution fails + } + + // Send an NTP request sendNTPpacket(ntpServerIP); - uint32_t beginWait = millis(); - while (millis() - beginWait < 1500) { - int size = Udp.parsePacket(); - if (size >= NTP_PACKET_SIZE) { - 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]; + + // Wait for a response with a 1500ms timeout + uint32_t startWait = millis(); + while (millis() - startWait < 1500) { + if (Udp.parsePacket() >= NTP_PACKET_SIZE) { // Check if a valid packet is received + Udp.read(packetBuffer, NTP_PACKET_SIZE); + + // Extract "seconds since 1900" from the packet (bytes 40-43) + unsigned long secsSince1900 = + ((unsigned long)packetBuffer[40] << 24) | + ((unsigned long)packetBuffer[41] << 16) | + ((unsigned long)packetBuffer[42] << 8) | + (unsigned long)packetBuffer[43]; + + // Convert to UNIX epoch time (seconds since 1970) return secsSince1900 - 2208988800UL; } } - return 0; // return 0 if unable to get the time + + // Return 0 if no valid response is received + return 0; } +// Updates the RTC with the time from an NTP server void NTPupdate() { - if (!wifi) { return; } - time_t time = getNtpTime(); - if (time) { - rtc.setTime(time); + // Abort if Wi-Fi is not connected + if (!wifi) { + NTPupdated = false; + return; } -} + // Retrieve the current time from the NTP server + time_t currentTime = getNtpTime(); + if (currentTime) { + // Set the RTC if valid time is received + rtc.setTime(currentTime); + rtcset = true; + NTPupdated = true; + radio.rds.ctupdate = false; + } else { + // Indicate that the update failed + NTPupdated = false; + radio.rds.ctupdate = true; + } +} \ No newline at end of file diff --git a/src/NTPupdate.h b/src/NTPupdate.h index 30ec0a8..8e5bc5b 100644 --- a/src/NTPupdate.h +++ b/src/NTPupdate.h @@ -7,6 +7,7 @@ #include #include #include +#include "TEF6686.h" static const char ntpServerName[] = "0.pool.ntp.org"; static const int localPort = 8944; @@ -15,7 +16,11 @@ const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message extern ESP32Time rtc; extern WiFiClient RemoteClient; extern WiFiUDP Udp; +extern TEF6686 radio; + extern bool wifi; +extern bool NTPupdated; +extern bool rtcset; void sendNTPpacket(IPAddress &address); void NTPupdate(); diff --git a/src/TEF6686.cpp b/src/TEF6686.cpp index 6dedeac..4cc1918 100644 --- a/src/TEF6686.cpp +++ b/src/TEF6686.cpp @@ -1340,7 +1340,7 @@ void TEF6686::readRDS(byte showrdserrors) { } break; case RDS_GROUP_4A: { - if (!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) { + if (!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold && rds.ctupdate) { // CT uint32_t mjd; mjd = (rds.rdsB & 0x03); diff --git a/src/TEF6686.h b/src/TEF6686.h index 96f6d15..98708fd 100644 --- a/src/TEF6686.h +++ b/src/TEF6686.h @@ -621,6 +621,7 @@ typedef struct _rds_ { bool filter; bool rdsreset; bool pierrors; + bool ctupdate = true; bool sortaf; bool rtbuffer = true; bool afreg; diff --git a/src/comms.cpp b/src/comms.cpp index 0def40a..369fe85 100644 --- a/src/comms.cpp +++ b/src/comms.cpp @@ -912,6 +912,7 @@ void tryWiFi() { Server.begin(); Udp.begin(9031); webserver.begin(); + NTPupdate(); remoteip = IPAddress (WiFi.localIP()[0], WiFi.localIP()[1], WiFi.localIP()[2], subnetclient); if (!setupmode) tftPrint(0, myLanguage[language][57], 155, 128, InsignificantColor, InsignificantColorSmooth, 28); } else { diff --git a/src/comms.h b/src/comms.h index e0ca88b..6ebc2e0 100644 --- a/src/comms.h +++ b/src/comms.h @@ -149,4 +149,5 @@ extern void ShowStepSize(); extern void startFMDXScan(); extern void cancelDXScan(); extern void printLogbookCSV(); +extern void NTPupdate(); #endif \ No newline at end of file diff --git a/src/rds.cpp b/src/rds.cpp index 97d2874..e7415bd 100644 --- a/src/rds.cpp +++ b/src/rds.cpp @@ -787,89 +787,70 @@ void showCT() { char str[6]; time_t t; - // Check if screen is not muted and the clock should be displayed - if (!screenmute && showclock) { + // Determine the current time source + if (radio.rds.hasCT && !dropout && !NTPupdated) { + t = radio.rds.time + radio.rds.offset; + } else { + t = rtc.getEpoch() + (NTPupdated ? 0 : radio.rds.offset); - // If RDS CT (Clock Time) is available and no dropout, use RDS time - if (radio.rds.hasCT && !dropout) { - t = radio.rds.time + radio.rds.offset; + // Update RDS time during dropout + if (dropout) { + radio.rds.time = static_cast(rtc.getEpoch()); } - // If no RDS CT or there is a dropout, fall back to RTC time - else { - t = rtc.getEpoch() + radio.rds.offset; - - // Update RDS time in case of dropout - if (dropout) { - radio.rds.time = static_cast(rtc.getEpoch()); - } - } - - // Check if USA region, use 12-hour AM/PM format - if (radio.rds.region == 1) { - // Format time in 24-hour format first - strftime(str, sizeof(str), "%I:%M", localtime(&t)); - - // Manually determine AM/PM and add it - int hour = localtime(&t)->tm_hour; - String ampm = (hour >= 12) ? "PM" : "AM"; - - // Adjust the hour to 12-hour format, taking care of 12 AM and 12 PM - if (hour == 0) { - hour = 12; // Midnight case - } else if (hour > 12) { - hour -= 12; // Convert PM to 12-hour format - } - - // Construct the final time string manually - rds_clock = String(hour) + ":" + String(localtime(&t)->tm_min) + " " + ampm; - } else { - // For other regions, use 24-hour format - strftime(str, sizeof(str), "%H:%M", localtime(&t)); - rds_clock = String(str); - } - - // If the clock has changed or RDS CT status has changed, update the display - if (rds_clock != rds_clockold || hasCTold != radio.rds.hasCT) { - - // If RDS CT is available and RDS status is active, set RTC time - if (radio.rds.hasCT && RDSstatus) { - rtcset = true; - rtc.setTime(radio.rds.time); - - // Display the new time with different coordinates based on advancedRDS setting - if (advancedRDS) { - tftReplace(1, rds_clockold, rds_clock, 208, 109, RDSColor, RDSColorSmooth, BackgroundColor, 16); - } else { - tftReplace(1, rds_clockold, rds_clock, 208, 163, RDSColor, RDSColorSmooth, BackgroundColor, 16); - } - } - // If no RDS CT available or status is inactive, handle dropout scenarios - else { - // If RTC was previously set, show dropout message - if (rtcset) { - if (advancedRDS) { - tftReplace(1, rds_clockold, rds_clock, 208, 109, RDSDropoutColor, RDSDropoutColorSmooth, BackgroundColor, 16); - } else { - tftReplace(1, rds_clockold, rds_clock, 208, 163, RDSDropoutColor, RDSDropoutColorSmooth, BackgroundColor, 16); - } - } - // If RTC is not set, just print the clock with no background (clear the display) - else { - if (advancedRDS) { - tftPrint(1, rds_clockold, 208, 109, BackgroundColor, BackgroundColor, 16); - tftPrint(1, rds_clock, 208, 109, BackgroundColor, BackgroundColor, 16); - } else { - tftPrint(1, rds_clockold, 208, 163, BackgroundColor, BackgroundColor, 16); - tftPrint(1, rds_clock, 208, 163, BackgroundColor, BackgroundColor, 16); - } - } - } - } - - // Update the previous clock and RDS CT status to detect future changes - rds_clockold = rds_clock; - hasCTold = radio.rds.hasCT; } + + // Apply the GMT offset only if NTPupdated is true + if (NTPupdated) { + t += NTPoffset * 3600; // Convert offset from hours to seconds + } + + // Format the time based on region + if (radio.rds.region == 1) { // USA region: 12-hour AM/PM format + strftime(str, sizeof(str), "%I:%M", localtime(&t)); + + // Determine AM/PM and adjust hour format + int hour = localtime(&t)->tm_hour; + String ampm = (hour >= 12) ? "PM" : "AM"; + if (hour == 0) { + hour = 12; // Midnight case + } else if (hour > 12) { + hour -= 12; // Convert PM hours + } + + rds_clock = String(hour) + ":" + String(localtime(&t)->tm_min) + " " + ampm; + } else { // Other regions: 24-hour format + strftime(str, sizeof(str), "%H:%M", localtime(&t)); + rds_clock = String(str); + } + + // Check if the clock or RDS CT status has changed + if (!screenmute && showclock && (rds_clock != rds_clockold || hasCTold != radio.rds.hasCT)) { + + // Update RTC if RDS CT is available or NTP was updated + if ((radio.rds.hasCT && RDSstatus) || NTPupdated) { + rtcset = true; + if (!NTPupdated) { + rtc.setTime(radio.rds.time); + } + + // Display the updated time + int yCoord = advancedRDS ? 109 : 163; + tftReplace(1, rds_clockold, rds_clock, 208, yCoord, RDSColor, RDSColorSmooth, BackgroundColor, 16); + } else { // Handle dropout scenarios + int yCoord = advancedRDS ? 109 : 163; + + if (rtcset) { // Display dropout message if RTC was set + tftReplace(1, rds_clockold, rds_clock, 208, yCoord, RDSDropoutColor, RDSDropoutColorSmooth, BackgroundColor, 16); + } else { // Clear and reprint the clock + tftPrint(1, rds_clockold, 208, yCoord, BackgroundColor, BackgroundColor, 16); + tftPrint(1, rds_clock, 208, yCoord, BackgroundColor, BackgroundColor, 16); + } + } + } + + // Update previous clock and RDS CT status + rds_clockold = rds_clock; + hasCTold = radio.rds.hasCT; } void showRadioText() { diff --git a/src/rds.h b/src/rds.h index 6c1b88a..dca1c2d 100644 --- a/src/rds.h +++ b/src/rds.h @@ -26,6 +26,7 @@ extern bool haseonold; extern bool hasrtplusold; extern bool hastmcold; extern bool memreset; +extern bool NTPupdated; extern bool rdsreset; extern bool RDSSPYTCP; extern bool RDSSPYUSB; @@ -51,6 +52,7 @@ extern byte ECCold; extern byte language; extern byte licold; extern byte MSold; +extern byte NTPoffset; extern byte eonptyold[20]; extern byte rdsblockold; extern byte rdsqualityold;