Trying to use the built-in RTC and account for timezone and DST

Hi everyone,

I have been using the RTC-NTC code from the arduino documentation and it is working as intended. The only thing is the time displayed is not my local time. I would like to adjust the code to enable me to set GMT + 1 timezone and DST enabled. If this is done by typeing "+1" or "+3600" (or double that) I don't mind.

Unfortunately I am not a coder and for the past 2 days I've been trying to adopt code from other RTC or time.h functions but without succes.

The sketch I'm experimenting with is this:

/*
 Udp NTP Client

 Get the time from a Network Time Protocol (NTP) time server
 Demonstrates use of UDP sendPacket and ReceivePacket
 For more on NTP time servers and the messages needed to communicate with them,
 see http://en.wikipedia.org/wiki/Network_Time_Protocol

 created 4 Sep 2010
 by Michael Margolis
 modified 9 Apr 2012
 by Tom Igoe
 modified 28 Dec 2022
 by Giampaolo Mancini

This code is in the public domain.
 */

#include <WiFi.h>
#include <WiFiUdp.h>
#include <mbed_mktime.h>

int status = WL_IDLE_STATUS;
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = "XXX"; // your network SSID (name)
char pass[] = "XXX"; // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0; // your network key index number (needed only for WEP)

unsigned int localPort = 2390; // local port to listen for UDP packets

// IPAddress timeServer(162, 159, 200, 123); // pool.ntp.org NTP server

constexpr auto timeServer { "pool.ntp.org" };

const int NTP_PACKET_SIZE = 48; // NTP timestamp is in the first 48 bytes of the message

byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets

// A UDP instance to let us send and receive packets over UDP
WiFiUDP Udp;

constexpr unsigned long printInterval { 1000 };
unsigned long printNow {};

void setup()
{
    // Open serial communications and wait for port to open:
    Serial.begin(9600);
    while (!Serial) {
        ; // wait for serial port to connect. Needed for native USB port only
    }

    // check for the WiFi module:
    if (WiFi.status() == WL_NO_SHIELD) {
        Serial.println("Communication with WiFi module failed!");
        // don't continue
        while (true)
            ;
    }

    // attempt to connect to WiFi network:
    while (status != WL_CONNECTED) {
        Serial.print("Attempting to connect to SSID: ");
        Serial.println(ssid);
        // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
        status = WiFi.begin(ssid, pass);

        // wait 10 seconds for connection:
        delay(10000);
    }

    Serial.println("Connected to WiFi");
    printWifiStatus();

    setNtpTime();

}

void loop()
{
    if (millis() > printNow) {
        printTime();
        printNow = millis() + printInterval;
    }
}

void printTime() {


  char buffer[32];
  tm t;
  _rtc_localtime(time(NULL), &t, RTC_FULL_LEAP_YEAR_SUPPORT);
  //strftime(buffer, 32, "%Y-%m-%d %k:%M:%S", &t);
  //return String(buffer);  
  //Serial.println(buffer);
  char bufferU[8];
  char bufferM[8];
  char bufferS[8];
  int hour = strftime(bufferU, 8, "%K", &t);
  int min = strftime(bufferM, 8, "%M", &t);
  int sec = strftime(bufferS, 8, "%S", &t);
  Serial.print(hour);
  Serial.print(":");
  Serial.print(min);
  Serial.print(":");
  Serial.println(sec);
  
}

void setNtpTime()
{
    Udp.begin(localPort);
    sendNTPpacket(timeServer);
    delay(1000);
    parseNtpPacket();
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(const char * address)
{
    memset(packetBuffer, 0, NTP_PACKET_SIZE);
    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
    packetBuffer[12] = 49;
    packetBuffer[13] = 0x4E;
    packetBuffer[14] = 49;
    packetBuffer[15] = 52;

    Udp.beginPacket(address, 123); // NTP requests are to port 123
    Udp.write(packetBuffer, NTP_PACKET_SIZE);
    Udp.endPacket();
}

unsigned long parseNtpPacket()
{
    if (!Udp.parsePacket())
        return 0;

    Udp.read(packetBuffer, NTP_PACKET_SIZE);
    const unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    const unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    const unsigned long secsSince1900 = highWord << 16 | lowWord;
    constexpr unsigned long seventyYears = 2208988800UL;
    const unsigned long epoch = secsSince1900 - seventyYears;
    set_time(epoch);

#if defined(VERBOSE)
    Serial.print("Seconds since Jan 1 1900 = ");
    Serial.println(secsSince1900);

    // now convert NTP time into everyday time:
    Serial.print("Unix time = ");
    // print Unix time:
    Serial.println(epoch);

    // print the hour, minute and second:
    Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT)
    Serial.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day)
    Serial.print(':');
    if (((epoch % 3600) / 60) < 10) {
        // In the first 10 minutes of each hour, we'll want a leading '0'
        Serial.print('0');
    }
    Serial.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute)
    Serial.print(':');
    if ((epoch % 60) < 10) {
        // In the first 10 seconds of each minute, we'll want a leading '0'
        Serial.print('0');
    }
    Serial.println(epoch % 60); // print the second
#endif

    return epoch;
}

String getLocaltime()
{
    char buffer[32];
    tm t;
    _rtc_localtime(time(NULL), &t, RTC_FULL_LEAP_YEAR_SUPPORT);
    strftime(buffer, 32, "%Y-%m-%d %k:%M:%S", &t);
    return String(buffer);
}

void printWifiStatus()
{
    // print the SSID of the network you're attached to:
    Serial.print("SSID: ");
    Serial.println(WiFi.SSID());

    // print your board's IP address:
    IPAddress ip = WiFi.localIP();
    Serial.print("IP Address: ");
    Serial.println(ip);

    // print the received signal strength:
    long rssi = WiFi.RSSI();
    Serial.print("signal strength (RSSI):");
    Serial.print(rssi);
    Serial.println(" dBm");
}

The operative bit being this:

void printTime() {


  char buffer[32];
  tm t;
  _rtc_localtime(time(NULL), &t, RTC_FULL_LEAP_YEAR_SUPPORT);

  //strftime(buffer, 32, "%Y-%m-%d %k:%M:%S", &t);
  //return String(buffer);  
  //Serial.println(buffer);

  char bufferU[8];
  char bufferM[8];
  char bufferS[8];
  int hour = strftime(bufferU, 8, "%K", &t);
  int min = strftime(bufferM, 8, "%M", &t);
  int sec = strftime(bufferS, 8, "%S", &t);
  Serial.print(hour);
  Serial.print(":");
  Serial.print(min);
  Serial.print(":");
  Serial.println(sec);
  
}

For reasons beyond my understanding this always prints 0:2:2

The original code spits out a String, and changing what goes into the buffer was easy. Trying to change a variable before it goes into the buffer didn't work, and I was also unable to change the String before it was written.

I thought I would be able to use "t.tm_hour + 1;" but I guess I'm missing something else to make that work.

Any help would be greatly appreciated!

You have several choices, you might find the code, you might hire somebody to write it or you may have to figure it out yourself. My suggestion is to get a copy of the Arduino Cookbook, skim the whole thing then study parts perterent to your project. There also several tutorials on line that will help as other articles. The code is not that complicated once you work with it for a while. Also look at the time library.

you were right, going back and reading the time.h instructions and variables gave me something to work with. I had not added the timelib.h, and I'm not sure if it was crucial but the examples I found included it. I think the crucial part I was missing was time_t t = now();. I had tried the hour(); function but it didn't work without now();

As for the GMT and DST I've decided to add them to the parseNtpPacket() where the line reads epoch = secsSince1900 - seventyYears;
Now it reads epoch = secsSince1900 - seventyYears + gmtOffset_sec + daylightOffset_sec;

The GMTOffset is easy but I'm still looking into making the DST change automatically.

From the timezone library I've gotten:

time_t eastern, utc;
TimeChangeRule usEDT = {"EDT", Second, Sun, Mar, 2, -240};  //UTC - 4 hours
TimeChangeRule usEST = {"EST", First, Sun, Nov, 2, -300};   //UTC - 5 hours
Timezone usEastern(usEDT, usEST);
utc = now();	//current time from the Time Library
eastern = usEastern.toLocal(utc);

Changing it to the numbers I need should not be hard but I'm not sure yet where/how to call this function. I'll look into it tomorrow.

Thanks for the help!

Did you find a solution?

I have not. The project got shelved for now. I was working on the bluetooth side of things and got stuck there as well. Since they commented they might implement full bluetooth on top of the BLE I decided to wait for a while and pick it up after I move house.

Sorry I couldn't help.

solution available here Arduino RTC does not stay in sync despite use of ArduinoCloud.getLocalTime(); - #35 by arneko