Notifications
Clear all

Setting the time on an RTC with an ESP8266 and an NTP server  

  RSS

Pugwash
(@pugwash)
Prominent Member
Joined: 1 year ago
Posts: 971
2020-04-08 12:02 pm  

As we still haven't got rid of this antiquated requirement of adjusting our clocks twice
every year, and as I have a few real-time clocks attached to various mini-projects that
have to be reset every time this happens. I decided it was time to automate this process
a little, using an ESP8266 Node-MCU12-E and I2C.

The wiring is simple:

ESP8266 >>> DS3231
D2 >>> SDA
D1 >>> SCL
G >>> GND
3 >>> VCC

The following code reads the contents of the first 7 DS3231 registers. Then it gets
the current GMT/UTC time from a Network Time Server and performs a local time conversion.
Then it writes the new time to the DS3231 registers and rereads the registers back
to the serial monitor to confirm that the DS3231 has been updated.

/* 
 *  KEYWORDS: DS3231, ESP8266, NodeMCU-12E, NTP
 */

#include <Wire.h>
#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>

#define bcd2dec(bcd_in) (bcd_in >> 4) * 10 + (bcd_in & 0x0f)
#define dec2bcd(dec_in) ((dec_in / 10) << 4) + (dec_in % 10)

// Replace with your network credentials
const char* ssid = "your_ssid";
const char* password = "your_password";

// Define NTP Client to get time
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org");

byte mByte[0x07]; // holds the array from the DS3231 register
byte tByte[0x07]; // holds the array from the NTP server

// change the values here to suit your timezone

const long gmtOffset = 3600; // 3600 seconds is +1 hours
const long summerTimeOffset = 3600; // change to 0 in winter

//Week Days
String weekDay[7]={"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

//Month names
String month[12]={"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};

void setup(){
  
  Serial.begin(115200); 
  
  while(!Serial){} // Wait for serial connection

  Wire.begin();
  
  // set up wireless internet connection

  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\n");

// Initialize a NTPClient to get the time
  timeClient.begin();
  // Set offset time in seconds to adjust for your timezone, for example:
  // GMT +1 = 3600
  // GMT +8 = 28800
  // GMT -1 = -3600
  // add another 3600 for Summertime if appropriate
  // GMT 0 = 0
  // Example CET = GMT/UTC+1 and CEST = GMT/UTC+2 i.e. 2 x 3600 = 7200
  timeClient.setTimeOffset(gmtOffset + summerTimeOffset); //Central European Summer Time
  
  //get current time from the RTC registers and store in the mByte[] array
  Serial.println(F("Old DS3231 register content........\n"));  
  getRTCdatetime();

  // get the datetime from the NTP server
  
  timeClient.update();

  unsigned long epochTime = timeClient.getEpochTime();  
  tByte[0] = (int)timeClient.getSeconds();  
  tByte[1] = (int)timeClient.getMinutes();  
  tByte[2] = (int)timeClient.getHours();  
  tByte[3] = (int)timeClient.getDay();
  
  // create a struct to hold date, month and year values  
  struct tm *ptm = gmtime ((time_t *)&epochTime);
  tByte[4] = (int)ptm->tm_mday;  
  tByte[5] = (int)ptm->tm_mon+1;  
  tByte[6] = (int)ptm->tm_year-100;

  Serial.print(F("NTP Webtime..........\n\n"));

  Serial.print("Seconds: "); Serial.println(tByte[0]);
  Serial.print("Minutes: "); Serial.println(tByte[1]);
  Serial.print("Hours:   "); Serial.println(tByte[2]);
  Serial.print("DoW:     "); Serial.println(weekDay[tByte[3]]);
  Serial.print("Day:     "); Serial.println(tByte[4]);
  Serial.print("Month:   "); Serial.println(month[tByte[5]-1]);
  Serial.print("Year:    "); Serial.println(tByte[6]);
  Serial.print("\n");

  /* if the time stored in the DS3231 register does not match
   *  the time retrieved from the NTP server, update the DS3231
   *  register to the current time.
   */
  
  if(mByte != tByte){
    Wire.beginTransmission (0x68);
    // Set device to start read reg 0
    Wire.write (0x00);
      for (int idx = 0; idx < 7; idx++){
        Wire.write (dec2bcd(tByte[idx]));
      }   
    Wire.endTransmission ();
    }
    
  Serial.println(F("New DS3231 register content........\n"));  
  getRTCdatetime();
  
}

void loop() {}

void getRTCdatetime(){
  
  Wire.beginTransmission (0x68);
  // Set device to start read reg 0
  Wire.write (0x00); 
  Wire.endTransmission ();
  
  // request 7 bytes from the DS3231 and release the I2C bus
  Wire.requestFrom(0x68, 0x07, true);
  
  int idx = 0;

  // read the first seven bytes from the DS3231 module into the array
  while(Wire.available()) {
    
    byte input = Wire.read(); // read each byte from the register
    mByte[idx] = input;    // store each single byte in the array
    idx++;
  }

  // display the current values to the serial monitor
  
  Serial.print(F("Register[0] Seconds: ")); Serial.println(bcd2dec(mByte[0]));
  Serial.print(F("Register[1] Minutes: ")); Serial.println(bcd2dec(mByte[1]));
  Serial.print(F("Register[2] Hours:   ")); Serial.println(bcd2dec(mByte[2]));
  Serial.print(F("Register[3] DoW:     ")); Serial.println(weekDay[bcd2dec(mByte[3])]);
  Serial.print(F("Register[4] Day:     ")); Serial.println(bcd2dec(mByte[4]));
  Serial.print(F("Register[5] Month:   ")); Serial.println(month[bcd2dec(mByte[5]-1)]);
  Serial.print(F("Register[6] Year:    ")); Serial.println(bcd2dec(mByte[6]));
  Serial.println("\n");


}

I am pretty sure that porting this code to an ESP32, would be reasonably straightforward.


Quote
Topic Tags
codecage
(@codecage)
Member Admin
Joined: 1 year ago
Posts: 686
2020-04-08 12:18 pm  

@pugwash

THANKS!  Very interesting indeed.

SteveG


ReplyQuote
byron
(@byron)
Reputable Member
Joined: 1 year ago
Posts: 307
2020-04-08 4:51 pm  

@pugwash

Thats a very good idea indeed.  Duly noted and copied to my snippets files 😀.  Thanks for sharing.


ReplyQuote