// Versi: v1.1
#include <WiFi.h>
#include <WiFiUdp.h>
#include <RTClib.h>
// Konfigurasi WiFi
const char* ssid = "NET"; // Ganti dengan nama WiFi Anda
const char* password = "M9RHEJN5"; // Ganti dengan password WiFi Anda
// Server NTP eksternal
const char* ntpServer = "time.google.com"; // NTP eksternal
const long gmtOffset_sec = 0; // Offset GMT+0 (untuk NTP)
const int daylightOffset_sec = 0; // Tidak ada DST (Daylight Saving Time)
// RTC
RTC_DS3231 rtc;
// Server NTP lokal
WiFiUDP udp;
const int localNtpPort = 123; // Port default NTP
// Pin untuk LED
#define LED_PIN 13
void setup() {
Serial.begin(115200);
Serial.println("\nMemulai program...");
pinMode(LED_PIN, OUTPUT); // Atur pin LED sebagai output
digitalWrite(LED_PIN, LOW); // Matikan LED saat awal
// Inisialisasi RTC
if (!rtc.begin()) {
Serial.println("RTC tidak ditemukan! Periksa koneksi.");
blinkError(10); // Kedipkan LED 10 detik jika RTC tidak terdeteksi
while (1);
}
if (rtc.lostPower()) {
Serial.println("RTC kehilangan daya, waktu tidak valid.");
} else {
Serial.println("RTC terdeteksi dan berfungsi.");
digitalWrite(LED_PIN, HIGH); // Hidupkan LED selama 5 detik jika RTC terdeteksi
delay(5000);
digitalWrite(LED_PIN, LOW);
}
// Sambungkan ke WiFi
connectToWiFi();
// Sinkronisasi waktu dengan NTP eksternal
if (WiFi.status() == WL_CONNECTED) {
syncTimeFromNTP();
} else {
Serial.println("WiFi tidak terhubung, menggunakan waktu RTC.");
blinkError(10); // Kedipkan LED 10 detik jika tidak ada koneksi WiFi
}
// Jalankan server NTP lokal
startLocalNtpServer();
}
void loop() {
maintainWiFiConnection(); // Periksa dan sambungkan ulang WiFi jika terputus
handleNtpRequest(); // Menangani permintaan NTP
}
// Fungsi untuk menyambungkan ke WiFi
void connectToWiFi() {
Serial.printf("Menghubungkan ke WiFi: %s\n", ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("\nWiFi terhubung!");
Serial.printf("IP ESP32: %s\n", WiFi.localIP().toString().c_str());
}
// Fungsi untuk memeriksa dan menyambungkan ulang WiFi
void maintainWiFiConnection() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("Koneksi WiFi hilang, mencoba menghubungkan ulang...");
WiFi.disconnect();
WiFi.begin(ssid, password);
unsigned long startAttemptTime = millis();
// Tunggu hingga koneksi berhasil atau timeout (10 detik)
while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < 10000) {
delay(500);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi berhasil terhubung kembali!");
Serial.printf("IP ESP32: %s\n", WiFi.localIP().toString().c_str());
} else {
Serial.println("\nGagal menyambungkan kembali ke WiFi.");
}
}
}
// Fungsi untuk sinkronisasi waktu dari NTP eksternal
void syncTimeFromNTP() {
Serial.println("Sinkronisasi waktu dengan server NTP eksternal...");
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Gagal mendapatkan waktu dari NTP!");
return;
}
// Debug waktu yang diperoleh dari NTP
Serial.printf("Waktu dari NTP: %04d-%02d-%02d %02d:%02d:%02d GMT+0\n",
timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
// Simpan waktu ke RTC
rtc.adjust(DateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec));
Serial.println("Waktu berhasil disinkronkan dengan NTP dan disimpan ke RTC.");
// Hidupkan LED selama 3 detik jika berhasil sinkronisasi
digitalWrite(LED_PIN, HIGH);
delay(3000);
digitalWrite(LED_PIN, LOW);
}
// Fungsi untuk memulai server NTP lokal
void startLocalNtpServer() {
if (udp.begin(localNtpPort)) {
Serial.printf("Server NTP lokal berjalan di port %d\n", localNtpPort);
} else {
Serial.println("Gagal memulai server NTP lokal.");
}
// Mendapatkan dan mencetak MAC Address
String macAddress = WiFi.macAddress();
Serial.print("MAC Address ESP32: ");
Serial.println(macAddress);
}
// Fungsi untuk menangani permintaan NTP
void handleNtpRequest() {
int packetSize = udp.parsePacket();
if (packetSize) {
Serial.printf("Permintaan NTP dari: %s:%d\n", udp.remoteIP().toString().c_str(), udp.remotePort());
byte buffer[48]; // Buffer untuk menyimpan data yang diterima
udp.read(buffer, 48);
// Debug log isi buffer permintaan
Serial.println("Paket diterima:");
for (int i = 0; i < packetSize; i++) {
Serial.printf("Byte[%d]: 0x%02X\n", i, buffer[i]);
}
// Cek validitas permintaan
if ((buffer[0] & 0b00111000) == 0b00011000) { // Leap Indicator (LI), Version, Mode
unsigned long currentTime = rtc.now().unixtime(); // Ambil waktu dari RTC
unsigned long timeSince1900 = currentTime + 2208988800UL; // Format NTP
// Isi buffer dengan respon
memset(buffer, 0, 48);
buffer[0] = 0b00100100; // Leap Indicator, Version, Mode
buffer[1] = 1; // Stratum
buffer[2] = 6; // Polling Interval
buffer[3] = 0xEC; // Precision
buffer[12] = 0x49; // Reference Identifier (Ex: ASCII "LOCL")
buffer[13] = 0x4F;
buffer[14] = 0x43;
buffer[15] = 0x4C;
unsigned long txTimestamp = timeSince1900;
buffer[40] = (txTimestamp >> 24) & 0xFF;
buffer[41] = (txTimestamp >> 16) & 0xFF;
buffer[42] = (txTimestamp >> 8) & 0xFF;
buffer[43] = txTimestamp & 0xFF;
// Tambahkan Origin Timestamp, Receive Timestamp, dan Transmit Timestamp
for (int i = 24; i < 48; i += 4) {
buffer[i] = buffer[40];
buffer[i + 1] = buffer[41];
buffer[i + 2] = buffer[42];
buffer[i + 3] = buffer[43];
}
// Kirim respon NTP
if (udp.beginPacket(udp.remoteIP(), udp.remotePort())) {
udp.write(buffer, 48);
udp.endPacket();
Serial.println("Respon NTP berhasil dikirim.");
// Kedipkan LED selama 3 detik
unsigned long startTime = millis();
while (millis() - startTime < 3000) {
digitalWrite(LED_PIN, HIGH);
delay(250);
digitalWrite(LED_PIN, LOW);
delay(250);
}
} else {
Serial.println("Gagal memulai pengiriman respon NTP.");
}
} else {
Serial.printf("Permintaan NTP tidak valid. Byte pertama: 0x%02X\n", buffer[0]);
}
}
}
// Fungsi untuk kedipkan LED saat terjadi kesalahan
void blinkError(int duration) {
unsigned long startTime = millis();
while (millis() - startTime < duration * 1000) {
digitalWrite(LED_PIN, HIGH);
delay(500);
digitalWrite(LED_PIN, LOW);
delay(500);
}
}