Waktu NTP ESP32: jam internet dengan zona waktu, OLED, dan cadangan RTC
Cara mendapatkan waktu NTP di ESP32: configTzTime dan zona waktu, getLocalTime, strftime, jam OLED, cadangan DS3231, tugas harian terjadwal, dan ntptime Pico W.
Mengambil waktu dengan ESP32 NTP berarti menghubungkan board ke server waktu di internet dan memperoleh tanggal serta waktu dengan akurasi beberapa puluh milidetik, tanpa modul jam apa pun. Setelah ESP32 terhubung ke Wi-Fi, satu panggilan configTzTime() mengambil waktu, menerapkan zona waktumu, lalu board terus menghitung waktu sendiri. Jam meja, penyiraman terjadwal, cap waktu untuk pencatatan data, alarm pagi, atau lampu yang menyala pada waktu tertentu tidak lagi memerlukan modul RTC terpisah.
Dalam panduan ini kita akan membahas langkah demi langkah cara kerja NTP, pengaturan zona waktu yang benar, penulisan tanggal dalam format apa pun dengan getLocalTime dan strftime, penampilan waktu pada layar OLED, pemeliharaan waktu dengan modul DS3231 ketika Wi-Fi terputus atau daya hilang, serta menjalankan tugas pada waktu tertentu setiap hari. Pada bagian terakhir kita akan melakukan hal yang sama di Raspberry Pi Pico W dengan modul ntptime MicroPython.

Apa itu NTP dan bagaimana ESP32 mendapatkan waktu?
NTP (Network Time Protocol) adalah protokol yang digunakan sejak 1980-an untuk menyinkronkan jam komputer dan perangkat melalui internet. Server disusun dalam lapisan yang disebut strata: server stratum 1 terhubung langsung ke jam atom atau GPS, server stratum 2 mengambil waktu dari server tersebut, dan seterusnya. ESP32 meminta waktu melalui paket kecil pada port UDP 123; server membalas dengan cap waktu berisi detik sejak 1900 beserta pecahan detiknya. ESP32 memperhitungkan waktu perjalanan pulang-pergi paket dan mengatur jamnya sendiri. Pada koneksi internet rumah, akurasi yang diperoleh biasanya antara 10 dan 50 milidetik, lebih dari cukup untuk proyek jam.
Stack jaringan lwIP di dalam ESP32 memakai SNTP, versi sederhana dari NTP. Setelah menerima waktu, ESP32 menyimpannya sebagai waktu sistem dan terus menghitung dengan timer yang bersumber dari kristalnya; board tidak mengakses internet setiap detik. Di latar belakang, board meminta waktu lagi ke server secara berkala (secara default setiap 3 jam pada core Arduino ESP32) dan mengoreksi pergeseran kecil. Waktu sistem disimpan dalam UTC; waktu lokal yang kamu lihat dihitung dengan menerapkan aturan zona waktu.
| Konsep | Nilai / penjelasan |
|---|---|
| Protokol dan port | SNTP, UDP 123 |
| Cap waktu | Detik sejak 1900 (ESP32 mengubahnya menjadi waktu Unix berbasis 1970) |
| Akurasi tipikal | 10–50 ms pada koneksi internet rumah |
| Sinkronisasi ulang | Default 3 jam pada Arduino ESP32, diubah dengan sntp_set_sync_interval() (minimum 15 dtk) |
| Jumlah maksimum server | 3 (nama server yang diteruskan ke configTzTime) |
| Lalu lintas internet | Request dan respons masing-masing berupa paket NTP 48 byte; paling banyak beberapa kilobyte per hari |
| Yang dibutuhkan | ESP32, jaringan Wi-Fi 2,4 GHz, dan koneksi internet |
configTime dan configTzTime: mengatur zona waktu
Ada dua cara memulai NTP pada core Arduino ESP32. configTime(gmtOffset_sec, daylightOffset_sec, server1, server2, server3) menerima selisih terhadap UTC dan selisih waktu musim panas dalam detik. Untuk Turki, misalnya, kamu menulis configTime(10800, 0, "pool.ntp.org"), karena 3 jam sama dengan 10.800 detik dan Turki tetap menggunakan UTC+3 sepanjang tahun tanpa waktu musim panas sejak 2016. Cara ini sederhana, tetapi tidak mengetahui tanggal pergantian waktu musim panas di wilayahmu; di tempat yang menerapkan DST, seperti Eropa atau AS, pergantian terjadi pada hari yang salah, jadi gunakan configTzTime di sana.
Cara yang lebih baik adalah configTzTime(TZ, server1, server2, server3). Fungsi ini menerima string zona waktu dalam format POSIX yang juga memuat aturan waktu musim panas, lalu ESP32 menangani pergantiannya sendiri setiap tahun. Untuk Turki, string-nya adalah "<+03>-3": +03 di dalam kurung sudut merupakan singkatan yang tampil di layar, sedangkan -3 berarti kamu mengurangi waktu lokal sebanyak 3 jam untuk mendapatkan UTC. Tabel berikut mencantumkan string untuk wilayah yang umum digunakan.
| Wilayah | String TZ (configTzTime) | Padanan configTime |
|---|---|---|
| Turki (UTC+3 sepanjang tahun) | <+03>-3 | configTime(10800, 0, ...) |
| Jerman, Prancis, Italia, Spanyol | CET-1CEST,M3.5.0,M10.5.0/3 | Tidak cocok, gunakan configTzTime |
| Britania Raya, Portugal | GMT0BST,M3.5.0/1,M10.5.0 | Tidak cocok, gunakan configTzTime |
| AS bagian timur (New York) | EST5EDT,M3.2.0,M11.1.0 | Tidak cocok, gunakan configTzTime |
| Azerbaijan, Georgia (UTC+4) | <+04>-4 | configTime(14400, 0, ...) |
| UTC (untuk cap waktu) | UTC0 | configTime(0, 0, ...) |
Kebutuhan dan pengkabelan
Untuk melihat waktu di Serial Monitor, kamu hanya membutuhkan ESP32 dan jaringan Wi-Fi. Pada bagian berikutnya kita memakai layar OLED SSD1306 0,96 inci untuk menampilkan waktu, modul RTC DS3231 untuk mempertahankannya ketika listrik padam, dan modul relay untuk contoh tugas terjadwal. OLED dan DS3231 berbagi bus I2C yang sama, sehingga keduanya terhubung ke dua pin yang sama. Kamu bisa melihat pinout ESP32 dengan alat Pinout.
| Pin komponen | ESP32 | Raspberry Pi Pico W | Catatan |
|---|---|---|---|
| OLED / DS3231 VCC | 3V3 | 3V3 (OUT) | Keduanya bekerja pada 3,3 V |
| OLED / DS3231 GND | GND | GND | Ground bersama |
| OLED / DS3231 SDA | GPIO 21 | GP4 | Jalur data I2C |
| OLED / DS3231 SCL | GPIO 22 | GP5 | Jalur clock I2C |
| IN modul relay | GPIO 26 | GP15 | Contoh tugas terjadwal |
| VCC modul relay | 5V (VIN) | VBUS | Sebagian besar kumparan relay membutuhkan 5 V |
Langkah demi langkah: sketch NTP ESP32 pertamamu
- Pastikan paket board ESP32 sudah terinstal di Arduino IDE dan pilih ESP32 Dev Module di Tools. Jika kamu belum tahu caranya, lihat artikel Apa Itu ESP32?.
- Tidak perlu library tambahan: WiFi.h dan time.h sudah disertakan dalam core ESP32.
- Ganti nilai WIFI_SSID dan WIFI_PASS dengan detail jaringanmu, serta TZ_INFO dengan zona waktumu dari tabel di atas.
- Upload sketch dan buka Serial Monitor pada 115200 baud.
- Mula-mula kamu akan melihat titik-titik Wi-Fi, lalu setelah beberapa detik tanggal dan waktu diperbarui setiap detik.
1#include <WiFi.h>
2#include <time.h>
3
4const char* WIFI_SSID = "AgAdi"; // Wi-Fi adı / Wi-Fi name
5const char* WIFI_PASS = "Sifre123"; // Wi-Fi şifresi / Wi-Fi password
6const char* TZ_INFO = "<+03>-3"; // Türkiye: UTC+3, yaz saati yok / Turkey: UTC+3, no DST
7
8void setup() {
9 Serial.begin(115200);
10 WiFi.begin(WIFI_SSID, WIFI_PASS);
11 Serial.print("Wi-Fi");
12 while (WiFi.status() != WL_CONNECTED) { // bağlanana kadar bekle / wait until connected
13 delay(500);
14 Serial.print(".");
15 }
16 Serial.println(" OK");
17
18 // NTP sunucuları + saat dilimi / NTP servers + time zone
19 configTzTime(TZ_INFO, "pool.ntp.org", "time.cloudflare.com");
20
21 struct tm t;
22 while (!getLocalTime(&t)) { // ilk eşitlemeyi bekle (5 sn deneme) / wait for the first sync (5 s per try)
23 Serial.println("NTP bekleniyor / waiting for NTP");
24 }
25 Serial.println("Saat alindi / time received");
26}
27
28void loop() {
29 struct tm t;
30 if (getLocalTime(&t)) {
31 Serial.println(&t, "%d.%m.%Y %H:%M:%S"); // ör. 16.11.2026 14:05:09 / e.g. 16.11.2026 14:05:09
32 }
33 delay(1000);
34}
35| Baris | Apa fungsinya? |
|---|---|
| configTzTime(TZ_INFO, ...) | Memulai klien SNTP serta mengatur server dan zona waktu; langsung kembali, waktu diterima di latar belakang |
| getLocalTime(&t) | Mengisi struct tm dengan waktu lokal; jika waktu belum tersedia, menunggu hingga 5 detik dan mengembalikan false |
| Serial.println(&t, "...") | Kelas Print ESP32 dapat mencetak struct tm langsung dengan format strftime |
| time(nullptr) | Mengembalikan detik sejak 1970 (waktu Unix, UTC) |
Ada detail penting di sini: getLocalTime() menentukan apakah waktu “valid” dengan memeriksa tahunnya. Ketika ESP32 melakukan boot, waktu sistem adalah 1 Januari 1970, dan fungsi mengembalikan false hingga tahunnya lebih besar dari 2016. Jadi, kode yang mencatat data atau menjalankan tugas sebelum waktu NTP diterima tidak akan tanpa sengaja menghasilkan entri bertanggal 1970, asalkan kamu memeriksa nilai kembaliannya. Parameter kedua adalah batas waktu dalam milidetik; jika kamu tidak ingin memblokir di dalam loop(), tulis getLocalTime(&t, 0) agar hanya mencoba sekali.
struct tm: bagian-bagian waktu
getLocalTime() menuliskan waktu ke dalam struct tm, yang terbagi menjadi beberapa bagian. Kamu memakai field ini untuk membandingkan jam, hari, atau bulan ketika membuat tugas terjadwal. Dua field sering membingungkan pemula: bulan dihitung mulai dari 0 dan tahun dihitung sejak 1900.
| Field | Rentang | Contoh (Senin 16 November 2026, 14.05.09) |
|---|---|---|
| tm_sec | 0–59 (60 pada detik kabisat) | 9 |
| tm_min | 0–59 | 5 |
| tm_hour | 0–23 | 14 |
| tm_mday | 1–31 | 16 |
| tm_mon | 0–11 (0 = Januari) | 10 |
| tm_year | Tahun sejak 1900 | 126 (1900 + 126 = 2026) |
| tm_wday | 0–6 (0 = Minggu) | 1 |
| tm_yday | 0–365 (hari dalam setahun, mulai dari 0) | 319 |
Memformat tanggal dan waktu dengan strftime
strftime() menuliskan waktu dari struct tm ke array karakter sesuai pola. Kode persen dalam pola diganti dengan nilai yang sesuai, dan semua karakter lainnya tetap apa adanya. Tentukan ukuran array sebesar keluaran terpanjang ditambah satu (untuk nol penutup): “14:05:09” terdiri dari 8 karakter, jadi array harus berukuran minimal 9 byte. Kode yang paling umum:
| Kode | Arti | Contoh keluaran |
|---|---|---|
| %H / %M / %S | Jam (00–23) / menit / detik | 14 / 05 / 09 |
| %I dan %p | Format 12 jam dan AM/PM | 02 PM |
| %d / %m / %Y | Hari / bulan (01–12) / tahun empat digit | 16 / 11 / 2026 |
| %y | Tahun dua digit | 26 |
| %A / %a | Nama hari, panjang / pendek (bahasa Inggris) | Monday / Mon |
| %B / %b | Nama bulan, panjang / pendek (bahasa Inggris) | November / Nov |
| %j | Hari dalam setahun (001–366) | 320 |
| %F / %T | Singkatan untuk %Y-%m-%d / %H:%M:%S | 2026-11-16 / 14:05:09 |
| %Z | Singkatan zona waktu | +03 |
Library C pada ESP32 hanya mengenal nama hari dan bulan bahasa Inggris; %A selalu mencetak “Monday”. Untuk tanggal dalam bahasa lain, simpan nama hari dan bulan dalam array-mu sendiri lalu gunakan tm_wday dan tm_mon sebagai indeks array. Contoh berikut memakai nama bahasa Turki; cukup ganti dengan bahasamu. Karena Serial Monitor mendukung UTF-8, huruf beraksen tampil dengan benar di sana. Sketch menghasilkan tanggal yang mudah dibaca sekaligus format ISO yang dapat diurutkan (2026-11-16 14:05:09) untuk pencatatan.
1#include <WiFi.h>
2#include <time.h>
3
4const char* WIFI_SSID = "AgAdi";
5const char* WIFI_PASS = "Sifre123";
6const char* TZ_INFO = "<+03>-3";
7
8// tm_wday: 0 = Pazar ... 6 = Cumartesi / 0 = Sunday ... 6 = Saturday
9const char* GUNLER[] = {"Pazar", "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi"};
10const char* AYLAR[] = {"Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran",
11 "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"};
12
13void setup() {
14 Serial.begin(115200);
15 WiFi.begin(WIFI_SSID, WIFI_PASS);
16 while (WiFi.status() != WL_CONNECTED) delay(500);
17 configTzTime(TZ_INFO, "pool.ntp.org");
18}
19
20void loop() {
21 struct tm t;
22 if (!getLocalTime(&t)) return; // saat henüz yok / time not set yet
23
24 char saat[9]; // "HH:MM:SS" + '\0'
25 char iso[20]; // "YYYY-MM-DD HH:MM:SS" + '\0'
26 strftime(saat, sizeof(saat), "%H:%M:%S", &t);
27 strftime(iso, sizeof(iso), "%Y-%m-%d %H:%M:%S", &t);
28
29 // Türkçe tarih: 16 Kasım 2026 Pazartesi / Turkish date
30 Serial.printf("%d %s %d %s %s\n", t.tm_mday, AYLAR[t.tm_mon], t.tm_year + 1900, GUNLER[t.tm_wday], saat);
31 Serial.printf("Kayit / log: %s epoch: %ld\n", iso, (long)time(nullptr));
32 delay(1000);
33}
34Menampilkan waktu dan tanggal pada layar OLED
Untuk mengubah keluaran Serial Monitor menjadi jam meja sungguhan, layar OLED SSD1306 0,96 inci sangat cocok: murah, terhubung dengan dua kabel, dan menampilkan angka besar dengan tajam. Sketch berikut menampilkan hari dan tanggal di bagian atas, waktu dengan font besar di tengah, serta status Wi-Fi di bawah. Karena layar digambar ulang hanya ketika detik berubah, bukan pada setiap perulangan, bus I2C tidak terus sibuk tanpa perlu. Instal library Adafruit SSD1306 dan Adafruit GFX melalui Library Manager; layarnya sendiri dibahas dalam artikel layar OLED (SSD1306).
1#include <WiFi.h>
2#include <Wire.h>
3#include <Adafruit_GFX.h>
4#include <Adafruit_SSD1306.h>
5#include <time.h>
6
7const char* WIFI_SSID = "AgAdi";
8const char* WIFI_PASS = "Sifre123";
9const char* TZ_INFO = "<+03>-3";
10// OLED yazı tipinde Türkçe harf yok / the OLED font has no Turkish letters
11const char* GUNLER[] = {"Pazar", "Pazartesi", "Sali", "Carsamba", "Persembe", "Cuma", "Cumartesi"};
12
13Adafruit_SSD1306 display(128, 64, &Wire, -1);
14int sonSaniye = -1; // son çizilen saniye / last drawn second
15
16void yaz(const char* mesaj) { // ekrana tek satır / one line on screen
17 display.clearDisplay();
18 display.setTextSize(1);
19 display.setCursor(0, 0);
20 display.print(mesaj);
21 display.display();
22}
23
24void setup() {
25 Wire.begin(21, 22); // SDA = 21, SCL = 22
26 display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
27 display.setTextColor(SSD1306_WHITE);
28 yaz("Wi-Fi baglaniyor...");
29
30 WiFi.begin(WIFI_SSID, WIFI_PASS);
31 while (WiFi.status() != WL_CONNECTED) delay(300);
32 configTzTime(TZ_INFO, "pool.ntp.org", "time.cloudflare.com");
33 yaz("NTP bekleniyor...");
34}
35
36void loop() {
37 struct tm t;
38 if (!getLocalTime(&t, 0)) { // 0 = beklemeden dene / try without waiting
39 delay(200);
40 return;
41 }
42 if (t.tm_sec == sonSaniye) { // saniye değişmediyse çizme / redraw only on a new second
43 delay(20);
44 return;
45 }
46 sonSaniye = t.tm_sec;
47
48 char saat[9], tarih[11];
49 strftime(saat, sizeof(saat), "%H:%M:%S", &t);
50 strftime(tarih, sizeof(tarih), "%d.%m.%Y", &t);
51
52 display.clearDisplay();
53 display.setTextSize(1);
54 display.setCursor(0, 0);
55 display.print(GUNLER[t.tm_wday]); // gün adı / day name
56 display.setCursor(68, 0);
57 display.print(tarih); // 10 karakter = 60 piksel / 10 chars = 60 px
58 display.setTextSize(2);
59 display.setCursor(16, 26);
60 display.print(saat); // 8 x 12 = 96 piksel genişlik / 96 px wide
61 display.setTextSize(1);
62 display.setCursor(0, 56);
63 display.print(WiFi.status() == WL_CONNECTED ? "Wi-Fi: var" : "Wi-Fi: yok");
64 display.display();
65}
66Perhatikan bahwa nama hari ditulis tanpa huruf beraksen: font default Adafruit GFX tidak memiliki karakter seperti ş, ç, atau é dan menampilkan simbol tak bermakna sebagai gantinya. Pada ukuran teks 2, setiap karakter lebarnya 12 piksel; “14:05:09” yang terdiri dari 8 karakter memerlukan 96 piksel, jadi mulai dari piksel 16 membuatnya tepat di tengah layar 128 piksel. Jika kamu lebih suka LCD karakter 16x2, cukup cetak keluaran strftime yang sama dengan lcd.print(); pengkabelannya dijelaskan dalam artikel layar LCD I2C.
Apa yang terjadi pada waktu ketika Wi-Fi terputus? Cadangan RTC DS3231
Pertanyaan yang paling umum: jika Wi-Fi hilang, apakah waktunya ikut hilang? Tidak. Setelah ESP32 mendapatkan waktu dari NTP, board terus menghitung dengan timer-nya sendiri; kehilangan Wi-Fi tidak menghentikan jam. Karena timer ini bersumber dari kristal 40 MHz, waktunya bergeser sekitar satu atau dua detik per hari, dan sinkronisasi berikutnya mengoreksinya ketika Wi-Fi kembali. Masalah sebenarnya adalah daya terputus atau mulai ulang dengan tombol EN: waktu sistem kembali ke 1970, dan tanpa Wi-Fi ESP32 tidak bisa mengetahui waktu.
| Situasi | Apakah waktu dipertahankan? | Yang perlu dilakukan |
|---|---|---|
| Wi-Fi terputus sementara | Ya, ESP32 terus menghitung | Tidak perlu tindakan; NTP mengoreksinya ketika Wi-Fi kembali |
| Deep sleep | Ya, timer RTC terus menghitung | Untuk sleep yang lama, sinkronkan ulang dengan NTP pada beberapa siklus bangun |
| Daya terputus atau tombol EN | Tidak, jam kembali ke 1970 | Tambahkan modul RTC dengan baterai cadangan seperti DS3231 |
| Tidak ada internet sama sekali (lapangan, gudang) | Waktu NTP tidak pernah diterima | Ambil waktu hanya dari modul RTC |
Selama deep sleep, waktu tidak dihitung oleh kristal utama, melainkan oleh osilator RC di dalam chip yang kurang presisi. Osilator ini dipengaruhi suhu, sehingga selama sleep yang lama jam bisa bergeser beberapa detik atau lebih; pada proyek bertenaga baterai yang sleep berjam-jam, sebaiknya lakukan sinkronisasi NTP setiap beberapa kali bangun. Detailnya ada dalam artikel deep sleep ESP32.

Cara terbaik membuat jam yang tetap berjalan ketika listrik padam adalah menggabungkan keduanya: ketika boot, segera ambil waktu dari modul DS3231, koreksi dengan NTP setelah Wi-Fi aktif, lalu perbarui DS3231 pada setiap sinkronisasi NTP. Dalam sketch berikut, DS3231 menyimpan UTC, bukan waktu lokal; dengan begitu aturan zona waktu berada di satu tempat (string TZ) dan pergantian waktu musim panas tidak pernah mengacaukan RTC. Untuk mendeteksi sinkronisasi NTP, callback didaftarkan dengan sntp_set_time_sync_notification_cb(). Karena callback ini berjalan di dalam tugas jaringan, tidak ada operasi I2C di sana; callback hanya mengaktifkan flag, dan loop() menangani penulisan ke DS3231. Sketch memakai library RTClib.
1#include <WiFi.h>
2#include <Wire.h>
3#include <RTClib.h>
4#include <sys/time.h>
5#include "esp_sntp.h"
6
7const char* WIFI_SSID = "AgAdi";
8const char* WIFI_PASS = "Sifre123";
9const char* TZ_INFO = "<+03>-3";
10
11RTC_DS3231 rtc;
12volatile bool ntpGeldi = false; // NTP eşitlemesi oldu mu? / did an NTP sync happen?
13
14void ntpCallback(struct timeval* tv) { // her NTP eşitlemesinde çağrılır / called on every NTP sync
15 ntpGeldi = true; // I2C işini loop() yapsın / let loop() do the I2C work
16}
17
18void setup() {
19 Serial.begin(115200);
20 Wire.begin(21, 22);
21 setenv("TZ", TZ_INFO, 1); // NTP gelmese de saat dilimi geçerli / TZ valid even without NTP
22 tzset();
23
24 // 1) Sistem saatini önce DS3231'den kur (DS3231'de UTC saklanır) / set system time from the DS3231 (stores UTC)
25 if (rtc.begin() && !rtc.lostPower()) {
26 struct timeval tv = { (time_t)rtc.now().unixtime(), 0 };
27 settimeofday(&tv, nullptr);
28 Serial.println("Saat DS3231'den / time from DS3231");
29 }
30
31 // 2) Wi-Fi gelince NTP saati düzeltir; bağlantı sonra gelse de SNTP yeniden dener
32 // NTP corrects the clock once Wi-Fi is up; SNTP keeps retrying if the link comes later
33 sntp_set_time_sync_notification_cb(ntpCallback);
34 WiFi.begin(WIFI_SSID, WIFI_PASS);
35 configTzTime(TZ_INFO, "pool.ntp.org", "time.cloudflare.com");
36}
37
38void loop() {
39 if (ntpGeldi) {
40 ntpGeldi = false;
41 rtc.adjust(DateTime((uint32_t)time(nullptr))); // DS3231'e UTC yaz / write UTC to the DS3231
42 Serial.println("DS3231 NTP ile guncellendi / DS3231 updated from NTP");
43 }
44
45 struct tm t;
46 if (getLocalTime(&t, 0)) {
47 Serial.println(&t, "%d.%m.%Y %H:%M:%S");
48 } else {
49 Serial.println("Saat yok: NTP ve DS3231 bekleniyor / no time yet");
50 }
51 delay(1000);
52}
53Menjalankan tugas pada waktu tertentu setiap hari
Penggunaan waktu NTP yang paling umum adalah tugas terjadwal: menjalankan pompa penyiraman selama 10 menit pada pukul 07.30, menyalakan lampu taman pada pukul 19.00, mengirim pembacaan tepat setiap pergantian jam. Caranya sederhana: baca waktu di loop() dan mulai tugas ketika jam serta menit sesuai dengan target. Detail yang perlu diperhatikan adalah mencegah tugas terpicu ratusan kali dalam menit yang sama. Untuk itu, simpan hari pelaksanaan tugas (tm_yday) dalam variabel agar tugas tidak pernah berjalan dua kali pada hari yang sama. Sketch berikut menyalakan relay pada GPIO 26 selama 10 menit setiap pagi pada hari kerja pukul 07.30; karena mengukur durasi dengan millis(), kode tidak pernah memblokir.
1#include <WiFi.h>
2#include <time.h>
3
4const char* WIFI_SSID = "AgAdi";
5const char* WIFI_PASS = "Sifre123";
6const char* TZ_INFO = "<+03>-3";
7
8const int ROLE_PIN = 26; // röle modülü IN / relay module IN
9const int GOREV_SAAT = 7; // her gün 07:30 / every day at 07:30
10const int GOREV_DAKIKA = 30;
11const unsigned long SURE_MS = 10UL * 60 * 1000; // 10 dakika çalış / run for 10 minutes
12
13int sonGun = -1; // görevin çalıştığı son gün (tm_yday) / last day the task ran
14bool roleAcik = false;
15unsigned long acilisMs = 0;
16
17void setup() {
18 Serial.begin(115200);
19 pinMode(ROLE_PIN, OUTPUT);
20 digitalWrite(ROLE_PIN, LOW);
21 WiFi.begin(WIFI_SSID, WIFI_PASS);
22 while (WiFi.status() != WL_CONNECTED) delay(500);
23 configTzTime(TZ_INFO, "pool.ntp.org");
24}
25
26void loop() {
27 struct tm t;
28 if (getLocalTime(&t, 0)) { // saat geçerliyse / only when the time is valid
29 bool haftaici = (t.tm_wday >= 1 && t.tm_wday <= 5); // Pzt-Cuma / Mon-Fri
30 if (haftaici && t.tm_hour == GOREV_SAAT && t.tm_min == GOREV_DAKIKA && t.tm_yday != sonGun) {
31 sonGun = t.tm_yday; // bugün bir kez / once per day
32 digitalWrite(ROLE_PIN, HIGH);
33 roleAcik = true;
34 acilisMs = millis();
35 Serial.println(&t, "%H:%M gorev basladi / task started");
36 }
37 }
38 if (roleAcik && millis() - acilisMs >= SURE_MS) { // süre dolunca kapat / switch off after the period
39 digitalWrite(ROLE_PIN, LOW);
40 roleAcik = false;
41 Serial.println("Gorev bitti / task finished");
42 }
43 delay(200);
44}
45- Mencocokkan detik: kondisi seperti t.tm_sec == 0 melewatkan tugas jika perulangan melewatkan detik tersebut (ketika Wi-Fi menghubungkan ulang atau selama delay yang panjang). Memeriksa menit dan hari lebih aman.
- Memeriksa validitas waktu: jangan jalankan tugas selama getLocalTime() mengembalikan false, atau kamu akan membuat keputusan yang salah berdasarkan tahun 1970 tepat setelah boot.
- Tugas terlewat: jika listrik padam pukul 07.29 dan kembali pukul 07.35, tugas tidak berjalan hari itu. Jika ini penting, tulis kondisi sebagai “sudah lewat pukul 07.30 dan belum berjalan hari ini” lalu simpan sonGun ke flash dengan Preferences.
- Beberapa tugas: simpan jam, menit, dan durasi dalam array serta gunakan variabel hari pelaksanaan terakhir yang terpisah untuk setiap tugas.
- Beban berarus tinggi: kendalikan pompa atau lampu melalui modul relay atau MOSFET, jangan pernah langsung dari pin.
Mengubah interval sinkronisasi dan memantau NTP
Interval sinkronisasi default 3 jam cocok untuk sebagian besar proyek. Untuk menyinkronkan lebih sering (misalnya di luar ruangan dengan perubahan suhu besar) atau mengurangi lalu lintas, teruskan interval baru dalam milidetik ke sntp_set_sync_interval(). Panggil sebelum configTzTime(), karena interval baru berlaku pada permulaan berikutnya atau ketika interval sekarang berakhir. Menurut aturan protokol, interval tidak boleh lebih pendek dari 15 detik. sntp_get_sync_status() mengembalikan SNTP_SYNC_STATUS_COMPLETED sekali setelah sinkronisasi selesai lalu mereset dirinya, sehingga kamu bisa mendeteksi setiap sinkronisasi baru.
1#include <WiFi.h>
2#include <time.h>
3#include "esp_sntp.h"
4
5const char* TZ_INFO = "<+03>-3";
6
7void setup() {
8 Serial.begin(115200);
9 WiFi.begin("AgAdi", "Sifre123");
10 while (WiFi.status() != WL_CONNECTED) delay(500);
11
12 sntp_set_sync_interval(6UL * 60 * 60 * 1000); // 6 saatte bir eşitle (en az 15 sn) / sync every 6 hours (min 15 s)
13 configTzTime(TZ_INFO, "pool.ntp.org"); // ayar sntp_init() öncesi verilir / set before sntp_init()
14}
15
16void loop() {
17 if (sntp_get_sync_status() == SNTP_SYNC_STATUS_COMPLETED) {
18 Serial.println("Yeni NTP esitlemesi / new NTP sync");
19 }
20 delay(1000);
21}
22Server NTP mana yang sebaiknya digunakan?
Pilihan paling umum adalah pool.ntp.org: kumpulan ribuan server yang dijalankan sukarelawan di seluruh dunia dan memberikan alamat server terdekat pada setiap request. Subdomain negara (uk.pool.ntp.org, de.pool.ntp.org) memberikan server yang lebih dekat lagi. configTzTime() menerima hingga tiga nama server; jika server pertama tidak menjawab, fungsi beralih ke server berikutnya.
| Server | Fitur | Catatan |
|---|---|---|
| pool.ntp.org (alamat server NTP) | Kumpulan server sukarelawan, server terdekat | Pilihan pertama untuk penggunaan umum |
| uk.pool.ntp.org / europe.pool.ntp.org (alamat server NTP per negara atau benua) | Kumpulan server negara atau benua | Latensi sedikit lebih rendah |
| time.cloudflare.com (server NTP Cloudflare) | Infrastruktur perusahaan besar | Bagus sebagai server cadangan |
| time.google.com (server NTP Google) | Menyebarkan detik kabisat secara bertahap (leap smear) | Jangan campurkan dengan server lain |
| Alamat IP router-mu | Sebagian router menyediakan NTP lokal | Berguna pada jaringan lokal tanpa internet |
NTP di Raspberry Pi Pico W: ntptime
Di Raspberry Pi Pico W (dan Pico 2 W), pekerjaan yang sama dilakukan dengan modul ntptime MicroPython. ntptime.settime() meminta waktu ke server NTP dan mengatur RTC internal Pico ke UTC. MicroPython tidak memiliki string zona waktu seperti ESP32; untuk waktu lokal, tambahkan selisih waktumu dalam detik ke UTC (3 × 3600 untuk UTC+3 dalam contoh). ntptime hanya mengambil waktu sekali dan tidak menyinkronkan ulang secara otomatis; pada proyek yang berjalan lama, memanggil settime() sekali atau dua kali sehari menghilangkan pergeseran. RTC Pico W juga direset ketika daya hilang, jadi cadangan DS3231 yang sama berlaku di sini. Menghubungkan Pico W ke Wi-Fi dibahas secara terperinci dalam artikel web server Pico W dan Wi-Fi.
1import network
2import ntptime
3import time
4
5SSID = "AgAdi"
6PASS = "Sifre123"
7UTC_OFFSET = 3 * 3600 # Türkiye UTC+3 / Turkey UTC+3
8# localtime(): 0 = Pazartesi / 0 = Monday
9GUNLER = ("Pazartesi", "Sali", "Carsamba", "Persembe", "Cuma", "Cumartesi", "Pazar")
10
11wlan = network.WLAN(network.STA_IF)
12wlan.active(True)
13wlan.connect(SSID, PASS)
14while not wlan.isconnected(): # bağlanana kadar bekle / wait until connected
15 time.sleep(0.5)
16
17ntptime.host = "pool.ntp.org"
18for deneme in range(5): # NTP bazen zaman aşımına uğrar / NTP may time out
19 try:
20 ntptime.settime() # dahili RTC'yi UTC'ye ayarlar / sets the internal RTC to UTC
21 break
22 except OSError:
23 time.sleep(2)
24
25def yerel_saat(): # UTC + 3 saat / UTC + 3 hours
26 return time.localtime(time.time() + UTC_OFFSET)
27
28son_gun = -1
29while True:
30 yil, ay, gun, saat, dk, sn, hgun, ygun = yerel_saat()
31 print("{:02d}.{:02d}.{} {} {:02d}:{:02d}:{:02d}".format(gun, ay, yil, GUNLER[hgun], saat, dk, sn))
32 if saat == 7 and dk == 30 and ygun != son_gun: # her gün 07:30'da bir kez / once a day at 07:30
33 son_gun = ygun
34 print("Gorev calisti / task ran")
35 time.sleep(1)
36Dalam MicroPython, time.localtime() mengembalikan tuple delapan elemen: tahun, bulan (1–12), hari, jam, menit, detik, hari dalam minggu, dan hari dalam setahun. Ada dua perbedaan dari struct tm ESP32: bulan dimulai dari 1 dan hari dalam minggu dihitung dengan 0 = Senin. ntptime.settime() memunculkan OSError ketika jaringan melewati batas waktu, itulah sebabnya sketch memiliki perulangan lima kali percobaan.
Ide proyek jam NTP ESP32
- Jam meja Wi-Fi: tampilkan waktu, tanggal, dan suhu ruangan pada satu layar OLED, lalu redupkan pada malam hari.
- Jam LED raksasa: tampilkan waktu NTP sebagai teks berjalan pada matriks LED MAX7219.
- Penyiraman tanaman terjadwal: periksa kelembapan tanah setiap pagi dan jalankan pompa air mini selama waktu tertentu jika diperlukan.
- Pencatatan data dengan cap waktu: tuliskan pembacaan sensor dengan tanggal ISO ke kartu SD.
- Jam cuaca: tambahkan informasi cuaca dari internet di samping waktu; artikel cuaca ESP32 melanjutkan pembahasan ini.
- Laporan pagi: setiap hari pukul 08.00, kirim suhu rumah dan gerakan yang terdeteksi semalaman ke ponselmu melalui Telegram.
Kesalahan umum dan solusinya
| Gejala | Kemungkinan penyebab | Solusi |
|---|---|---|
| Waktu menampilkan 1970 atau tidak pernah diterima | Wi-Fi belum terhubung atau port NTP (UDP 123) diblokir | Periksa WiFi.status() dahulu; pada jaringan sekolah dan kantor, coba jaringan lain atau hotspot ponsel |
| Waktu meleset tepat sebesar selisih UTC-mu | Zona waktu belum diatur, yang ditampilkan adalah UTC | configTzTime("<+03>-3", ...) atau string untuk zona waktumu |
| Waktu meleset sebesar dua kali selisihnya | Tanda terbalik dalam string TZ ("UTC+3") | Gunakan "<+03>-3" atau "UTC-3" untuk UTC+3 |
| Bulan kurang satu, tahun menunjukkan 126 | tm_mon dihitung mulai dari 0, tm_year sejak 1900 | Gunakan tm_mon + 1, tm_year + 1900, atau strftime |
| Tugas terpicu berkali-kali dalam menit yang sama | Tidak ada pencegah pengulangan | Simpan hari pelaksanaannya (tm_yday) dan periksa |
| Perulangan terhenti selama 5 detik setiap kali berjalan | Tanpa waktu yang valid, getLocalTime() secara default menunggu 5000 ms | Gunakan getLocalTime(&t, 0) di dalam loop() |
| Simbol aneh pada nama hari di OLED | Font tidak memiliki huruf beraksen | Tulis nama hari dalam ASCII biasa atau beralih ke library U8g2 |
NTP ESP32 adalah cara termurah dan paling praktis untuk menambahkan waktu yang akurat ke proyek: dengan beberapa baris kode, board mendapatkan waktu yang tepat hingga detik, menangani zona waktu dan waktu musim panas sendiri, serta tetap mempertahankan waktu ketika listrik padam jika ditambahkan DS3231. Di aplikasi BlueGrays kamu akan menemukan proyek langkah demi langkah lengkap dengan skema rangkaian dan kode untuk jam waktu nyata melalui NTP ESP32, jam digital dengan RTC DS1307, tampilan cuaca di OLED, dan pencatat suhu dengan cap waktu.
Pertanyaan umum
Bisakah ESP32 mempertahankan waktu tanpa internet?
Bisa, setelah menerima waktu dari NTP: ESP32 terus menghitung waktu sistem dengan timer-nya sendiri, dan kehilangan Wi-Fi tidak menghentikan jam. Namun, jika daya hilang atau board mulai ulang, waktu kembali ke 1970. Di tempat tanpa internet, atau untuk jam yang tetap mempertahankan waktu ketika listrik padam, tambahkan modul RTC dengan baterai cadangan seperti DS3231.
Mengapa waktu NTP ESP32-ku meleset beberapa jam?
Jika zona waktu belum diatur, ESP32 menampilkan UTC, sehingga jam meleset tepat sebesar selisih UTC-mu. Gunakan configTzTime dengan string POSIX yang benar, misalnya "CET-1CEST,M3.5.0,M10.5.0/3" untuk Eropa Tengah atau "GMT0BST,M3.5.0/1,M10.5.0" untuk Britania Raya. Jika meleset sebesar dua kali selisihnya, tanda dalam string TZ-mu terbalik.
Apa perbedaan configTime dan configTzTime?
configTime menerima selisih UTC dan selisih waktu musim panas dalam detik, tetapi tidak mengetahui kapan DST dimulai dan berakhir di wilayahmu. configTzTime menerima string zona waktu POSIX yang mencakup aturan DST, sehingga ESP32 beralih antara waktu musim panas dan musim dingin secara otomatis. Untuk wilayah mana pun yang menerapkan waktu musim panas, configTzTime adalah pilihan yang tepat.
Seberapa sering ESP32 menyinkronkan waktu dengan NTP?
Pada core Arduino ESP32, SNTP secara default meminta waktu lagi ke server setiap 3 jam dan mempertahankan waktu dengan timer-nya sendiri di antaranya. Kamu bisa mengubah interval dalam milidetik dengan sntp_set_sync_interval(), tetapi menurut aturan protokol interval tidak boleh lebih pendek dari 15 detik.
Apakah ESP32 membutuhkan modul RTC?
Tidak jika perangkat selalu mendapat daya dan dapat mengakses Wi-Fi; NTP memberikan waktu yang lebih akurat. Jika kamu membutuhkan waktu yang benar setelah listrik padam sebelum internet kembali, atau perangkat akan berjalan di tempat tanpa internet, tambahkan modul RTC dengan baterai cadangan seperti DS3231.
Bagaimana cara mendapatkan waktu NTP di Raspberry Pi Pico W?
Dalam MicroPython, setelah terhubung ke Wi-Fi, cukup jalankan import ntptime dan ntptime.settime(); ini mengatur RTC internal Pico ke UTC. Untuk waktu lokal, tambahkan selisih waktumu dalam detik, misalnya time.localtime(time.time() + 3600) untuk UTC+1. ntptime tidak menyinkronkan ulang secara otomatis, jadi panggil settime() lagi sekali atau dua kali sehari.
Proyek untuk dibuat setelah membaca ini
Lihat semua proyek
Jam Real-Time dari Internet lewat NTP ESP32
ESP32 tersambung ke Wi-Fi dan mengambil waktu nyata dengan presisi milidetik dari server NTP (Network Time Protocol). Berkat fungsi configTzTime(), zona waktu Turki (TRT, UTC+3) otomatis diterapkan, dan pergantian DST di negara yang memakai waktu musim panas otomatis diatur dengan string POSIX TZ. Waktunya ditampilkan di Serial Monitor sekaligus di LCD I2C opsional. configTzTime() memulai layanan SNTP; sinkronisasi ulang berkala terjadi selama koneksi tersedia.

Jam Digital dengan ESP32 dan RTC DS1307 (I2C + LCD)
Di proyek ini, kita menampilkan waktu dan tanggal di layar LCD I2C 16x2 memakai modul jam real-time (RTC) DS1307. DS1307 adalah RTC berbaterai cadangan yang tetap menjaga waktu meskipun ESP32 dimatikan. Kedua modul berbagi bus I2C yang sama (SDA/SCL), sehingga kita belajar mengendalikan beberapa perangkat hanya dengan dua kabel sinyal. Lewat proyek ini kamu mendapat pengetahuan praktis tentang protokol komunikasi I2C, cara kerja modul RTC, dan tampilan data dinamis di LCD.

Koneksi Wi-Fi STA ESP32 (dengan Layar LCD)
Di proyek ini, kita menyambungkan ESP32 ke jaringan Wi-Fi rumah dalam mode Station (STA) dengan cara yang aman dan tahan gangguan. Kita menyimpan SSID dan kata sandi di file secrets.h terpisah agar tidak bocor ke Git, menyiapkan mekanisme sambung ulang otomatis, dan menampilkan tingkat sinyal RSSI dan alamat IP di layar LCD I2C. LED bawaan bekerja sebagai indikator status. Ini adalah pengelola koneksi kelas produksi.

HTTPClient + API Cuaca ESP32 (dengan Layar OLED SSD1306)
Di proyek ini, kita memakai ESP32 untuk melakukan panggilan REST API ke API cuaca (wttr.in) lewat internet dengan HTTPClient.h. Berbeda dengan versi LCD I2C sebelumnya, di sini kita memakai layar OLED SSD1306 (128×64), yang memungkinkan font besar, ikon vektor, dan tampilan visual yang kaya di satu layar. Tujuh ikon vektor untuk kondisi cuaca (cerah, berawan sebagian, berawan, hujan, salju, badai, berkabut) digambar ketika program berjalan (tanpa file bitmap).

HTTPClient + API Cuaca ESP32 (dengan Layar LCD)
Di proyek ini, kita memakai ESP32 untuk melakukan panggilan REST API ke API cuaca (wttr.in) lewat internet dengan HTTPClient.h. Kita mengurai respons JSON yang diterima dengan ArduinoJson v7 dan menampilkan kota, suhu, kelembapan, cuaca, dan informasi angin di layar LCD I2C dalam bahasa Turki dan Inggris. wttr.in gratis dan tidak butuh kunci API; kamu cukup memasukkan nama kota apa pun untuk mengambil kondisi cuaca terkini kota itu. Data diperbarui otomatis setiap 30 menit.

Deep Sleep ESP32: Hemat Baterai (OLED + Penghitung RTC RAM)
Mode Deep Sleep ESP32 menurunkan konsumsi arus hingga ~10 µA dengan mematikan CPU dan sebagian besar periferal, sehingga sangat penting untuk perangkat IoT bertenaga baterai. Di proyek ini, kita memakai tiga sumber bangun yang berbeda: timer (15 detik), pin eksternal (EXT0, tombol GPIO 33), dan sentuhan (T0/GPIO 4). Variabel yang ditandai RTC_DATA_ATTR dipertahankan selama deep sleep; penghitung boot, penghitung bangun karena timer, dan penghitung bangun karena tombol terus dilacak.

Pencatat Data Suhu ESP32 (DHT22 + RTC DS1307 + microSD + OLED)
Buat pencatat suhu bercap waktu dengan ESP32! Baca suhu/kelembapan DHT22 setiap menit, ambil waktu sebenarnya dari RTC DS1307, dan catat ke kartu microSD sebagai CSV. OLED menampilkan suhu langsung (font besar), kelembapan, cap waktu, dan penghitung catatan. File /temp_log.csv bisa dibuka di Excel, Google Sheets, atau Python pandas; cocok untuk rumah kaca, suhu kamar, data laboratorium, penyimpanan makanan, atau sarang lebah.

Layar LCD I2C ESP32 (16x2)
Di proyek ini, kita menampilkan teks dengan menyambungkan layar LCD karakter 16x2 ke ESP32 lewat protokol I2C. Berkat I2C, kita bisa mengendalikan layar hanya dengan dua pin data (SDA dan SCL), sehingga tidak perlu memboroskan 6+ pin seperti pada sambungan paralel klasik. Kode mencetak pesan tetap di baris pertama dan penghitung yang naik setiap detik di baris kedua. Selain itu, kita menjalankan pemindai I2C di awal untuk mencetak alamat layar ke Serial Monitor (biasanya 0x27).
Artikel lainnya
Aplikasi BlueGrays
Rakit rangkaian ini langkah demi langkah di ponselmu
Skema pengkabelan, kode, dan daftar komponen dalam satu layar. 200+ proyek Arduino, ESP32, dan Pico siap dibuat.
Pindai dengan ponselmu
Arahkan kamera ke kode ini dan toko aplikasi di ponselmu akan terbuka.
atau buka toko aplikasi

