#include <TM1637Display.h>
#include <RTClib.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Pin definitions untuk TM1637 displays
#define CLK1 2 // Jam:Menit
#define DIO1 3
#define CLK2 4 // Detik
#define DIO2 5
#define CLK3 6 // Tanggal-Bulan
#define DIO3 7
#define CLK4 8 // Tahun
#define DIO4 9
#define CLK5 10 // Jam:Menit waktu sholat saat ini
#define DIO5 11
#define CLK6 12 // Jam:Menit iqomah
#define DIO6 13
#define CLK7 14 // Detik iqomah
#define DIO7 15
#define CLK8 16 // Jam:Menit countdown ke sholat berikutnya
#define DIO8 17
#define CLK9 18 // Detik countdown
#define DIO9 19
// Pin LED indikator dan buzzer
#define LED_RED 22
#define BUZZER_PIN 23
// Initialize displays
TM1637Display display1(CLK1, DIO1); // Jam:Menit
TM1637Display display2(CLK2, DIO2); // Detik
TM1637Display display3(CLK3, DIO3); // Jam:Menit waktu sholat saat ini
TM1637Display display4(CLK4, DIO4); // Jam:Menit countdown ke sholat berikutnya
TM1637Display display5(CLK5, DIO5); // Detik countdown
TM1637Display display6(CLK6, DIO6); // Jam:Menit iqomah
TM1637Display display7(CLK7, DIO7); // Detik iqomah
TM1637Display display8(CLK8, DIO8); // Tanggal:Bulan
TM1637Display display9(CLK9, DIO9); // Tahun
// Initialize RTC and LCD
RTC_DS3231 rtc;
LiquidCrystal_I2C lcd(0x27, 20, 4); // I2C address 0x27, 20x4 LCD
// Struct untuk waktu sholat
struct PrayerTime {
int hour;
int minute;
};
// Enum untuk jenis waktu sholat
enum PrayerType {
MALAM_1_2, // Malam 1/2
MALAAM_1_2, // Malam 1/2
MALLAM_1_2, // Malam 1/2
TENGAH_MALLAM, // Tengah Malam
TENGAH_MALAM, // Tengah Malam
TENGGAH_MALAM, // Tengah Malam
MALAM_2_3, // Malam 2/3
TAHAJUD, // Tahajud
SAHUR, // Sahur
SAHR, // Sahur
IMSYAK, // Imsyak
SUBUH, // Subuh
TERBIT, // Terbit
ISHRAQ, // Ishraq
ISYRAQ, // Ishraq
DHUHA, // Dhuha
DZUHUR_JUMAT, // Dzuhur/Jumat
ASHAR, // Ashar
MAGHRIB, // Maghrib
ISYA, // Isya
PRAYER_COUNT
};
// Nama-nama waktu sholat
const char* prayerNames[] = {
"MALAM 1/2", "MALAM 1/2", "MALAM 1/2", "TENGAH MALAM", "TENGAH MALAM", "TENGAH MALAM", "MALAM 2/3", "TAHAJUD", "SAHUR", "SAHUR",
"IMSYAK", "SUBUH", "TERBIT", "ISHRAQ", "ISHRAQ", "DHUHA",
"DZUHUR/JUMAT", "ASHAR", "MAGHRIB", "ISYA"
};
// Nama bulan Hijriah
const char* hijriMonths[] = {
"Muharam", "Safar", "Rabiul Awwal", "Rabiul Akhir",
"Jumadil Awwal", "Jumadil Akhir", "Rajab", "Syaban",
"Ramadhan", "Syawwal", "Dzulqaidah", "Dzulhijjah"
};
// Waktu sholat (contoh untuk Jakarta)
PrayerTime prayerTimes[PRAYER_COUNT] = {
{20, 42}, // MALAM_1_2
{21, 42}, // MALAM_1_2
{22, 42}, // MALAM_1_2
{23, 26}, // TENGAH_MALAM
{0, 26}, // TENGAH_MALAM
{1, 26}, // TENGAH_MALAM
{2, 10}, // MALAM_2_3
{3, 10}, // TAHAJUD
{3, 36}, // SAHUR
{3, 56}, // SAHUR
{4, 29}, // IMSYAK
{4, 39}, // SUBUH
{5, 57}, // TERBIT
{6, 27}, // ISHRAQ
{6, 47}, // ISHRAQ
{7, 7}, // DHUHA
{11, 58}, // DZUHUR/JUMAT
{15, 17}, // ASHAR
{17, 54}, // MAGHRIB
{19, 3} // ISYA
};
// Waktu iqomah (menit setelah adzan)
int iqomahDelayMinutes[] = {
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, // Tidak ada iqomah untuk waktu malam
11, // SUBUH - 11 menit setelah adzan
11,11, 11, 11, // Tidak ada iqomah untuk TERBIT, ISHRAQ, DHUHA
11, // DZUHUR/JUMAT - 11 menit setelah adzan
11, // ASHAR - 11 menit setelah adzan
11, // MAGHRIB - 11 menit setelah adzan
11 // ISYA - 11 menit setelah adzan
};
// Contoh tanggal Hijriah (harus disesuaikan dengan kalender Hijriah yang akurat)
struct HijriDate {
int day;
int month; // 1-12
int year;
};
// Variables
DateTime now;
HijriDate currentHijriDate = {21, 2, 1447}; // 21 Safar 1447 (contoh)
int currentPrayerIndex = -1;
int nextPrayerIndex = -1;
bool isIqomahTime = false;
unsigned long lastUpdate = 0;
unsigned long lastLCDUpdate = 0;
// Variables untuk buzzer dan LED yang baru
bool isPrayerTimeActive = false;
bool isIqomahCountdownActive = false;
int buzzerBeepCount = 0;
int ledBlinkCount = 0;
unsigned long buzzerLastBeep = 0;
unsigned long ledLastBlink = 0;
bool buzzerState = false;
bool ledState = false;
bool buzzerSequenceActive = false;
bool ledSequenceActive = false;
void setup() {
Serial.begin(9600);
// Initialize displays
display1.setBrightness(0x0f);
display2.setBrightness(0x0f);
display3.setBrightness(0x0f);
display4.setBrightness(0x0f);
display5.setBrightness(0x0f);
display6.setBrightness(0x0f);
display7.setBrightness(0x0f);
display8.setBrightness(0x0f);
display9.setBrightness(0x0f);
// Initialize LED and Buzzer
pinMode(LED_RED, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(LED_RED, LOW);
digitalWrite(BUZZER_PIN, LOW);
// Initialize LCD
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Islamic Clock");
lcd.setCursor(0, 1);
lcd.print("Initializing...");
delay(2000);
// Initialize RTC
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("RTC Error!");
while (1);
}
// Set RTC to compile time if needed
if (rtc.lostPower()) {
Serial.println("RTC lost power, setting time!");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
Serial.println("Islamic Prayer Time System Started");
lcd.clear();
}
void loop() {
now = rtc.now();
// Update displays every second
if (millis() - lastUpdate >= 1000) {
updateDisplays();
updatePrayerStatus();
lastUpdate = millis();
}
// Update LCD every 5 seconds
if (millis() - lastLCDUpdate >= 5000) {
updateLCD();
lastLCDUpdate = millis();
}
// Handle buzzer dan LED berdasarkan kondisi
handleBuzzerAndLED();
delay(50); // Reduced delay for better responsiveness
}
void updateDisplays() {
// Display 1: Jam:Menit
int timeValue = now.hour() * 100 + now.minute();
display1.showNumberDecEx(timeValue, 0b01000000, true);
// Display 2: Detik
display2.showNumberDec(now.second(), true);
// Display 3: Tanggal:Bulan
int dateValue = now.day() * 100 + now.month();
display3.showNumberDecEx(dateValue, 0b01000000, true);
// Display 4: Tahun
display4.showNumberDec(now.year() % 100, true);
// Display 5: Jam:Menit waktu sholat saat ini
if (currentPrayerIndex >= 0) {
int currentPrayerValue = prayerTimes[currentPrayerIndex].hour * 100 +
prayerTimes[currentPrayerIndex].minute;
display5.showNumberDecEx(currentPrayerValue, 0b01000000, true);
} else {
display5.showNumberDec(0, true);
}
// Display 8 & 9: Countdown ke sholat berikutnya
if (nextPrayerIndex >= 0) {
long countdown = getCountdownSeconds(nextPrayerIndex);
if (countdown > 0) {
int countdownHours = countdown / 3600;
int countdownMinutes = (countdown % 3600) / 60;
int countdownSeconds = countdown % 60;
// Display 8: Jam:Menit countdown
int countdownHM = countdownHours * 100 + countdownMinutes;
display8.showNumberDecEx(countdownHM, 0b01000000, true);
// Display 9: Detik countdown
display9.showNumberDec(countdownSeconds, true);
} else {
display8.showNumberDec(0, true);
display9.showNumberDec(0, true);
}
}
// Display 6 & 7: Iqomah time
if (isIqomahTime && hasIqomah(currentPrayerIndex)) {
PrayerTime iqomahTime = getIqomahTime(currentPrayerIndex);
int iqomahHM = iqomahTime.hour * 100 + iqomahTime.minute;
display6.showNumberDecEx(iqomahHM, 0b01000000, true);
// Countdown to iqomah
long iqomahCountdown = getIqomahCountdownSeconds(currentPrayerIndex);
if (iqomahCountdown > 0) {
display7.showNumberDec(iqomahCountdown, true);
} else {
display7.showNumberDec(0, true);
}
} else {
display6.clear();
display7.clear();
}
}
void updateLCD() {
lcd.clear();
// Baris pertama: Tanggal Hijriah
lcd.setCursor(0, 0);
lcd.print(currentHijriDate.day);
lcd.print(" ");
lcd.print(hijriMonths[currentHijriDate.month - 1]);
// Baris kedua: Tahun Hijriah dan status sholat
lcd.setCursor(0, 1);
lcd.print(currentHijriDate.year);
lcd.print("H ");
if (currentPrayerIndex >= 0) {
lcd.print(prayerNames[currentPrayerIndex]);
} else {
lcd.print("Normal");
}
}
void updatePrayerStatus() {
int currentMinutes = now.hour() * 60 + now.minute();
int currentSeconds = now.second();
// Find current prayer time
currentPrayerIndex = -1;
nextPrayerIndex = -1;
isPrayerTimeActive = false;
isIqomahCountdownActive = false;
for (int i = 0; i < PRAYER_COUNT; i++) {
int prayerMinutes = prayerTimes[i].hour * 60 + prayerTimes[i].minute;
// Check if it's exactly prayer time (same minute)
if (currentMinutes == prayerMinutes) {
currentPrayerIndex = i;
isPrayerTimeActive = true;
// Find next prayer
if (i < PRAYER_COUNT - 1) {
nextPrayerIndex = i + 1;
} else {
nextPrayerIndex = 0; // Next day's first prayer
}
break;
}
// Check for iqomah countdown (5-0 seconds before iqomah)
if (hasIqomah(i)) {
int iqomahTotalMinutes = prayerMinutes + iqomahDelayMinutes[i];
int iqomahHour = (iqomahTotalMinutes / 60) % 24;
int iqomahMinute = iqomahTotalMinutes % 60;
if (currentMinutes == iqomahTotalMinutes && currentSeconds >= 55) {
// 5-0 seconds before iqomah
currentPrayerIndex = i;
isIqomahCountdownActive = true;
break;
}
}
// Find current prayer period for normal operations
if (currentMinutes >= prayerMinutes &&
(i == PRAYER_COUNT - 1 || currentMinutes < (prayerTimes[i + 1].hour * 60 + prayerTimes[i + 1].minute))) {
if (currentPrayerIndex == -1) { // Only set if not already set by prayer time or iqomah
currentPrayerIndex = i;
// Find next prayer
if (i < PRAYER_COUNT - 1) {
nextPrayerIndex = i + 1;
} else {
nextPrayerIndex = 0; // Next day's first prayer
}
}
}
}
// Check if it's iqomah time (for display purposes)
if (currentPrayerIndex >= 0 && hasIqomah(currentPrayerIndex)) {
long timeSincePrayer = currentMinutes - (prayerTimes[currentPrayerIndex].hour * 60 + prayerTimes[currentPrayerIndex].minute);
isIqomahTime = (timeSincePrayer >= 0 && timeSincePrayer < iqomahDelayMinutes[currentPrayerIndex]);
} else {
isIqomahTime = false;
}
// Debug output
if (isPrayerTimeActive) {
Serial.println("PRAYER TIME ACTIVE!");
}
if (isIqomahCountdownActive) {
Serial.println("IQOMAH COUNTDOWN ACTIVE!");
}
}
void handleBuzzerAndLED() {
// Reset sequences when conditions change
static bool lastPrayerTimeActive = false;
static bool lastIqomahCountdownActive = false;
if (isPrayerTimeActive != lastPrayerTimeActive || isIqomahCountdownActive != lastIqomahCountdownActive) {
// Reset all sequences when state changes
buzzerSequenceActive = false;
ledSequenceActive = false;
buzzerBeepCount = 0;
ledBlinkCount = 0;
digitalWrite(BUZZER_PIN, LOW);
digitalWrite(LED_RED, LOW);
buzzerState = false;
ledState = false;
}
lastPrayerTimeActive = isPrayerTimeActive;
lastIqomahCountdownActive = isIqomahCountdownActive;
// Handle Prayer Time (20 beeps and 20 blinks)
if (isPrayerTimeActive) {
if (!buzzerSequenceActive && buzzerBeepCount < 20) {
buzzerSequenceActive = true;
buzzerLastBeep = millis();
}
if (!ledSequenceActive && ledBlinkCount < 20) {
ledSequenceActive = true;
ledLastBlink = millis();
}
// Handle buzzer sequence (20 beeps)
if (buzzerSequenceActive && buzzerBeepCount < 20) {
if (!buzzerState && millis() - buzzerLastBeep >= 200) { // 200ms interval between beeps
digitalWrite(BUZZER_PIN, HIGH);
buzzerState = true;
buzzerLastBeep = millis();
} else if (buzzerState && millis() - buzzerLastBeep >= 100) { // 100ms beep duration
digitalWrite(BUZZER_PIN, LOW);
buzzerState = false;
buzzerBeepCount++;
buzzerLastBeep = millis();
if (buzzerBeepCount >= 20) {
buzzerSequenceActive = false;
}
}
}
// Handle LED sequence (20 blinks)
if (ledSequenceActive && ledBlinkCount < 20) {
if (!ledState && millis() - ledLastBlink >= 200) { // 200ms interval between blinks
digitalWrite(LED_RED, HIGH);
ledState = true;
ledLastBlink = millis();
} else if (ledState && millis() - ledLastBlink >= 100) { // 100ms blink duration
digitalWrite(LED_RED, LOW);
ledState = false;
ledBlinkCount++;
ledLastBlink = millis();
if (ledBlinkCount >= 20) {
ledSequenceActive = false;
}
}
}
}
// Handle Iqomah Countdown (6 beeps and 6 blinks for 5-0 seconds before iqomah)
else if (isIqomahCountdownActive) {
if (!buzzerSequenceActive && buzzerBeepCount < 6) {
buzzerSequenceActive = true;
buzzerLastBeep = millis();
}
if (!ledSequenceActive && ledBlinkCount < 6) {
ledSequenceActive = true;
ledLastBlink = millis();
}
// Handle buzzer sequence (6 beeps)
if (buzzerSequenceActive && buzzerBeepCount < 6) {
if (!buzzerState && millis() - buzzerLastBeep >= 150) { // 150ms interval for faster beeps
digitalWrite(BUZZER_PIN, HIGH);
buzzerState = true;
buzzerLastBeep = millis();
} else if (buzzerState && millis() - buzzerLastBeep >= 100) { // 100ms beep duration
digitalWrite(BUZZER_PIN, LOW);
buzzerState = false;
buzzerBeepCount++;
buzzerLastBeep = millis();
if (buzzerBeepCount >= 6) {
buzzerSequenceActive = false;
}
}
}
// Handle LED sequence (6 blinks)
if (ledSequenceActive && ledBlinkCount < 6) {
if (!ledState && millis() - ledLastBlink >= 150) { // 150ms interval for faster blinks
digitalWrite(LED_RED, HIGH);
ledState = true;
ledLastBlink = millis();
} else if (ledState && millis() - ledLastBlink >= 100) { // 100ms blink duration
digitalWrite(LED_RED, LOW);
ledState = false;
ledBlinkCount++;
ledLastBlink = millis();
if (ledBlinkCount >= 6) {
ledSequenceActive = false;
}
}
}
}
// Turn off buzzer and LED when no active sequences
else {
digitalWrite(BUZZER_PIN, LOW);
digitalWrite(LED_RED, LOW);
buzzerSequenceActive = false;
ledSequenceActive = false;
buzzerBeepCount = 0;
ledBlinkCount = 0;
buzzerState = false;
ledState = false;
}
}
long getCountdownSeconds(int prayerIndex) {
if (prayerIndex < 0) return 0;
int currentTotalSeconds = now.hour() * 3600 + now.minute() * 60 + now.second();
int prayerTotalSeconds = prayerTimes[prayerIndex].hour * 3600 + prayerTimes[prayerIndex].minute * 60;
long countdown = prayerTotalSeconds - currentTotalSeconds;
// If prayer is tomorrow
if (countdown < 0) {
countdown += 24 * 3600; // Add 24 hours
}
return countdown;
}
bool hasIqomah(int prayerIndex) {
if (prayerIndex < 0 || prayerIndex >= PRAYER_COUNT) return false;
return iqomahDelayMinutes[prayerIndex] > 0;
}
PrayerTime getIqomahTime(int prayerIndex) {
PrayerTime iqomahTime = {0, 0};
if (!hasIqomah(prayerIndex)) return iqomahTime;
int totalMinutes = prayerTimes[prayerIndex].hour * 60 + prayerTimes[prayerIndex].minute + iqomahDelayMinutes[prayerIndex];
iqomahTime.hour = (totalMinutes / 60) % 24;
iqomahTime.minute = totalMinutes % 60;
return iqomahTime;
}
long getIqomahCountdownSeconds(int prayerIndex) {
if (!hasIqomah(prayerIndex)) return 0;
PrayerTime iqomahTime = getIqomahTime(prayerIndex);
int currentTotalSeconds = now.hour() * 3600 + now.minute() * 60 + now.second();
int iqomahTotalSeconds = iqomahTime.hour * 3600 + iqomahTime.minute * 60;
return iqomahTotalSeconds - currentTotalSeconds;
}