Set up current time for Arduino

When you are logging or publishing data (e.g. to an MQTT), typically a timestamp is added. I have build a small distance sensor based on an Arduino and a HC-SR04 ultrasound sensor.

I am using this together with an D1 Mini Arduino including a WiFi module.

Every second I am publishing the data to an MQTT server including a timestamp. Being a Java developer I was of course looking how to get the time in milliseconds so that I have an easy to use and universal number to compare and convert.

Since Arduino is programmed in C it comes with a now() method you can use. Unfortunately, the now() method only gives you the milliseconds since the Arduino got started. Which can help you, but in my case where I needed the actual timestamp it did not.

After doing some research I came across the TimeLib library. The TimeLib library gives you very convenient functions, to handle time related topics. Still the problem with the now() persisted. To change this, you have to provide a time sync provider, which syncs your Arduino with the actual time.

If you Google around a little, some recomend using RTC. This has not solved the issue for me though. (After writing this article I found this example which might solve it. I have not yet checked it yet).

If you look at the specs of the TimeLib, there are functions to set the actual time. But now the problem is: How do you find out the current time without knowing when it actually gets started?

To solve this issue I was looking for a RESTful service which gives me the current time and I found https://showcase.api.linx.twenty57.net/UnixTime/tounix?date=now. This URL gives you the current time in millisenconds.

So let's get started programming.

The source code

At first you need to add the libraries for TimeLib and the WiFi. In addition you will need the ArduinoHttpClient to connect to the RESTful WebService:

#include <ESP8266WiFi.h>
#include <TimeLib.h>
#include <ArduinoHttpClient.h>

As a next step, the WiFi connection needs to be set up:

//Set WiFi credentials
#define WIFI_SSID "xxx"
#define WIFI_PASS "xxx"

//Build WiFi and Http Client
WiFiClient espClient;
HttpClient httpClient(espClient, "showcase.api.linx.twenty57.net");

// Number of milliseconds to wait without receiving any data before we give up
const int kNetworkTimeout = 30*1000;
// Number of milliseconds to wait if no data is available before trying again
const int kNetworkDelay = 1000;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println();
  setup_wifi();
}

void setup_wifi(){  
  // Begin WiFi
  WiFi.begin(WIFI_SSID, WIFI_PASS);

  // Connecting to WiFi...
  Serial.print("Connecting to ");
  Serial.print(WIFI_SSID);
  // Loop continuously while WiFi is not connected
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(100);
    Serial.print(".");
  }

  // Connected to WiFi
  Serial.println();
  Serial.print("Connected! IP address: ");
  Serial.println(WiFi.localIP());
}

You can deploy your code and in your console (ctrl + shift + m) you should see the connection to be stablished.

As a next step, the sync provider needs to be set. This is done by calling the setSyncProvider method in your setup method:

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println();
  setup_wifi();
  setSyncProvider(syncProvider);
}

Of course you now need to implement the syncProvider method, which looks as follows:

long syncProvider()
{
  int err =0;
  String result = "";
  
  err = httpClient.get("/UnixTime/tounix?date=now");
  if (err == 0)
  {
    err = httpClient.responseStatusCode();
    if (err >= 0)
    {
      Serial.print("Got status code: ");
      Serial.println(err);

      int bodyLen = httpClient.contentLength();
      // Now we've got to the body, so we can print it out
      unsigned long timeoutStart = millis();
      char c;
      // Whilst we haven't timed out & haven't reached the end of the body
      while ( (httpClient.connected() || httpClient.available()) &&
             (!httpClient.endOfBodyReached()) &&
             ((millis() - timeoutStart) < kNetworkTimeout) )
      {
          if (httpClient.available())
          {
              c = httpClient.read();
              result.concat(c);
              // We read something, reset the timeout counter
              timeoutStart = millis();
          }
          else
          {
              // We haven't got any data, so let's pause to allow some to
              // arrive
              delay(kNetworkDelay);
          }
      }
    }
  }

  return result.toInt();
}

And that's it. When you now call now() in your code, you will be getting the actual timestamp in milliseconds:

void loop() {
    if (!client.connected()) {
      while (!client.connected()) {
          client.connect("ESP8266Client");
          delay(100);
      }
      Serial.println("ESP8266Client connected");
  }
  client.loop();
    
  Serial.println(now());
  delay(1000);
}

The total source code will then look like this:

#include <ESP8266WiFi.h>
#include <TimeLib.h>
#include <ArduinoHttpClient.h>

//Set WiFi credentials
#define WIFI_SSID "xxx"
#define WIFI_PASS "xxx"

//Build WiFi and Http Client
WiFiClient espClient;
HttpClient httpClient(espClient, "showcase.api.linx.twenty57.net");

// Number of milliseconds to wait without receiving any data before we give up
const int kNetworkTimeout = 30*1000;
// Number of milliseconds to wait if no data is available before trying again
const int kNetworkDelay = 1000;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println();
  setup_wifi();
  setSyncProvider(syncProvider);
}

void setup_wifi(){  
  // Begin WiFi
  WiFi.begin(WIFI_SSID, WIFI_PASS);

  // Connecting to WiFi...
  Serial.print("Connecting to ");
  Serial.print(WIFI_SSID);
  // Loop continuously while WiFi is not connected
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(100);
    Serial.print(".");
  }

  // Connected to WiFi
  Serial.println();
  Serial.print("Connected! IP address: ");
  Serial.println(WiFi.localIP());
}

long syncProvider()
{
  int err =0;
  String result = "";
  
  err = httpClient.get("/UnixTime/tounix?date=now");
  if (err == 0)
  {
    err = httpClient.responseStatusCode();
    if (err >= 0)
    {
      Serial.print("Got status code: ");
      Serial.println(err);

      int bodyLen = httpClient.contentLength();
      // Now we've got to the body, so we can print it out
      unsigned long timeoutStart = millis();
      char c;
      // Whilst we haven't timed out & haven't reached the end of the body
      while ( (httpClient.connected() || httpClient.available()) &&
             (!httpClient.endOfBodyReached()) &&
             ((millis() - timeoutStart) < kNetworkTimeout) )
      {
          if (httpClient.available())
          {
              c = httpClient.read();
              result.concat(c);
              // We read something, reset the timeout counter
              timeoutStart = millis();
          }
          else
          {
              // We haven't got any data, so let's pause to allow some to arrive
              delay(kNetworkDelay);
          }
      }
    }
  }

  return result.toInt();
}

void loop() {
    if (!client.connected()) {
      while (!client.connected()) {
          client.connect("ESP8266Client");
          delay(100);
      }
      Serial.println("ESP8266Client connected");
  }
  client.loop();
    
  Serial.println(now());
  delay(1000);
}