#include <WiFi.h>
#include <time.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// ===== KONFIGURASI JARINGAN =====
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// ===== KONFIGURASI NTP =====
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 7 * 3600; // GMT+7 (WIB)
const int daylightOffset_sec = 0;
// ===== KONFIGURASI LCD I2C =====
#define I2C_ADDR 0x27
#define LCD_COLUMNS 16
#define LCD_ROWS 2
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_ROWS);
// ===== STRUKTUR DATA KALENDER =====
struct JavaneseDate {
int day; // Hari pasaran (0-4)
int cycleDay; // Hari dalam siklus 5-hari
const char* pasaranName;
int wukuDay; // Hari dalam wuku (1-30)
int wukuWeek; // Minggu dalam wuku (1-30)
};
// ===== NAMA HARI PASARAN JAWA =====
const char* pasaranNames[] = {
"Legi", "Pahing", "Pon", "Wage", "Kliwon"
};
// ===== NAMA HARI MASEHI =====
const char* dayNames[] = {
"Minggu", "Senin", "Selasa", "Rabu",
"Kamis", "Jumat", "Sabtu"
};
// ===== NAMA WUKU (30 WUKU) =====
const char* wukuNames[] = {
"Sinta", "Landep", "Wukir", "Kurantil", "Tolu",
"Gumbreg", "Wariga", "Warigadean", "Julungwangi", "Sungsang",
"Galungan", "Kuningan", "Langkir", "Mandasia", "Julungpujut",
"Pahang", "Kuruwelut", "Marakeh", "Tambir", "Medangkungan",
"Maktal", "Wuye", "Manahil", "Prangbakat", "Bala",
"Wugu", "Wayang", "Kulawu", "Dukut", "Watugunung"
};
// ===== TANGGAL AWAL REFERENSI =====
// Referensi: 1 Januari 2023 adalah hari Minggu Legi
struct tm referenceDate = {
.tm_year = 123, // 2023 - 1900
.tm_moon = 0, // Januari (0-based)
.tm_mday = 1,
.tm_hour = 0,
.tm_min = 0,
.tm_sec = 0,
.tm_wday = 0, // Minggu
.tm_yday = 0,
.tm_isdst = 0 };
const int referencePasaran = 0; // 0 = Legi
// ===== VARIABEL GLOBAL =====
struct tm timeinfo;
char timeString[9];
char dateString[11];
char dayString[10];
char pasaranString[10];
char wukuString[20];
// ===== DEKLARASI FUNGSI =====
void connectToWiFi();
void waitForTimeSync();
void formatTime();
void updateDisplay();
JavaneseDate calculateJavaneseDate(struct tm masehiDate);
int daysBetweenDates(struct tm date1, struct tm date2);
void printJavaneseCalendarInfo(struct tm date);
void setupLCD();
void setup() {
Serial.begin(115200);
// Inisialisasi LCD
setupLCD();
// Koneksi ke WiFi
connectToWiFi();
// Konfigurasi waktu
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
// Tunggu sinkronisasi
waitForTimeSync();
// Tampilkan pesan selesai
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("NTP Sync OK!");
delay(2000);
}
void loop() {
// Ambil waktu saat ini
if (getLocalTime(&timeinfo)) {
// Format waktu
formatTime();
// Hitung kalender Jawa
JavaneseDate javaneseDate = calculateJavaneseDate(timeinfo);
// Salin nama pasaran ke string
strcpy(pasaranString, javaneseDate.pasaranName);
// Tentukan wuku
int wukuIndex = (javaneseDate.wukuWeek - 1) % 30;
strcpy(wukuString, wukuNames[wukuIndex]);
// Tampilkan pada LCD
updateDisplay();
// Debug ke Serial Monitor (opsional)
static unsigned long lastSerialPrint = 0;
if (millis() - lastSerialPrint > 5000) {
printJavaneseCalendarInfo(timeinfo);
lastSerialPrint = millis();
}
} else {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Time Error!");
}
delay(1000); // Update setiap 1 detik
}
// ===== FUNGSI SETUP LCD =====
void setupLCD() {
Wire.begin();
lcd.init();
lcd.backlight();
lcd.clear();
// Tampilkan pesan awal
lcd.setCursor(0, 0);
lcd.print("Kalender Jawa");
lcd.setCursor(0, 1);
lcd.print("Initializing...");
delay(2000);
}
// ===== FUNGSI KONEKSI WIFI =====
void connectToWiFi() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Connecting WiFi");
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
lcd.setCursor(attempts % 16, 1);
lcd.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("WiFi Connected");
delay(1000);
} else {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("WiFi Failed!");
delay(2000);
ESP.restart();
}
}
// ===== FUNGSI SINKRONISASI WAKTU =====
void waitForTimeSync() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Syncing time...");
int attempts = 0;
while (!getLocalTime(&timeinfo) && attempts < 20) {
delay(500);
lcd.setCursor(attempts % 16, 1);
lcd.print(".");
attempts++;
}
}
// ===== FUNGSI FORMAT WAKTU =====
void formatTime() {
// Format jam
strftime(timeString, sizeof(timeString), "%H:%M:%S", &timeinfo);
// Format tanggal
strftime(dateString, sizeof(dateString), "%d/%m/%Y", &timeinfo);
// Format nama hari
int dayOfWeek = timeinfo.tm_wday;
strcpy(dayString, dayNames[dayOfWeek]);
}
// ===== FUNGSI HITUNG KALENDER JAWA =====
JavaneseDate calculateJavaneseDate(struct tm masehiDate) {
JavaneseDate result;
// 1. Hitung selisih hari dari tanggal referensi
int daysDiff = daysBetweenDates(referenceDate, masehiDate);
// 2. Hitung hari pasaran (siklus 5 hari)
int pasaranDay = (referencePasaran + daysDiff) % 5;
if (pasaranDay < 0) pasaranDay += 5; // Handle nilai negatif
result.day = pasaranDay;
result.cycleDay = (pasaranDay + 1); // 1-5
result.pasaranName = pasaranNames[pasaranDay];
// 3. Hitung Wuku (siklus 210 hari = 30 wuku × 7 hari)
// Wuku dimulai dari Wuku Sinta
int totalDaysFromStart = daysDiff + 1; // +1 untuk penyesuaian
int wukuCycleDay = (totalDaysFromStart - 1) % 210;
if (wukuCycleDay < 0) wukuCycleDay += 210;
// Setiap wuku punya 7 hari
result.wukuWeek = (wukuCycleDay / 7) + 1; // Minggu ke- dalam wuku (1-30)
result.wukuDay = (wukuCycleDay % 7) + 1; // Hari ke- dalam minggu wuku (1-7)
return result;
}
// ===== FUNGSI HITUNG SELISIH HARI =====
int daysBetweenDates(struct tm date1, struct tm date2) {
// Konversi ke time_t (detik sejak 1 Jan 1970)
time_t time1 = mktime(&date1);
time_t time2 = mktime(&date2);
// Hitung selisih dalam hari
double diffSeconds = difftime(time2, time1);
int diffDays = (int)(diffSeconds / (60 * 60 * 24));
return diffDays;
}
// ===== FUNGSI TAMPILAN LCD =====
void updateDisplay() {
static bool showDetail = false;
static unsigned long lastToggle = 0;
// Ganti tampilan setiap 10 detik
if (millis() - lastToggle > 10000) {
showDetail = !showDetail;
lastToggle = millis();
}
lcd.clear();
if (!showDetail) {
// Mode 1: Tampilan standar (Waktu + Pasaran)
lcd.setCursor(0, 0);
lcd.print(dayString);
lcd.print(" ");
lcd.print(pasaranString);
// Rata kanan tanggal
int datePos = LCD_COLUMNS - strlen(dateString);
lcd.setCursor(datePos, 0);
lcd.print(dateString);
// Baris 2: Waktu
lcd.setCursor(0, 1);
lcd.print(timeString);
// Tampilkan wuku singkat
lcd.setCursor(10, 1);
lcd.print("Wk:");
lcd.print(wukuString[0]); // Huruf pertama wuku
} else {
// Mode 2: Detail kalender Jawa
lcd.setCursor(0, 0);
lcd.print("Pasaran: ");
lcd.print(pasaranString);
lcd.setCursor(0, 1);
lcd.print("Wuku: ");
// Potong nama wuku jika terlalu panjang
if (strlen(wukuString) > 9) {
char shortWuku[10];
strncpy(shortWuku, wukuString, 9);
shortWuku[9] = '\0';
lcd.print(shortWuku);
} else {
lcd.print(wukuString);
}
}
}
// ===== FUNGSI DEBUG KE SERIAL =====
void printJavaneseCalendarInfo(struct tm date) {
JavaneseDate javanese = calculateJavaneseDate(date);
Serial.println("\n=== KALENDER JAWA ===");
Serial.printf("Tanggal Masehi: %02d/%02d/%04d\n",
date.tm_mday, date.tm_mon + 1, date.tm_year + 1900);
Serial.printf("Hari: %s\n", dayNames[date.tm_wday]);
Serial.printf("Pasaran: %s (Hari ke-%d dalam siklus)\n",
javanese.pasaranName, javanese.cycleDay);
Serial.printf("Wuku: %s (Minggu ke-%d, Hari ke-%d)\n",
wukuString, javanese.wukuWeek, javanese.wukuDay);
Serial.println("====================\n");
}
// ===== FUNGSI TAMBAHAN: PERHITUNGAN NEptu (JIKA DIBUTUHKAN) =====
int calculateNeptu(const char* pasaran) {
// Nilai neptu untuk setiap pasaran
if (strcmp(pasaran, "Legi") == 0) return 5;
if (strcmp(pasaran, "Pahing") == 0) return 9;
if (strcmp(pasaran, "Pon") == 0) return 7;
if (strcmp(pasaran, "Wage") == 0) return 4;
if (strcmp(pasaran, "Kliwon") == 0) return 8;
return 0;
}
int calculateNeptuDay(const char* hari) {
// Nilai neptu untuk setiap hari
if (strcmp(hari, "Minggu") == 0) return 5;
if (strcmp(hari, "Senin") == 0) return 4;
if (strcmp(hari, "Selasa") == 0) return 3;
if (strcmp(hari, "Rabu") == 0) return 7;
if (strcmp(hari, "Kamis") == 0) return 8;
if (strcmp(hari, "Jumat") == 0) return 6;
if (strcmp(hari, "Sabtu") == 0) return 9;
return 0;
}