How to: Get local time on ESP8266 boards

I have learnt tons from the folks on this forum, so I want to give something back.
And since I am new to Arduino and programming, I welcome advice on improvement.

I have explored several ways to get the date and local time on a Wemos D Mini board / ESP8266 board.
Most involve lots of conversions from utc to something humans understand to local timezone to DST and whatnot. Finally settled on using timezonedb.com as source and found it to work well.

Here is the sketch with lots of comments.

/*
  Get date and local time with ESP8266-based boards over WiFi from timezonedb.com
  Tested on Wemos D1 Mini; Arduino IDE V1.8.2 (Mac)

  Getting your board set up:
  If you need to install a CH340G driver, get them here: https://wiki.wemos.cc/downloads.
  To add ESP8266-based boards to the Arduino IDE, paste http://arduino.esp8266.com/stable/package_esp8266com_index.json into Arduino: Preferences: Add'l. Boards Mngr.
  Install under Tools: Board: Boards Manager: select "ESP8266 by ESP8266 community".
  Select your ESP8266-based board under Tools: Board.
  Include libraries under Sketch: Include library.
  Make sure your serial monitor is set to 115200.

  Set up free account @ timezonedb.com to get your API key.
  Insert your API key into host URL below.
  In the URL, leave format=xml, fields=formatted; adjust zone to your location.
  Check https://timezonedb.com/references/get-time-zone for zone (and other) options.

  Process is broken down into three separate functions:
  - connect to WiFi
  - connect to timezonedb
  - parse data, convert to int and store it in y, mo, d, h, mi, s.

  Setup goes through three functions once; loop refreshes every 30 seconds.

  Bits and pieces of this sketch were collected from all over the internet. 
  Thank you to all the folks who pointed me towards the solution.
  Aggertroll - Feb 8, 2018
*/

#include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h>

const char* ssid          = "*********";      // The SSID (name) of your Wi-Fi network
const char* password  = "*********";      // The password of the Wi-Fi network
const char* host      = "http://api.timezonedb.com/v2/get-time-zone?key=******&format=xml&fields=formatted&by=zone&zone=Europe/Berlin";

String payload;               // Variables to accept data
String nowday;
String nowmonth;
String nowyear;
String nowhour;
String nowmin;
String nowsec;

unsigned long prevMillis = millis();
unsigned long interval = 30000;                 // Refresh in loop every 30 seconds


void setup() {
  Serial.begin(115200);                     // Start serial communication
  delay(10);
  Serial.println('\n');

  wifi();
  tzdb();
  parse_response();
}

void wifi() {
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();                                       // Clear any existing connection
  WiFi.begin(ssid, password);                         // Access WiFi
   
  Serial.print("Connecting to ");
  Serial.print(ssid);
  Serial.print(" ...");

  while (WiFi.status() != WL_CONNECTED) {   // Wait for WiFi to connect
    delay(1000);
    Serial.print(".");
  }

  Serial.println('\n');
  Serial.println("WiFi connection established");
  Serial.print("Device's IP address is ");
  Serial.println(WiFi.localIP());                        // Show device's IP address
}

void tzdb() {
  int httpCode = 0;                                        // Variable to hold received data
  HTTPClient http;                                          // Declare an object of class HTTPClient
  
  Serial.println("Connecting to TimezoneDB...");

  http.begin(host);                                        // Connect to site
  httpCode = http.GET();                               // Check if data is coming in

  while (httpCode == 0) {                             // if no data is in
    delay(1000);                                           // wait a sec
    http.begin(host);                                     // and try again
    httpCode = http.GET();
  }

  payload = http.getString();                        // Save response as string
  Serial.println(payload);                              // Show response
 
  //Sample response:
  //<?xml version="1.0" encoding="UTF-8"?>
  //<result><status>OK</status><message/><formatted>2018-02-08 14:24:16</formatted></result>

  http.end();                                                // Close connection to timezonedb
  WiFi.mode(WIFI_OFF);                               // Close connection to WiFi
}

void parse_response() {
  int colon = payload.indexOf(':');                 // Set the first colon in time as reference point
  int d;                                                        // Variables to hold data as integers
  int mo;
  int y;
  int h;
  int mi;
  int s;

  nowday = payload.substring(colon - 5, colon - 3);     // Get data as substring
  d = nowday.toInt();                                                // and convert to int
  nowmonth = payload.substring(colon - 8, colon - 6);
  mo = nowmonth.toInt();
  nowyear = payload.substring(colon - 13, colon - 9);
  y = nowyear.toInt();

  nowhour = payload.substring(colon - 2, colon);
  h = nowhour.toInt();
  nowmin = payload.substring(colon + 1, colon + 3);
  mi = nowmin.toInt();
  nowsec = payload.substring(colon + 4, colon + 6);
  s = nowsec.toInt();

  // Print integers without leading zeros - use in calculations

  Serial.print("Today's date is:   ");
  Serial.print(y);
  Serial.print("-");
  Serial.print(mo);
  Serial.print("-");
  Serial.println(d);

  Serial.print("Current local time is:   ");
  Serial.print(h);
  Serial.print(":");
  Serial.print(mi);
  Serial.print(":");
  Serial.println(s);
  Serial.println();

  /*Print characters with leading zeros - use for lcd etc.

    Serial.print("Today's date is:   ");
    Serial.print(nowyear);
    Serial.print("-");
    Serial.print(nowmonth);
    Serial.print("-");
    Serial.println(nowday);

    Serial.print("Current local time is:   ");
    Serial.print(nowhour);
    Serial.print(":");
    Serial.print(nowmin);
    Serial.print(":");
    Serial.println(nowsec);
    Serial.println();
  */
}

void loop() {

  if (millis() - prevMillis > interval) {                              // refresh
    wifi();
    tzdb();
    parse_response();
    prevMillis = millis();
  }
}

Maybe this is helpful to someone - and let me know how it can be improved.

I find it easier to just hand code my timezone in the sketch - it helps that I don't have to deal with DST.

Aren't there any libraries out there with timezone info?

Yes, there is timezone.h, but when I tried it it did not change from or to DST - I did not make much of an effort to find out why not tho.

I built an LED clock driven by a nano and found the RTC1307 drifts over time. So I will switch to the Wemos and let it adjust time periodically. Maybe even skip the RTC, need to see how much the Wemos drifts by itself.

If you can update NTP ever hour or so you don't need an RTC. I don't know the accuracy of the internal clock of the ESP8266 (it's not using an external crystal so it'll be far from perfect), but of such a time span it's good enough.

By the way, the DS1307 has an option to correct the drift by adjusting the internal loading of the crystal. This way you can get a much better precision out of them. Set it correct, let it run for a few days, check the drift, calculate and set the correction, and you should be able to get to 0.1-0.2 seconds a day.

NTP is of course still more accurate and has no drift.

1 Like

I read about the option to adjust the RTC1307 crystal and I am going to try it out. And I will let the esp8266 run time for a while to see how it drifts.

Hi,
Consider a scenario where in there is intermittent internet connection.
How to keep the time in such a situation

pawanjhamnnit:
Hi,
Consider a scenario where in there is intermittent internet connection.
How to keep the time in such a situation

Update when there's a connection available.
Use the internal clock to keep track of time in between.
The only problem you have is when you're resetting the device while there's no connection.