NTPClient.h Update(), can it be done Async ? (ESP8266)

Hi, i creating an LED clock using WS2812b LED's and wanting it to get the time from NTP using an ESP8266.
It all works fine for now, although i will need to do some polishing of the visual output.
The issue i ran into is that while the NTP time is updated, the program does not progress. Possibly due to the clock not being to close to the router, it seems as if the response takes almost 5 seconds to come in, during which the program does not update the time on the LEDs. The seconds marker stops.
Now i don't intend to update very often, once a day should be enough, but i would like to run some kind of animation while it is updating, either something fancy, or just that the seconds keep running.
The full code at the moment

#include <NTPClient.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <TimeLib.h>
#include <time.h>
#include <Timezone.h>
#include <NeoPixelBus.h>

#define PIN 3  // ws2811 data pin
#define BLOCK_PIN 0
#define HR_WIDTH 2
#define MN_WIDTH 3
#define BR_DEC 6
#define BACK_LED 28
#define LED_COUNT (60 + BACK_LED)

const RgbColor HR_COLOR (0, 255, 0);
const RgbColor MN_COLOR (255, 0, 0);
const RgbColor SC_COLOR (0, 0, 255);
const RgbColor BACK_COL (160, 30, 0);

static tm getDateTimeByParams(long time) {
  struct tm *newtime;
  const time_t tim = time;
  newtime = localtime(&tim);
  return *newtime;
}

static String getDateTimeStringByParams(tm *newtime, char* pattern = (char *)"%d/%m/%Y %H:%M:%S") {
  char buffer[30];
  strftime(buffer, 30, pattern, newtime);
  return buffer;
}

static String getEpochStringByParams(long time, char* pattern = (char *)"%d/%m/%Y %H:%M:%S") {
  tm newtime;
  newtime = getDateTimeByParams(time);
  return getDateTimeStringByParams(&newtime, pattern);
}

NeoPixelBus<NeoGrbFeature, NeoEsp8266Dma800KbpsMethod> strip(LED_COUNT, PIN); // the object name is strip.

const char *ssid     = "XXX";
const char *password = "xxxxxxxx";

WiFiUDP ntpUDP;
int GTMOffset = 0;
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", GTMOffset * 60 * 60, 60 * 1000);

// Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120};     // Central European Summer Time
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60};       // Central European Standard Time
Timezone CE(CEST, CET);

void setup() {
  delay(100);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay (500);
  }
  timeClient.begin();
  delay (1000);
  while  (!timeClient.update()) {
    delay(3000);
  }
  unsigned long epoch = timeClient.getEpochTime();
  setTime(epoch);
  delay(200);
  pinMode(1, OUTPUT);
  digitalWrite(1, LOW);
  strip.Begin();
  ClearLeds();
  strip.Show();
  delay(200);
  pinMode(BLOCK_PIN, OUTPUT);
  digitalWrite(BLOCK_PIN, LOW);
  strip.Show();

}

void loop() {
  static uint32_t moment = millis();
  if (millis() - moment > 60000)   { // * 60 * 24) {   // every minute or once a day
    moment = millis();
    timeClient.update();
  }
  DisplayTime(CE.toLocal(now()));
}

void DisplayTime(uint32_t theTime) {
  static uint32_t oldTime = 0;
  static int8_t oldSec = 0;
  static uint32_t lastNewSec = 0;
  tm nowtime;
  uint8_t centerPix, brightness;
  nowtime = getDateTimeByParams(theTime);
  ClearLeds();
  SetBackLeds();
  centerPix = nowtime.tm_min;
  brightness = 255;
  for (uint8_t i = 0; i < MN_WIDTH; i++) {  // fill the Hour
    //uint8_t brightness = ((uint16_t) 255 * (MN_WIDTH - i)) / MN_WIDTH + (i * 2);
    brightness = brightness / (BR_DEC - MN_WIDTH) ;
    uint8_t pix = (59 + centerPix - i) % 60;
    strip.SetPixelColor(pix + BACK_LED, ColorBrightness(MN_COLOR, brightness));
    pix = (59 + centerPix + i) % 60;
    strip.SetPixelColor(pix + BACK_LED, ColorBrightness(MN_COLOR, brightness));
  }

  centerPix = ((nowtime.tm_hour % 12) * 5) + (nowtime.tm_min / 12);
  brightness = 255;
  for (uint8_t i = 0; i < HR_WIDTH; i++) {  // fill the Hour
    brightness = brightness / (BR_DEC - HR_WIDTH);
    //uint8_t brightness = ((uint16_t) 255 * (HR_WIDTH - i)) / HR_WIDTH + (i * 2);
    uint8_t pix = (59 + centerPix - i) % 60;
    strip.SetPixelColor(pix + BACK_LED, ColorBrightness(HR_COLOR, brightness));
    pix = (59 + centerPix + i) % 60;
    strip.SetPixelColor(pix + BACK_LED, ColorBrightness(HR_COLOR, brightness));
  }
  

  if (oldSec != nowtime.tm_sec) {
    oldSec = nowtime.tm_sec;
    lastNewSec = millis();
  }
  uint16_t elapsed = millis() - lastNewSec;
  if (elapsed < 512) {
    if (elapsed > 255) elapsed = 511 - elapsed;
    uint8_t pix = (59 + oldSec) % 60;
    strip.SetPixelColor(pix + BACK_LED, ColorBrightness(SC_COLOR, elapsed));
    //digitalWrite(1, LOW);
  }
  else {
    //digitalWrite(1, HIGH);
  }
  strip.Show();
}

RgbColor ColorBrightness(RgbColor color, uint16_t brightness) {
  RgbColor newColor = 0;

  newColor.R = (color.R * brightness) / 255;
  newColor.G = (color.G * brightness) / 255;
  newColor.B = (color.B * brightness) / 255;
  return newColor;
}

void ClearLeds() {
  for (uint16_t u = 0; u < LED_COUNT; u++)  {
    strip.SetPixelColor(u, 0);
  }
}

void SetBackLeds() {
  for (uint8_t i = 0; i < BACK_LED; i++) {
    strip.SetPixelColor(i, BACK_COL);
  }
}

I started out with a basic sketch i found somewhere, which works, but once i moved the clock to my soldering desk, updating started to take a bit longer. I know the libraries i use are not the only ones, and i am looking for recommendations before i start looking into the libraries to see how it could also be fixed. In this project i would really just like to focus on the cosmetics.

the esp8266 SDK retrieves the time from NTP and updates it automatically in the background.
I recommend to set the time server and zone, because default are Chinese ntp servers.
In setup():

  settimeofday_cb([]() { // set callback to execute after time is retrieved
    time_t now = time(nullptr);
    setTime(now); // update time to TimeLib
  });
  configTime(TIME_ZONE, "pool.ntp.org");

the includes and define are

#include <TimeLib.h>
#include <sntp.h>
#include <coredecls.h> // for settimeofday_cb()
#include <TZ.h>

#define TIME_ZONE TZ_Europe_London

if you want to wait in setup() until time is retrieved for the first time:

  time_t now = time(nullptr);
  while (now < SECS_YR_2000) {
    delay(100);
    now = time(nullptr);
  }

Great thank you ! i knew my code wasn't what it was supposed to be. I'll test it later.
I don't want to update the time to often though, how often does it update ?

every hour

it can be set by definnig a function:

// OPTIONAL: change SNTP update delay
// a weak function is already defined and returns 1 hour
// it can be redefined:
uint32_t sntp_update_delay_MS_rfc_not_less_than_15000 ()
{
    //info_sntp_update_delay_MS_rfc_not_less_than_15000_has_been_called = true;
    return 15000; // 15s
}
1 Like

OK now it works, but i did have a few minor issues.

#include <TZ.h>

Was not in the library manager, and when i googled it and found it on github, it was just a long list of timezone referrals. But when i copied the define, confiTime() threw an error expecting an 'int' for it's argument.
since London is GMT and therefore '0' i just provided that, but then it kept complaining, and i found it wanted also the Daylightsavings offset, which for practical purposes i set to 0 as well. i am using another library for that anyway. Still i'd say there was a parameter missing or this has been altered in the more recent core.

Anyway, full working code :

#include <TimeLib.h>
#include <sntp.h>
#include <coredecls.h> // for settimeofday_cb()
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <time.h>
#include <Timezone.h>
#include <NeoPixelBus.h>

#define TIME_ZONE 0 
#define DLS_OFFSET 0

#define PIN 3  // ws2811 data pin
#define BLOCK_PIN 0
#define HR_WIDTH 2
#define MN_WIDTH 3
#define BR_DEC 6
#define BACK_LED 28
#define LED_COUNT (60 + BACK_LED)

const RgbColor HR_COLOR (0, 255, 0);
const RgbColor MN_COLOR (255, 0, 0);
const RgbColor SC_COLOR (0, 0, 255);
const RgbColor BACK_COL (160, 30, 0);

static tm getDateTimeByParams(long time) {
  struct tm *newtime;
  const time_t tim = time;
  newtime = localtime(&tim);
  return *newtime;
}

uint32_t sntp_update_delay_MS_rfc_not_less_than_15000 (){
    //info_sntp_update_delay_MS_rfc_not_less_than_15000_has_been_called = true;
    return 60000 * 60; // 1hr
}

NeoPixelBus<NeoGrbFeature, NeoEsp8266Dma800KbpsMethod> strip(LED_COUNT, PIN); // the object name is strip.

const char *ssid     = "XXX";
const char *password = "xxxxxxxx";

// Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120};     // Central European Summer Time
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60};       // Central European Standard Time
Timezone CE(CEST, CET);

void setup() {
  delay(100);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay (500);
  }
  settimeofday_cb([]() { // set callback to execute after time is retrieved
    time_t now = time(nullptr);
    setTime(now); // update time to TimeLib
  });
  configTime(TIME_ZONE, DLS_OFFSET, "europe.pool.ntp.org");
  
  time_t now = time(nullptr);
  while (now < SECS_YR_2000) {
    delay(100);
    now = time(nullptr);
  }
  
  pinMode(1, OUTPUT);
  digitalWrite(1, LOW);
  strip.Begin();
  ClearLeds();
  strip.Show();
  delay(200);
  pinMode(BLOCK_PIN, OUTPUT);
  digitalWrite(BLOCK_PIN, LOW);
  strip.Show();

}

void loop() {
  DisplayTime(CE.toLocal(now()));
}

void DisplayTime(uint32_t theTime) {
  static uint32_t oldTime = 0;
  static int8_t oldSec = 0;
  static uint32_t lastNewSec = 0;
  tm nowtime;
  uint8_t centerPix, brightness;
  nowtime = getDateTimeByParams(theTime);
  ClearLeds();
  SetBackLeds();
  centerPix = nowtime.tm_min;
  brightness = 255;
  for (uint8_t i = 0; i < MN_WIDTH; i++) {  // fill the Hour
    //uint8_t brightness = ((uint16_t) 255 * (MN_WIDTH - i)) / MN_WIDTH + (i * 2);
    brightness = brightness / (BR_DEC - MN_WIDTH) ;
    uint8_t pix = (59 + centerPix - i) % 60;
    strip.SetPixelColor(pix + BACK_LED, ColorBrightness(MN_COLOR, brightness));
    pix = (59 + centerPix + i) % 60;
    strip.SetPixelColor(pix + BACK_LED, ColorBrightness(MN_COLOR, brightness));
  }

  centerPix = ((nowtime.tm_hour % 12) * 5) + (nowtime.tm_min / 12);
  brightness = 255;
  for (uint8_t i = 0; i < HR_WIDTH; i++) {  // fill the Hour
    brightness = brightness / (BR_DEC - HR_WIDTH);
    //uint8_t brightness = ((uint16_t) 255 * (HR_WIDTH - i)) / HR_WIDTH + (i * 2);
    uint8_t pix = (59 + centerPix - i) % 60;
    strip.SetPixelColor(pix + BACK_LED, ColorBrightness(HR_COLOR, brightness));
    pix = (59 + centerPix + i) % 60;
    strip.SetPixelColor(pix + BACK_LED, ColorBrightness(HR_COLOR, brightness));
  }
  

  if (oldSec != nowtime.tm_sec) {
    oldSec = nowtime.tm_sec;
    lastNewSec = millis();
  }
  uint16_t elapsed = millis() - lastNewSec;
  if (elapsed < 512) {
    if (elapsed > 255) elapsed = 511 - elapsed;
    uint8_t pix = (59 + oldSec) % 60;
    strip.SetPixelColor(pix + BACK_LED, ColorBrightness(SC_COLOR, elapsed));
    //digitalWrite(1, LOW);
  }
  else {
    //digitalWrite(1, HIGH);
  }
  strip.Show();
}

RgbColor ColorBrightness(RgbColor color, uint16_t brightness) {
  RgbColor newColor = 0;

  newColor.R = (color.R * brightness) / 255;
  newColor.G = (color.G * brightness) / 255;
  newColor.B = (color.B * brightness) / 255;
  return newColor;
}

void ClearLeds() {
  for (uint16_t u = 0; u < LED_COUNT; u++)  {
    strip.SetPixelColor(u, 0);
  }
}

void SetBackLeds() {
  for (uint8_t i = 0; i < BACK_LED; i++) {
    strip.SetPixelColor(i, BACK_COL);
  }
}

TZ.h is in the esp8266 arduino core.
there are multiple configTime versions with different set of parameters. the timezone definition includes timezone and DST information

Well not in the core version i am using, anyway it works now.

it is there since 2.6.0 (Nov. 2019). you should really upgrade at least to 2.7.4

You are probably right, the thing was that when i upgraded to 2.5.1 (and even 2.6 i think) the support for the generic 8285 broke, and those are the boards i use most, and that made me revert to 2.4.2, which actually works without issue except for the https and this i guess. The deprecation of SPIFFS is also a bit of a hassle. Improved cores are great, but at times they break stuff as well. The techies sometimes also seem to have to much energy and just keep writing code as if there is no end to it. You know i am also a mechanic, and the mechanics maxim is 'if it's not broken, don't fix it !'

try the 2.7.4, not 3+

I'll have a go when i have time.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.