ZenoModiff
Published © Apache-2.0

No RTC Needed – ESP32 NTP Clock

This ESP32 clock grabs accurate time from the internet and displays it on a crisp OLED screen.

IntermediateFull instructions provided1 hour3
No RTC Needed – ESP32 NTP Clock

Things used in this project

Hardware components

ESP32
Espressif ESP32
×1
0.96" OLED 64x128 Display Module
ElectroPeak 0.96" OLED 64x128 Display Module
×1
Breadboard (generic)
Breadboard (generic)
×1
Pushbutton switch 12mm
SparkFun Pushbutton switch 12mm
×1
Jumper wires (generic)
Jumper wires (generic)
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Hot glue gun (generic)
Hot glue gun (generic)

Story

Read more

Schematics

Ntp Clock

Code

Ntp Clock

Arduino
#include <WiFi.h>
#include "time.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// --- Configuration ---
const char* ssid     = "ENTER_NEW_WIFI_NAME_HERE"; 
const char* password = "ENTER_NEW_PASSWORD_HERE";

const char* ntpServer = "pool.ntp.org";
const long  gmtOffset_sec = 19800; // IST: UTC + 5:30
const int   daylightOffset_sec = 0;

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// --- Pins ---
const int START_STOP_BUTTON = 18; 
const int MODE_RESET_BUTTON = 5;

// --- State Variables ---
enum Mode { CLOCK, STOPWATCH, QUOTES };
Mode currentMode = CLOCK;

unsigned long startTime = 0;
unsigned long elapsedTime = 0;
bool running = false;

// You can add more quotes here as needed
const char* quotes[] = {
  "Stay hungry. Stay foolish.",
  "Code is poetry.",
  "Keep moving forward.",
  "Innovation is key.",
  "Build, Hack, Repeat."
};
int totalQuotes = 5;
int quoteIndex = 0;

void setup() {
  Serial.begin(115200);
  
  pinMode(START_STOP_BUTTON, INPUT_PULLUP);
  pinMode(MODE_RESET_BUTTON, INPUT_PULLUP);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(10, 20);
  display.println("Connecting WiFi...");
  display.display();

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("\nWiFi Connected!");
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
}

void loop() {
  checkButtons();
  
  display.clearDisplay();
  
  switch (currentMode) {
    case CLOCK:
      showClock();
      break;
    case STOPWATCH:
      showStopwatch();
      break;
    case QUOTES:
      showQuotes();
      break;
  }
  
  display.display();
  delay(50); // Smoother refresh for stopwatch
}

void checkButtons() {
  static bool lastModeState = HIGH;
  static bool lastStartState = HIGH;
  
  bool modeState = digitalRead(MODE_RESET_BUTTON);
  bool startState = digitalRead(START_STOP_BUTTON);

  // Mode Switch / Reset Logic (D5)
  if (lastModeState == HIGH && modeState == LOW) {
    if (currentMode == STOPWATCH && !running) {
      elapsedTime = 0; // Reset only when paused
    } else {
      currentMode = (Mode)((currentMode + 1) % 3);
      if(currentMode == QUOTES) quoteIndex = random(0, totalQuotes);
    }
    delay(200); // Debounce
  }

  // Start / Stop Logic (D18)
  if (lastStartState == HIGH && startState == LOW) {
    if (currentMode == STOPWATCH) {
      running = !running;
      if (running) startTime = millis() - elapsedTime;
    }
    delay(200); // Debounce
  }

  lastModeState = modeState;
  lastStartState = startState;
}

void showClock() {
  struct tm timeinfo;
  if(!getLocalTime(&timeinfo)){
    display.setCursor(0, 25);
    display.println("Syncing Time...");
    return;
  }
  
  display.setTextSize(1);
  display.setCursor(35, 0);
  display.print("NTP CLOCK");
  
  display.setTextSize(2);
  display.setCursor(15, 25);
  display.printf("%02d:%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
  
  display.setTextSize(1);
  display.setCursor(25, 55);
  display.print("Region: IST");
}

void showStopwatch() {
  if (running) elapsedTime = millis() - startTime;
  
  unsigned long totalSeconds = elapsedTime / 1000;
  unsigned long minutes = (totalSeconds / 60) % 60;
  unsigned long seconds = totalSeconds % 60;
  unsigned long tenths = (elapsedTime / 100) % 10;

  display.setTextSize(1);
  display.setCursor(35, 0);
  display.print("STOPWATCH");
  
  display.setTextSize(2);
  display.setCursor(20, 25);
  display.printf("%02lu:%02lu.%lu", minutes, seconds, tenths);
  
  display.setTextSize(1);
  display.setCursor(0, 55);
  display.print(running ? "Running..." : "Paused (D5 to Reset)");
}

void showQuotes() {
  display.setTextSize(1);
  display.setCursor(35, 0);
  display.print("QUOTES");
  
  display.setTextSize(1);
  display.setCursor(0, 25);
  display.println(quotes[quoteIndex]);
  
  display.setCursor(0, 55);
  display.print("D5 for Next Quote");
}

Credits

ZenoModiff
19 projects • 10 followers

Comments