NTP Clock with 8 digit Seven Segment display

I want to build an NTP clock to drive an 8 digit (HH MM SS) seven segment display using an Arduino.
I have the Arduino UNO and will be getting the UNO Wifi rev 2 board soon.

Can someone recommend the best approach to my project? Should I use the RTC in the UNO or use an external device such as a DS3231?

I just need to get pointed in the right direction.
Thanks, Bob

the Time library can keep the time between NTP syncs

Do you have a shield which will mount on top of the Uno and drive the displays? Or will you be using a breadboard or soldering on to strip board to build the circuit? If the former, then Uno/Uno WiFi is the right decision. If the latter, I would recommend a Wemos Mini, which can plug into a breadboard or be soldered onto strip board, unlike Uno.

I have built a couple of ntp clocks and built similar functions into other projects. I use only the Wemos, no external RTC. The Wemos syncs time with the NTP server pool at startup and every 24hrs after that to prevent drift.

What type of displays do you want to use and how will you drive them? If the digits are small and have a segment forward voltage of less than 5V, I would recommend a max7219 or ht16k33 driver chips. If the digits are larger with a forward voltage greater than 5V, using a tpic6c595 per digit is easy, but does mean a lot of chips and resistors, so perhaps other driver chips could reduce that.

Don't buy the displays until you have a plan for how to drive them and understand whether common anode or common cathode would be more appropriate. Post links to the displays you like the look of and we can advise.

I agree with Paul about using a Wemos module.
I'd use a Wemos D1 mini and a 4 digit LED module like one of the TM1637 4 digit LED modules.

Total cost would be under $10 USD for the two modules and you can be up and going in an afternoon if you use the built in time libraries that come with the esp8266 platform.
This will not only give you NTP synchronization running in the background (you don't have to do anything your sketch) but also local time conversion including DST offsets.

BTW, the NTP sync is about once per hour.

--- bill

use the built in time libraries that come with the esp8266 platform.
This will not only give you NTP synchronization running in the background (you don't have to do anything your sketch) but also local time conversion including DST offsets.

That sounds simpler than the code I have been using to achieve the same thing. Can you post a link or example code for that please?

my current Arduino project is NTP clock with esp8266 and TM1647 four 7 segment digits display module and LDR for display brightness. it turns WiFi on once a day to let the SDK sync the time

#include <ESP8266WiFi.h>
#include <WiFiManager.h>
#include <TZ.h>
#include <coredecls.h>                  // settimeofday_cb()
#include <TM1637Display.h> // author Avishay Orpaz

#define TIME_ZONE TZ_Europe_Bratislava

const byte DISPLAY_DATA_PIN = D1;
const byte DISPLAY_SCLK_PIN = D2;

TM1637Display display(DISPLAY_SCLK_PIN, DISPLAY_DATA_PIN);
byte displayData[4];

const uint8_t SEG_JA[] = {
  SEG_A | SEG_D | SEG_E | SEG_F,   // [
  SEG_B | SEG_C | SEG_D | SEG_E,   // J
  SEG_A | SEG_B | SEG_C | SEG_E | SEG_F | SEG_G,   // A
  SEG_A | SEG_B | SEG_C | SEG_D    // ]
  };

const uint8_t SEG_CONF[] = {
  SEG_A | SEG_D | SEG_E | SEG_F,   // C
  SEG_C | SEG_D | SEG_E | SEG_G,   // o
  SEG_C | SEG_E | SEG_G,           // n
  SEG_A | SEG_E | SEG_F | SEG_G    // f
  };

void time_is_set_scheduled() {
  WiFi.mode(WIFI_OFF);
}

void setup() {
  Serial.begin(115200);

  display.setBrightness(7, true);
  display.setSegments(SEG_JA);

  if (WiFi.waitForConnectResult() != WL_CONNECTED) {
    display.setSegments(SEG_CONF);
    WiFiManager wm;
    wm.autoConnect("NTPClock");
  }
  WiFi.persistent(false); // to not store mode changes

  display.setSegments(SEG_JA);

  settimeofday_cb(time_is_set_scheduled);
  configTime(TIME_ZONE, "pool.ntp.org");
}

void loop() {

  static unsigned long previousMillis = 0;

  if (millis() - previousMillis > (1000L * 60 * 60 * 24) || !previousMillis) {
    previousMillis = millis();
    WiFi.begin();
  }
  if (WiFi.isConnected() && millis() - previousMillis > 30000) {
    WiFi.mode(WIFI_OFF);
  }

  static uint8_t lastMinute;
  time_t t = time(nullptr);
  struct tm* tm = localtime(&t);
  byte minute = tm->tm_min;
  byte hour = tm->tm_hour;
  if (lastMinute != minute) {
    lastMinute = minute;
    displayData[0] = (hour < 10) ? 0 : display.encodeDigit(hour / 10);
    displayData[1] = display.encodeDigit(hour % 10) | 0x80; // with colon
    displayData[2] = display.encodeDigit(minute / 10);
    displayData[3] = display.encodeDigit(minute % 10);
    display.setSegments(displayData);
  }

  static int lastLDRReading = 1300; // init out of range
  unsigned a = analogRead(A0);
  if (abs(a - lastLDRReading) > 30 && displayData[1] != 0) {
    lastLDRReading = a;
    byte brightness = map(a, 0, 1024, 1, 7);
    display.setBrightness(brightness, true);
    display.setSegments(displayData);
  }

  delay(1000);
}

PaulRB:
That sounds simpler than the code I have been using to achieve the same thing. Can you post a link or example code for that please?

The esp8266 platform comes with an example:

Attached an example sketch I did that I think is more of a real demonstration.
It prints to the console and updates a hd44780 display that uses an i2c backpack.

One thing to keep in mind with all these time libraries is the y2k38 issue.
Most of the time libraries in use right now have this issue.

This is now just a bit over 17 years from now and is going to cause chaos around the world when it happens.
It is MUCH worse than the y2k issue back in 2000.
If you want/need your clock project to work past early 2038, it is an issue.

The current time library in esp8266, SAM, esp32, and pic32 platforms have this issue. The stm32 platform uses 64 bit time_t values so it does not.

The time code in the AVR libC library has a work around that pushes the issue out about 80 years. But they no longer use the same epoch and don't have DST support.

The Arduino Time/Timelib library has a work around of using a unsigned 32 bit value which pushes the issue to 6:28:15 UTC on Sunday, 7 February 2106
This means you can't support any DateTime before 1970 is is typically ok for a simple clock. However, the Timelib library does not support timezones, and using things like Jack's TimeZone library are a bit of a hack and are munging the time_t value which not the way time_t values are supposed to work.
However, messing the time_t value can appear to work for a simple clock.

On various projects I using Wemos D1 minies have different implementations.
On one project (A WordClock) I use the TimeLib library, an RTC, with buttons to set it manually, and I also reach out to a time server that provides the local time based on the IP address.
This is cool because it self sets itself. I get time from the time server every morning at a bit after 2:00 am so that is how DST changes are done.
That project uses sever different time mechanisms to set the time and the RTC, (manual, RTC, WEB interface, and time server) to set the time and RTC.
The reason is I want that clock to be capable of working for many years (beyond 2038), and it needs to keep working, even if the RTC is not working, the battery is dead, and/or if Wifi or the timeserver are no longer working.
It amazing how complex something like a clock can get if you want to ensure that it can keep working with all these failures.

But for other projects I simply use the built in time library functions and NTP which dramatically simplifies the s/w as well as the h/w.

I have one clock that uses a ring of 60 neopixel LEDs that is nothing more than the wemos board and the neopixel ring.

--- bill

NTP-RTC-LCD-MinExample.ino (8.26 KB)

The esp8266 platform comes with an example:

Thanks! Will try that out sometime.

the y2k38 issue

O...M...G... not another one in my lifetime.... hopefully not my working life, but who knows...

I have one clock that uses a ring of 60 neopixel LEDs that is nothing more than the wemos board and the neopixel ring.

Me too, except mine also has a DFPLAYER mini which plays mp3 recordings of mechanical chimes.

@pau;RB: Thanks for the reply. I was going to use the sevseg.h library that appears to work with a multi-digit seven segment display without the need to for the MAX7219. Agree?

I agree it can work. But it will be more difficult, more components and result not as good.

Show us the circuit you plan to use and we can check if it safe for the Arduino and advise on any downsides you may be unaware of.

bperrybap:
The current time library in esp8266, SAM, esp32, and pic32 platforms have this issue. The stm32 platform uses 64 bit time_t values so it does not.

The stm32 time library does indeed use a 64 bit time_t BUT the stm32f103 hardware seconds counter is only 32 bit (see stm RM0008) so the y2k38 problem will still apply.

Note - if using stm cube ide to automatically generate a calendar then be aware that only the seconds are part of the backup domain. The seconds will keep incrementing during a power outage BUT the minutes, hours, days etc will not. They can of course be reconstructed from the seconds but the example code does not do this. I found it better to just use the time library. At my age, I doubt that the y2k38 problem will worry me.

Is it "bad form" for an NTP Clock to poll NTP "constantly" like, to derive your time solely from those updates, no RTC'ing?

runaway_pancake:
Is it "bad form" for an NTP Clock to poll NTP "constantly" like, to derive your time solely from those updates, no RTC'ing?

Yes, I think that might be perceived as a Denial Of Service Attack or something. Are you asking out of curiosity? No-one on this thread is suggesting doing that.

"Are you asking out of curiosity?"

Basically, yes.
I have this thing running, I don't know how many NTPservers are out there, and is my contraption a drag on the system or is it in/consequential.

runaway_pancake:
"Are you asking out of curiosity?"

Basically, yes.
I have this thing running, I don't know how many NTPservers are out there, and is my contraption a drag on the system or is it in/consequential.

There is no need to implement it like that.
You can easily cache a time_t value when you do the NTP request and use millis() to calculate an offset from the last time you did a NTP fetch.

The time should not drift much during the interval if you use something like 15 minutes.

--- bill

skyvan:
The stm32 time library does indeed use a 64 bit time_t BUT the stm32f103 hardware seconds counter is only 32 bit (see stm RM0008) so the y2k38 problem will still apply.

Not necessarily. It depends on if they use the hardware seconds counter for the time tracking and how they use it.
And even if they did, it wouldn't necessarily mean that there was any sort of rollover issue for the s/w 64 bit time_ t tracking.
I'm not familiar with the STM h/w or the time code.
(I'm off looking for their code right now)

The time library code that most platforms use is from the GNU project and usually runs off of a tick interface function that is periodically called. So my guess would be that it is called at some interval and is not affected by any internal RTC h/w inside the micro since the time code maintains its own s/w time counters.

--- bill.

skyvan:
The stm32 time library does indeed use a 64 bit time_t BUT the stm32f103 hardware seconds counter is only 32 bit (see stm RM0008) so the y2k38 problem will still apply.

Note - if using stm cube ide to automatically generate a calendar then be aware that only the seconds are part of the backup domain. The seconds will keep incrementing during a power outage BUT the minutes, hours, days etc will not. They can of course be reconstructed from the seconds but the example code does not do this. I found it better to just use the time library. At my age, I doubt that the y2k38 problem will worry me.

IIRC, I looked at this. The real problem is that the Arduino core utilizes the HAL for time functions. The HAL is written to support both YYMMDDHHMMSS and binary RTC formats (found in different STM32 types). I can't recall the exact reason, but there is some problem in that aspect of the implementation that causes the seconds implementation to fail. When I looked it over, I decided that the core couldn't be fixed for that, it would take a fix to the HAL. At that point, I gave up. It does work perfectly well on the STM32F4.