ESP32 üzerinde çalışan, yerel ağımdaki her cihaza doğru zaman sunan ve bunu şık bir şekilde yapan — renkli TFT ekran ve telefonunuzdan yönetebileceğiniz bir web paneli ile tamamlanan bir Stratum 2 NTP zaman sunucusu yaptım.

Neden Kendi NTP Sunucunuzu Yapmalısınız?
Yerel bir NTP sunucusu şu anlama gelir:
- Daha hızlı senkronizasyon — 20-50ms gidiş-dönüş süresi yerine milisaniye altı yanıtlar
- Çevrimdışı çalışır — internetiniz kesilse bile LAN’ınız zamanı tutar
- Tek upstream bağlantı — ESP32 internete senkronize olur; geri kalan her şey onunla konuşur
- Görünürlük — hangi cihazların ne sıklıkla zaman istediğini tam olarak görebilirsiniz
Ve açıkçası? Eğlenceli bir hafta sonu projesi.
Donanım
Yapı sadece iki bileşen kullanır:
| Parça |
|---|
| ESP32 DevKit v1 (WROOM-32) |
| GMT020-02 — 2.0″ SPI TFT (ST7789V, 240×320) |
Yedi kablo bunları birbirine bağlar:
| Ekran Pini | ESP32 GPIO |
|---|---|
| VCC | 3.3V |
| GND | GND |
| CS | GPIO 5 |
| DC | GPIO 4 |
| RST | GPIO 22 |
| SDA (MOSI) | GPIO 23 |
| SCL (CLK) | GPIO 18 |
PCB yok, lehim yok (breadboard kullanıyorsanız), seviye dönüştürücü yok. Tak ve çalıştır.

Ekran; güncel saat, tarih, IP adresi, senkronizasyon durumu ve kaç NTP istemcisinin bağlı olduğunu bir bakışta gösterir.
İlk Açılış: Captive Portal ile WiFi Kurulumu
İlk açılışta ESP32 WiFi bilgilerinizi bilmez. Bunları koda gömmek yerine, ESP32-NTP-Setup adında kendi erişim noktasını açar ve TFT ekranda rastgele bir şifre gösterir (tarayabileceğiniz bir QR kodu ile birlikte).
Telefonunuzdan bağlanın, captive portal otomatik açılır — otel WiFi’si gibi. Ağınızı seçin, şifreyi girin, kaydedin. ESP32 yeniden başlar ve WiFi’nize katılır.

Karşılaştığım bir şey: telefonlar sürekli “captive portal algılama” probeları gönderir (Android /generate_204‘e, iOS /hotspot-detect.html‘e, Windows /connecttest.txt‘e gider). Her şeyi körü körüne kurulum sayfanıza yönlendirirseniz, telefon sonsuz döngüde sayfayı yeniden yükler. Çözüm, bu probe URL’lerinin her biri için handler kaydetmek ve beklenen yanıtları döndürmek, artı WiFi tarama sonuçlarını önbelleğe almak oldu — böylece sayfa her yüklemede 3 saniye bloklamıyor.
Web Paneli
Bağlandıktan sonra ESP32 tam bir web arayüzü sunar — uygulama gerekmez, herhangi bir tarayıcıda IP’yi açın.

Panel şunları gösterir:
- Sunucu durumu — güncel saat, saat dilimi, çalışma süresi, IP, boş heap, açılış sayısı
- WiFi sinyal gücü (RSSI)
- Toplam sunulan NTP istekleri ve benzersiz istemci sayısı
- Dakika başına istek grafiği — son 60 dakikanın canlı canvas grafiği
- NTP upstream yapılandırması — üç upstream havuz sunucunuzu değiştirin
- Saat dilimi seçici — yaygın ön ayarlar veya özel POSIX TZ dizesi girin
Her şey Server-Sent Events (SSE) ile gerçek zamanlı güncellenir — sayfa yenilemesi yok, polling yok.
İstemci Takibi
İstemciler sekmesi, sunucudan zaman isteyen her cihazı gösterir:

Her istemcinin kendi detay sayfası vardır — istemci bazlı istek geçmişi grafiği ve bir etiket alanı ile — böylece 192.168.1.42‘ye bakmak yerine “Masaüstü PC” veya “Raspberry Pi” olarak adlandırabilirsiniz:

Etiketler NVS flash’a kaydedilir, yeniden başlatmalarda korunur. Sunucu aynı anda 32’ye kadar benzersiz istemciyi takip eder.
Günlükler, Ayarlar ve OTA
Günlükler sekmesi son 100 olayı gösterir — açılış mesajları, NTP senkronizasyon sonuçları, istemci bağlantıları:

Ayarlar şunları yapılandırmanızı sağlar:
- HTTP Basic Auth kimlik bilgileri (varsayılan
admin/admin) - Syslog yönlendirme — UDP üzerinden uzak syslog sunucusuna günlük gönderme (RFC 5424)
- Webhook / Telegram uyarıları — WiFi düştüğünde, yeniden bağlandığında veya NTP senkronizasyonu başarısız olduğunda bildirim alın

Ve OTA sayfası yeni firmware’i doğrudan tarayıcıdan yüklemenizi sağlar — ilk flash’tan sonra USB kablosuna gerek yok:

Altyapı
Tüm proje tek bir .ino dosyası — yaklaşık 2.600 satır. İçinde neler var:
NTP Sunucusu (RFC 5905)
Sunucu UDP port 123’te dinler ve NTPv4 isteklerine yanıt verir. ESP32’nin yerleşik SNTP istemcisini kullanarak upstream havuzlara senkronize olur, ardından Stratum 2 kaynağı olarak zaman sunar. Yanıt, mikrosaniye hassasiyetinde uygun referans zaman damgaları, kaynak zaman damgaları ve iletim zaman damgaları içerir.
Kendinden İmzalı Sertifikalarla HTTPS
İlk açılışta ESP32, mbedTLS kullanarak bir EC P-256 anahtar çifti ve kendinden imzalı bir X.509 sertifikası oluşturur — ESP-IDF ile birlikte gelen aynı kripto kütüphanesi. Sertifika ve anahtar NVS’de saklanır, yeniden başlatmalarda korunur. HTTPS, ESP-IDF’nin esp_https_server‘ı kullanılarak port 443’te çalışır.
Captive Portal Algılama
AP modundayken DNS sunucusu her domain’i ESP32’nin IP’sine çözer. Ancak telefonlar ve işletim sistemleri captive portalları algılamak için belirli URL’leri sorgular:
- Android:
/generate_204,/gen_204 - iOS/macOS:
/hotspot-detect.html,/library/test/success.html - Windows:
/connecttest.txt,/ncsi.txt
Her biri, tümünün 302 yönlendirme döngüsüne düşmesi yerine işletim sisteminin portal sayfasını göstermesini tetikleyen uygun bir handler alır.
Yedek AP Modu
WiFi 5 dakikadan fazla bağlantı kesilirse, ESP32 yeniden yapılandırabilmeniz için otomatik olarak AP moduna geri döner. AP şifresi bir kez rastgele oluşturulur ve NVS’de kalıcı olarak saklanır — yeniden başlatmalarda aynı kalır ancak WiFi ayarlarını temizlerseniz sıfırlanır.
NVS Kalıcılığı
Tüm ayarlar ESP32’nin Non-Volatile Storage’ında tek bir namespace (ntp-cfg) altında yaşar: WiFi kimlik bilgileri, NTP sunucuları, saat dilimi, kimlik doğrulama, syslog yapılandırması, uyarı uç noktaları, açılış sayacı, birikmiş çalışma süresi, TLS sertifikaları ve istemci etiketleri.
Bellek Kullanımı
Firmware flash’ın %98’ini kullanır (1.28 MB / 1.31 MB) — çoğunlukla TLS yığını ve binary’ye gömülü tüm HTML/CSS/JS nedeniyle. RAM rahat bir %19‘da (62 KB / 327 KB) oturur, çalışma zamanında bol miktarda alan bırakır.
Flash alanı boşaltmanız gerekirse, en büyük kazanımlar HTTPS desteğini çıkarmak veya HTML şablonlarını küçültmek olacaktır. Ancak %98’de her şey hâlâ sığıyor.
Nasıl Kurulur
1. Firmware’i yükleyin
# Arduino IDE'ye (veya arduino-cli) ESP32 board desteği kurun
# Library Manager'dan Bodmer'in TFT_eSPI'sini kurun
# ~/Arduino/libraries/TFT_eSPI/User_Setup.h dosyasını düzenleyin:
# #define ST7789_DRIVER
# #define TFT_WIDTH 240 / TFT_HEIGHT 320
# #define TFT_CS 15 / TFT_DC 27 / TFT_RST 4
# #define TFT_MOSI 23 / TFT_SCLK 18
arduino-cli compile --fqbn esp32:esp32:esp32 .
arduino-cli upload --fqbn esp32:esp32:esp32 -p /dev/ttyUSB0 .2. WiFi’ye bağlanın
Açın → ESP32-NTP-Setup‘a bağlanın → WiFi bilgilerini girin → kaydedin.
3. Cihazlarınızı yönlendirin
Linux:
# /etc/systemd/timesyncd.conf
[Time]
NTP=192.168.1.44Windows:
Ayarlar → Saat ve Dil → İnternet Saati → Sunucu: 192.168.1.44macOS:
sudo sntp -sS 192.168.1.44Diğer ESP32’ler:
configTime(0, 0, "192.168.1.44");Kaynak Kod
Tam kaynak GitHub’da: ESP32-NTP_Project
Tek dosya firmware, TFT_eSPI dışında harici bağımlılık yok, herhangi bir ESP32 DevKit’te çalışır.