← All projects
Displays Beginner

Accurate OLED Clock Synced over the Internet (NTP)

The ESP32 fetches accurate time from NTP servers, handles daylight saving automatically and shows it on an SSD1306 128×64 display.

Difficulty
Beginner
Time
45 min
Category
Displays

Why

The ESP32 has no battery-backed real-time clock, but it does have Wi-Fi and a built-in SNTP client. Set the time zone once and the board keeps itself in sync and handles daylight saving on its own.

Parts

  • ESP32 DevKit
  • SSD1306 0.96" 128×64 I²C OLED display (4 pins)
  • 4 jumper wires

Wiring

OLED ESP32
VCC 3V3
GND GND
SDA GPIO 21
SCL GPIO 22

Libraries

  • Adafruit SSD1306
  • Adafruit GFX Library

Code

#include <WiFi.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <time.h>

const char* WIFI_SSID = "YOUR_SSID";
const char* WIFI_PASS = "YOUR_PASSWORD";

// POSIX time zone string: Kyiv, with automatic daylight saving
const char* TZ_INFO = "EET-2EEST,M3.5.0/3,M10.5.0/4";

Adafruit_SSD1306 display(128, 64, &Wire, -1);

void showMessage(const char* text) {
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print(text);
  display.display();
}

void setup() {
  Serial.begin(115200);
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("SSD1306 not found");
    while (true) delay(1000);
  }
  display.setTextColor(SSD1306_WHITE);
  showMessage("Connecting WiFi...");

  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(300);

  showMessage("Syncing time...");
  configTzTime(TZ_INFO, "pool.ntp.org", "time.google.com");
}

void loop() {
  struct tm now;
  if (!getLocalTime(&now)) {
    delay(500);
    return;
  }

  char timeStr[9], dateStr[11];
  strftime(timeStr, sizeof timeStr, "%H:%M:%S", &now);
  strftime(dateStr, sizeof dateStr, "%d.%m.%Y", &now);

  display.clearDisplay();
  display.setTextSize(2);          // 12×16 px per char → 96 px wide
  display.setCursor(16, 16);
  display.print(timeStr);
  display.setTextSize(1);
  display.setCursor(34, 44);
  display.print(dateStr);
  display.display();

  delay(200);
}

How it works

  • configTzTime() starts the ESP-IDF SNTP client and sets the time zone. Re-sync then happens in the background roughly every hour.
  • getLocalTime() returns local time with daylight saving applied.
  • EET-2EEST,M3.5.0/3,M10.5.0/4 means: base zone UTC+2, summer time from the last Sunday of March 03:00 to the last Sunday of October 04:00.

Other time zones

City TZ string
Warsaw / Berlin CET-1CEST,M3.5.0,M10.5.0/3
London GMT0BST,M3.5.0/1,M10.5.0
New York EST5EDT,M3.2.0,M11.1.0

Blank display?

  • Some modules use address 0x3D instead of 0x3C.
  • Run the Wire → I2C Scanner example to find the address.

Improvements

  • Turn Wi-Fi off after syncing (WiFi.disconnect(true)) — the clock keeps running and the board runs cooler.
  • Add the DHT22 from the "Mini Weather Station" project and show the temperature on the bottom line.
  • Bigger digits: display.setFont(&FreeSansBold18pt7b) from Adafruit GFX.