⇧
vadnica-logo
Editor My Blog Password Manager Finance My Services About the Project Telegram O meni O meni

Project: Arduino Smart Clock with WiFi & NTP Sync

In this project, we will build a digital clock that combines internet precision with the stability of a physical time module. Upon startup, the clock connects to a WiFi network and retrieves the exact time via the NTP protocol. To ensure the clock does not lose time during a connection outage, it uses a DS3231 RTC module, which maintains the time using a battery backup. The data is displayed on a high-resolution 2.0-inch TFT screen.

Technical Reminder

When using the Arduino Nano ESP32, we must pay attention to two things:

  1. 3.3V Logic: The board operates at 3.3V, so peripheral devices (TFT, RTC) must be compatible with this voltage or properly protected.
  2. Pin Numbering: Always use labels with "D" in the code (e.g., D2, D10) to ensure correct mapping on the board.

Wiring Instructions

Correct wiring is the key to the stable operation of your smart clock. This project uses two different communication protocols: SPI for fast data rendering on the TFT screen and I2C for reading time from the RTC module. Pay special attention to the power pins; the Arduino Nano ESP32 operates on 3.3V logic, so all components are powered from the corresponding output on the board. Follow the table below for the exact pin layout to enable coordinated operation of all components.

TFT Display / Pin Arduino Nano ESP32 Pin
VCC 3.3V
GND GND
SCL D13
SDA D11
CS D10
DC D9
RST D8
RTC DS3231 / Pin Arduino Nano ESP32 Pin
VCC Vin 5V
GND GND
SDA A4
SCL A5

Full Project Code

The program code is the heart of this project and handles three main tasks: establishing a WiFi connection, synchronizing with world time (NTP), and securely storing this data in the local RTC module. The code is designed to be optimized – the screen does not refresh entirely; instead, using the fillRect function, we clear and redraw only the parts (seconds, minutes, days) that have actually changed. This prevents unpleasant image flickering and ensures a smooth real-time display.

/*
 * PROJECT: Arduino Smart Clock with WiFi & NTP Sync
 * AUTHOR: Borut Bukovnik
 * DATE: 2026-05-01
 * DESCRIPTION: Digital clock with time updates via WiFi (NTP), backup with DS3231 RTC module, and TFT display.
 * HARDWARE: Arduino Nano ESP32, DS3231, ST7789 TFT (2.0-inch)
 */

#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <Wire.h>
#include <RTClib.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>

// WiFi settings
const char* ssid = "ssid name";       
const char* password = "wifi password"; 

// NTP settings
WiFiUDP ntpUDP;
// 7200 seconds = GMT+2 (Slovenia summer time)
NTPClient timeClient(ntpUDP, "pool.ntp.org", 7200, 60000);

// TFT settings (SPI)
#define TFT_CS 10
#define TFT_DC 9
#define TFT_RST 8

Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

// DS3231 settings (I2C)
#define I2C_SDA A4
#define I2C_SCL A5

RTC_DS3231 rtc;

// Variables for screen refresh
int previousSecond = -1;
int previousDay = -1;
unsigned long lastNtpSync = 0;
const unsigned long NTP_SYNC_INTERVAL = 600000; // 10 minutes

void setup() {
  Serial.begin(115200);
  Wire.setPins(I2C_SDA, I2C_SCL);

  if (!rtc.begin()) {
    Serial.println("RTC not found!");
    while (1);
  }

  // Display initialization
  tft.init(240, 320); 
  tft.setRotation(1);
  tft.fillScreen(ST77XX_BLACK);

  // WiFi connection
  tft.setCursor(10, 100);
  tft.setTextColor(ST77XX_WHITE);
  tft.setTextSize(2);
  tft.print("Connecting to WiFi...");

  WiFi.begin(ssid, password);
  int timeout = 0;
  while (WiFi.status() != WL_CONNECTED && timeout < 20) {
    delay(500);
    timeout++;
  }

  if (WiFi.status() == WL_CONNECTED) {
    timeClient.begin();
    timeClient.update();
    rtc.adjust(DateTime(timeClient.getEpochTime()));
    lastNtpSync = millis();
    tft.fillScreen(ST77XX_BLACK);
    tft.setTextColor(ST77XX_GREEN);
    tft.setCursor(10, 200);
    tft.print("WiFi: Connected");
  } else {
    tft.fillScreen(ST77XX_BLACK);
    tft.setTextColor(ST77XX_RED);
    tft.setCursor(10, 200);
    tft.print("WiFi: Offline (RTC)");
  }
  delay(1000);
}

void loop() {
  // NTP synchronization
  if (WiFi.status() == WL_CONNECTED && (millis() - lastNtpSync >= NTP_SYNC_INTERVAL)) {
    if(timeClient.update()) {
      rtc.adjust(DateTime(timeClient.getEpochTime()));
      lastNtpSync = millis();
    }
  }

  DateTime now = rtc.now();

  // Date display (only on day change)
  if (now.day() != previousDay) {
    tft.fillRect(0, 20, 320, 30, ST77XX_BLACK);
    tft.setCursor(10, 20);
    tft.setTextColor(ST77XX_YELLOW);
    tft.setTextSize(2);
    
    char dateBuf[20];
    sprintf(dateBuf, "%02d.%02d.%04d", now.day(), now.month(), now.year());
    tft.print(dateBuf);
    previousDay = now.day();
  }

  // Time display (every second)
  if (now.second() != previousSecond) {
    tft.fillRect(0, 80, 320, 60, ST77XX_BLACK);
    tft.setCursor(10, 80);
    tft.setTextColor(ST77XX_CYAN);
    tft.setTextSize(5);

    char timeBuf[10];
    sprintf(timeBuf, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
    tft.print(timeBuf);
    previousSecond = now.second();
  }
  
  delay(200);
}

Why the NTP + RTC combination?

  1. Startup Speed: WiFi connection takes time. Without an RTC module, your clock would show 00:00:00 or remain blank for a few seconds (or minutes if the router acts up) upon every power-up. With an RTC module, the clock "knows" what time it is before it even connects to the internet.
  2. Internet Outages: If your WiFi goes down in the middle of the night or your ISP performs maintenance, your clock without an RTC module will simply freeze or start lagging significantly. The DS3231, however, continues to run independently of the outside world.
  3. Less Server Load: It is not ideal (and sometimes they may even block you) to query an NTP server for the time every second. A smart solution is to sync the time, for example, once an hour or once a day, while the DS3231 handles precision in between.
  4. Offline Accuracy: The DS3231 is a "Temperature Compensated" RTC, meaning it is incredibly accurate (losing only about a minute per year), even if you have no internet for an entire month.

In our code, it works like this:

WiFi and NTP are the managers that check every 10 minutes if the clock happens to be lagging by a millisecond and correct the RTC module. The RTC, however, is the worker that "toils" every second and sends data to the TFT screen.

Required Material