Arduino Prayer Times Modifikasi
Saya mengajak teman-teman untuk dapat membuat jam sholat secara mandiri menggunakan Arduino. Di postingan saya memberikan kode program dasar untuk dapat menampilkan jadwal atau waktu sholat secara otomatis hanya dengan memasukkan koordinat, zona waktu, tanggal, bulan serta tahun seperti yang ditunjukkan pada gambar berikut ini:
Library
Saya mendapatkan sebuah library yang sangat bagus di Github bernama Arduino-Prayer-Times yang di posting oleh asmaklad (tidak tahu apakah nama seseorang). Library merupakan hasil konversi dari http://praytimes.org/manual/. Terima kasih prayertimes.org dan asmaklad!
Setelah saya mencoba library, saya membandingkan dengan waktu sholat KEMENAG dan memiliki selisih perbedaan. Dari itu, saya sedikit memodifikasi library Prayer Times tersebut. Link Library yang sudah di edit dapat teman-teman download dengan klik tombol download berikut:
Modifikasi Library
Setelah anda mendownload library dan memasukkan ke Arduino IDE, sekarang akan ada 2 file library tersebut yaitu PrayerTimes.cpp dan PrayerTimes.h. Saya memodifikasi file tersebut. Silahkan anda copy paste ke masing-masing file tersebut.
Modifikasi PrayerTimes.cpp
#include "Arduino.h"
#include "PrayerTimes.h"
double latitude;
double longitude;
double timezone;
double elevation;
double julian_date;
MethodConfig method_params[CalculationMethodsCount];
CalculationMethod calc_method;
JuristicMethod asr_juristic;
AdjustingMethod adjust_high_lats;
double dhuhr_minutes;
/* ---------------------- Trigonometric Functions ----------------------- */
static double dsin(double d) { return sin(deg2rad(d)); }
static double dcos(double d) { return cos(deg2rad(d)); }
static double dtan(double d) { return tan(deg2rad(d)); }
static double darcsin(double x) { return rad2deg(asin(x)); }
static double darccos(double x) { return rad2deg(acos(x)); }
static double darctan(double x) { return rad2deg(atan(x)); }
static double darctan2(double y, double x) { return rad2deg(atan2(y, x)); }
static double darccot(double x) { return rad2deg(atan(1.0 / x)); }
static double deg2rad(double d) { return d * M_PI / 180.0; }
static double rad2deg(double r) { return r * 180.0 / M_PI; }
static double fix_angle(double a)
{
a = a - 360.0 * floor(a / 360.0);
a = a < 0.0 ? a + 360.0 : a;
return a;
}
static double fix_hour(double a)
{
a = a - 24.0 * floor(a / 24.0);
a = a < 0.0 ? a + 24.0 : a;
return a;
}
/* ALGORITMA JULIAN DATE - MEEUS (ASTRONOMICAL ALGORITHMS) */
double get_julian_date(int year, int month, int day)
{
// Adjust untuk Januari dan Februari
if (month <= 2) {
year -= 1;
month += 12;
}
// Koreksi Gregorian calendar (setelah 15 Oktober 1582)
int a = year / 100;
int b = 2 - a + (a / 4);
// Formula Meeus - presisi tinggi
double jd = floor(365.25 * (year + 4716)) +
floor(30.6001 * (month + 1)) +
day + b - 1524.5;
return jd;
}
/* POSISI MATAHARI - IMPROVED PRECISION */
DoublePair sun_position(double jd)
{
// Julian centuries dari J2000.0
double T = (jd - 2451545.0) / 36525.0;
// Mean longitude matahari (derajat)
double L0 = 280.46646 + 36000.76983 * T + 0.0003032 * T * T;
L0 = fix_angle(L0);
// Mean anomaly matahari (derajat)
double M = 357.52911 + 35999.05029 * T - 0.0001537 * T * T;
M = fix_angle(M);
// Eccentricity orbit bumi
double e = 0.016708634 - 0.000042037 * T - 0.0000001267 * T * T;
// Sun's equation of center
double C = (1.914602 - 0.004817 * T - 0.000014 * T * T) * dsin(M)
+ (0.019993 - 0.000101 * T) * dsin(2 * M)
+ 0.000289 * dsin(3 * M);
// True longitude matahari
double L = L0 + C;
// True anomaly
double v = M + C;
// Radius vector (AU) - tidak dipakai tapi lengkap
double R = (1.000001018 * (1 - e * e)) / (1 + e * dcos(v));
// Obliquity of ecliptic (kemiringan sumbu bumi)
double epsilon = 23.439291 - 0.0130042 * T - 0.00000164 * T * T + 0.000000504 * T * T * T;
// Apparent longitude (koreksi nutasi)
double omega = 125.04 - 1934.136 * T;
double lambda = L - 0.00569 - 0.00478 * dsin(omega);
// Declination matahari (derajat)
double delta = darcsin(dsin(epsilon) * dsin(lambda));
// Right Ascension (jam)
double alpha = darctan2(dcos(epsilon) * dsin(lambda), dcos(lambda));
alpha = alpha / 15.0; // konversi ke jam
alpha = fix_hour(alpha);
// Equation of time (jam)
double EqT = (L0 / 15.0) - alpha;
EqT = fix_hour(EqT + 12.0) - 12.0; // normalize ke ±12 jam
if (EqT > 12.0) EqT -= 24.0;
if (EqT < -12.0) EqT += 24.0;
DoublePair result = {delta, EqT};
return result;
}
void set_asr_method(JuristicMethod method_id)
{
asr_juristic = method_id;
}
void set_high_lats_adjust_method(AdjustingMethod method_id)
{
adjust_high_lats = method_id;
}
double sun_declination(double jd)
{
return sun_position(jd).first;
}
double equation_of_time(double jd)
{
return sun_position(jd).second;
}
double compute_mid_day(double _t)
{
double t = equation_of_time(julian_date + _t);
double z = fix_hour(12 - t);
return z;
}
double compute_time(double g, double t)
{
double d = sun_declination(julian_date + t);
double z = compute_mid_day(t);
double v = 1.0 / 15.0 * darccos((-dsin(g) - dsin(d) * dsin(latitude)) / (dcos(d) * dcos(latitude)));
return z + (g > 90.0 ? -v : v);
}
double compute_asr(int step, double t)
{
double d = sun_declination(julian_date + t);
double g = -darccot(step + dtan(fabs(latitude - d)));
return compute_time(g, t);
}
void compute_times(double times[])
{
day_portion(times);
times[Fajr] = compute_time(180.0 - method_params[calc_method].fajr_angle, times[Fajr]);
times[Sunrise] = compute_time(180.0 - 0.833, times[Sunrise]);
times[Dhuhr] = compute_mid_day(times[Dhuhr]);
times[Asr] = compute_asr(1 + asr_juristic, times[Asr]);
times[Sunset] = compute_time(0.833, times[Sunset]);
times[Maghrib] = compute_time(method_params[calc_method].maghrib_value, times[Maghrib]);
times[Isha] = compute_time(method_params[calc_method].isha_value, times[Isha]);
// Koreksi elevasi (dip angle)
if (elevation > 0) {
// Formula: correction = 1.76 * sqrt(h) arcminutes
// Convert ke jam: arcminutes / 60 / 60
double elev_corr = (1.76 * sqrt(elevation)) / 3600.0;
times[Sunrise] -= elev_corr; // Sunrise lebih awal
times[Sunset] += elev_corr; // Sunset lebih lambat
times[Maghrib] += elev_corr; // Maghrib ikut Sunset
}
}
void compute_day_times(double times[])
{
double default_times[] = {5, 6, 12, 13, 18, 18, 18};
for (int i = 0; i < TimesCount; ++i)
times[i] = default_times[i];
for (int i = 0; i < NUM_ITERATIONS; ++i)
compute_times(times);
adjust_times(times);
}
static double time_diff(double time1, double time2)
{
return fix_hour(time2 - time1);
}
double night_portion(double angle)
{
switch (adjust_high_lats)
{
case AngleBased: return angle / 60.0;
case MidNight: return 1.0 / 2.0;
case OneSeventh: return 1.0 / 7.0;
default: return 0;
}
}
void adjust_high_lat_times(double times[])
{
double night_time = time_diff(times[Sunset], times[Sunrise]);
// Adjust Fajr
double fajr_diff = night_portion(method_params[calc_method].fajr_angle) * night_time;
if (isnan(times[Fajr]) || time_diff(times[Fajr], times[Sunrise]) > fajr_diff)
times[Fajr] = times[Sunrise] - fajr_diff;
// Adjust Isha
double isha_angle = method_params[calc_method].isha_is_minutes ? 18.0 : method_params[calc_method].isha_value;
double isha_diff = night_portion(isha_angle) * night_time;
if (isnan(times[Isha]) || time_diff(times[Sunset], times[Isha]) > isha_diff)
times[Isha] = times[Sunset] + isha_diff;
// Adjust Maghrib
double maghrib_angle = method_params[calc_method].maghrib_is_minutes ? 4.0 : method_params[calc_method].maghrib_value;
double maghrib_diff = night_portion(maghrib_angle) * night_time;
if (isnan(times[Maghrib]) || time_diff(times[Sunset], times[Maghrib]) > maghrib_diff)
times[Maghrib] = times[Sunset] + maghrib_diff;
}
void adjust_times(double times[])
{
for (int i = 0; i < TimesCount; ++i)
times[i] += timezone - longitude / 15.0;
times[Dhuhr] += dhuhr_minutes / 60.0;
if (method_params[calc_method].maghrib_is_minutes)
times[Maghrib] = times[Sunset] + method_params[calc_method].maghrib_value / 60.0;
if (method_params[calc_method].isha_is_minutes)
times[Isha] = times[Maghrib] + method_params[calc_method].isha_value / 60.0;
if (adjust_high_lats != None)
adjust_high_lat_times(times);
}
void day_portion(double times[])
{
for (int i = 0; i < TimesCount; ++i)
times[i] /= 24.0;
}
void get_prayer_times(int year, int month, int day,
double _latitude, double _longitude,
double _timezone,
double _elevation,
double times[])
{
latitude = _latitude;
longitude = _longitude;
timezone = _timezone;
elevation = _elevation;
julian_date = get_julian_date(year, month, day) - longitude / (15.0 * 24.0);
compute_day_times(times);
}
void set_calc_method(CalculationMethod method_id)
{
calc_method = method_id;
}
void set_fajr_angle(double angle)
{
method_params[Custom].fajr_angle = angle;
calc_method = Custom;
}
void set_maghrib_angle(double angle)
{
method_params[Custom].maghrib_is_minutes = false;
method_params[Custom].maghrib_value = angle;
calc_method = Custom;
}
void set_isha_angle(double angle)
{
method_params[Custom].isha_is_minutes = false;
method_params[Custom].isha_value = angle;
calc_method = Custom;
}
void set_dhuhr_minutes(double minutes)
{
dhuhr_minutes = minutes;
}
void set_maghrib_minutes(double minutes)
{
method_params[Custom].maghrib_is_minutes = true;
method_params[Custom].maghrib_value = minutes;
calc_method = Custom;
}
void set_isha_minutes(double minutes)
{
method_params[Custom].isha_is_minutes = true;
method_params[Custom].isha_value = minutes;
calc_method = Custom;
}
void get_float_time_parts(double time, int& hours, int& minutes)
{
time = fix_hour(time + 0.5 / 60.0);
hours = floor(time);
minutes = floor((time - hours) * 60.0);
}
Modifikasi PrayerTimes.h
#ifndef PrayerTimes_h
#define PrayerTimes_h
#include "Arduino.h"
#include <math.h>
#include <String.h>
#include <WString.h>
#include <stdio.h>
static const int NUM_ITERATIONS = 1; // number of iterations needed to compute times
/* ------------------------------------------------------------------------------------------------- */
static const char* TimeName[] =
{
"Fajr",
"Sunrise",
"Dhuhr",
"Asr",
"Sunset",
"Maghrib",
"Isha",
"TimesCount"
};
// Calculation Methods
enum CalculationMethod
{
Jafari, // Ithna Ashari
Karachi, // University of Islamic Sciences, Karachi
ISNA, // Islamic Society of North America (ISNA)
MWL, // Muslim World League (MWL)
Makkah, // Umm al-Qura, Makkah
Egypt, // Egyptian General Authority of Survey
Custom, // Custom Setting
CalculationMethodsCount
};
// Juristic Methods
enum JuristicMethod
{
Shafii, // Shafii (standard)
Hanafi, // Hanafi
};
// Adjusting Methods for Higher Latitudes
enum AdjustingMethod
{
None, // No adjustment
MidNight, // middle of night
OneSeventh, // 1/7th of night
AngleBased, // angle/60th of night
};
// Time IDs
enum TimeID
{
Fajr,
Sunrise,
Dhuhr,
Asr,
Sunset,
Maghrib,
Isha,
TimesCount
};
struct MethodConfig
{
MethodConfig()
{
}
MethodConfig(double fajr_angle,
bool maghrib_is_minutes,
double maghrib_value,
bool isha_is_minutes,
double isha_value)
: fajr_angle(fajr_angle)
, maghrib_is_minutes(maghrib_is_minutes)
, maghrib_value(maghrib_value)
, isha_is_minutes(isha_is_minutes)
, isha_value(isha_value)
{
}
double fajr_angle;
bool maghrib_is_minutes;
double maghrib_value; // angle or minutes
bool isha_is_minutes;
double isha_value; // angle or minutes
};
struct DoublePair {
double first;
double second;
};
void compute_times(double times[]);
void adjust_times(double times[]);
void day_portion(double times[]);
void set_calc_method(CalculationMethod method_id);
void adjust_high_lat_times(double times[]);
void set_asr_method(JuristicMethod method_id);
void set_high_lats_adjust_method(AdjustingMethod method_id);
void set_calc_method(CalculationMethod method_id);
void set_fajr_angle(double angle);
void set_maghrib_angle(double angle);
void set_isha_angle(double angle);
double compute_time(double g, double t);
double sun_declination(double jd);
double compute_mid_day(double _t);
double equation_of_time(double jd);
double night_portion(double angle);
static double fix_angle(double a);
static double deg2rad(double d);
static double rad2deg(double r);
static String int_to_string(int num);
// FUNGSI UTAMA - DENGAN PARAMETER ELEVASI
void get_prayer_times(int year, int month, int day,
double _latitude, double _longitude,
double _timezone,
double _elevation, // PARAMETER ELEVASI
double times[]);
void get_float_time_parts(double time, int& hours, int& minutes);
#endifKode Program
Contoh kode dasar adalah sebagai berikut:
#include <Wire.h>
#include "RTClib.h"
#include "PrayerTimes.h"
RTC_DS3231 rtc;
// ========================
// KONFIGURASI LOKASI
// ========================
double LATITUDE = 4.107927;
double LONGITUDE = 96.196475;
int TIMEZONE = 7;
double ELEVATION = 10.0;
String LOCATION_NAME = "Nagan Raya";
// ========================
// METODE PERHITUNGAN
// ========================
String CALCULATION_METHOD = "MUI";
// ========================
// OFFSET OTOMATIS PER BULAN
// ========================
// Format: {Fajr, Sunrise, Dhuhr, Asr, Maghrib, Isha}
const int MONTHLY_OFFSETS[12][6] = {
// Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
{3, -3, 3, 3, 3, 3}, // Januari
{3, -3, 4, 3, 3, 3}, // Februari
{2, -2, 4, 3, 3, 2}, // Maret
{3, -3, 4, 3, 3, 2}, // April
{2, -3, 3, 3, 3, 3}, // Mei
{3, -3, 3, 2, 3, 3}, // Juni
{2, -3, 4, 3, 4, 3}, // Juli
{2, -3, 4, 3, 4, 3}, // Agustus
{3, -3, 4, 3, 3, 2}, // September
{2, -3, 4, 3, 3, 3}, // Oktober
{3, -3, 4, 3, 3, 3}, // November
{3, -2, 4, 2, 3, 2} // Desember
};
// Variabel offset aktif (akan diisi otomatis)
int FAJR_OFFSET = 0;
int SUNRISE_OFFSET = 0;
int DHUHR_OFFSET = 0;
int ASR_OFFSET = 0;
int MAGHRIB_OFFSET = 0;
int ISHA_OFFSET = 0;
// ========================
// MODE DAN VARIABEL
// ========================
bool CONFIG_MODE = false;
bool AUTO_OFFSET_ENABLED = true; // Toggle otomatis/manual
#define NUM_PRAYER_TIMES 11 // Tambah Imsak dan Dhuha
double times[NUM_PRAYER_TIMES];
int inputYear = 2026;
int inputMonth = 2;
int inputDay = 3;
// ========================
// UPDATE OFFSET OTOMATIS BERDASARKAN BULAN
// ========================
void updateMonthlyOffsets(int month) {
if (!AUTO_OFFSET_ENABLED) return;
if (month < 1 || month > 12) return;
int idx = month - 1; // Array 0-based
FAJR_OFFSET = MONTHLY_OFFSETS[idx][0];
SUNRISE_OFFSET = MONTHLY_OFFSETS[idx][1];
DHUHR_OFFSET = MONTHLY_OFFSETS[idx][2];
ASR_OFFSET = MONTHLY_OFFSETS[idx][3];
MAGHRIB_OFFSET = MONTHLY_OFFSETS[idx][4];
ISHA_OFFSET = MONTHLY_OFFSETS[idx][5];
}
// ========================
// Fungsi pilih metode perhitungan
// ========================
void setPrayerMethod(String method) {
set_calc_method(Custom);
set_asr_method(Shafii);
set_high_lats_adjust_method(AngleBased);
if (method == "MUI" || method == "Singapore") {
set_fajr_angle(20);
set_isha_angle(18);
} else if (method == "MWL" || method == "Turkey") {
set_fajr_angle(18);
set_isha_angle(17);
} else if (method == "ISNA") {
set_fajr_angle(15);
set_isha_angle(15);
} else if (method == "Egypt") {
set_fajr_angle(19.5);
set_isha_angle(17.5);
} else if (method == "Makkah") {
set_fajr_angle(18.5);
set_isha_angle(18);
} else if (method == "Karachi") {
set_fajr_angle(18);
set_isha_angle(18);
} else {
set_fajr_angle(18);
set_isha_angle(17);
}
}
// ========================
// Helper print
// ========================
void p(char *fmt, ...) {
char tmp[128];
va_list args;
va_start(args, fmt);
vsnprintf(tmp, 128, fmt, args);
va_end(args);
Serial.print(tmp);
}
// ========================
// Parse tanggal DD-MM-YYYY
// ========================
bool parseDate(String dateStr, int &year, int &month, int &day) {
int firstDash = dateStr.indexOf('-');
int secondDash = dateStr.indexOf('-', firstDash + 1);
if (firstDash == -1 || secondDash == -1) {
return false;
}
day = dateStr.substring(0, firstDash).toInt();
month = dateStr.substring(firstDash + 1, secondDash).toInt();
year = dateStr.substring(secondDash + 1).toInt();
if (day < 1 || day > 31 || month < 1 || month > 12 || year < 2000 || year > 2100) {
return false;
}
return true;
}
// ========================
// Tampilkan jadwal sholat
// ========================
void displayPrayerTimes() {
// Update offset otomatis berdasarkan bulan
updateMonthlyOffsets(inputMonth);
setPrayerMethod(CALCULATION_METHOD);
get_prayer_times(inputYear, inputMonth, inputDay,
LATITUDE, LONGITUDE, TIMEZONE,
ELEVATION,
times);
// PENTING: Hitung waktu terkoreksi DULU
double fajr_corrected = times[0] + (FAJR_OFFSET / 60.0);
double sunrise_corrected = times[1] + (SUNRISE_OFFSET / 60.0);
// Baru hitung Imsak dan Dhuha dari waktu yang sudah terkoreksi
double imsak = fajr_corrected - (10.0 / 60.0); // (Subuh + koreksi) - 10 menit
double dhuha = sunrise_corrected + (20.0 / 60.0); // (Terbit + koreksi) + 20 menit
// Reorganisasi array times dengan urutan baru
// [0]=Imsak, [1]=Fajr, [2]=Terbit, [3]=Dhuha, [4]=Dhuhr, [5]=Asr, [6]=Terbenam, [7]=Maghrib, [8]=Isha
double orderedTimes[NUM_PRAYER_TIMES];
orderedTimes[0] = imsak; // Imsak (sudah final, tidak perlu offset lagi)
orderedTimes[1] = times[0]; // Fajr (akan ditambah offset)
orderedTimes[2] = times[1]; // Terbit (akan ditambah offset)
orderedTimes[3] = dhuha; // Dhuha (sudah final, tidak perlu offset lagi)
orderedTimes[4] = times[2]; // Dhuhr
orderedTimes[5] = times[3]; // Asr
orderedTimes[6] = times[4]; // Terbenam
orderedTimes[7] = times[5]; // Maghrib
orderedTimes[8] = times[6]; // Isha
orderedTimes[9] = 0; // Reserved
orderedTimes[10] = 0; // Reserved
// Offset untuk setiap waktu
int offsets[NUM_PRAYER_TIMES] = {
0, // 0: Imsak (TIDAK ada offset, sudah final)
FAJR_OFFSET, // 1: Fajr
SUNRISE_OFFSET, // 2: Terbit
0, // 3: Dhuha (TIDAK ada offset, sudah final)
DHUHR_OFFSET, // 4: Dhuhr
ASR_OFFSET, // 5: Asr
0, // 6: Terbenam
MAGHRIB_OFFSET, // 7: Maghrib
ISHA_OFFSET, // 8: Isha
0, // 9: Reserved
0 // 10: Reserved
};
Serial.println();
Serial.println("=================================");
Serial.println(" JADWAL WAKTU SHOLAT v3.1");
Serial.println("=================================");
Serial.print("Lokasi : ");
Serial.println(LOCATION_NAME);
Serial.print("Koordinat: ");
Serial.print(LATITUDE, 6);
Serial.print(", ");
Serial.println(LONGITUDE, 6);
Serial.print("Elevasi : ");
Serial.print(ELEVATION, 1);
Serial.println(" m");
Serial.print("Metode : ");
Serial.println(CALCULATION_METHOD);
Serial.print("Tanggal : ");
Serial.print(inputDay);
Serial.print("/");
Serial.print(inputMonth);
Serial.print("/");
Serial.println(inputYear);
Serial.print("Timezone : UTC");
if (TIMEZONE >= 0) Serial.print("+");
Serial.println(TIMEZONE);
// Tampilkan status auto-offset
Serial.print("Auto-Adj : ");
Serial.println(AUTO_OFFSET_ENABLED ? "AKTIF" : "MANUAL");
Serial.println("=================================");
// Nama waktu dalam Bahasa Indonesia
const char *displayNames[] = {
"Imsak", "Subuh", "Terbit", "Dhuha", "Dzuhur",
"Ashar", "Terbenam", "Maghrib", "Isya"
};
for (int i = 0; i < 9; i++) { // Hanya tampilkan 9 waktu utama
int hours, minutes;
double correctedTime = orderedTimes[i] + (offsets[i] / 60.0);
get_float_time_parts(correctedTime, hours, minutes);
// Handle overflow
while (minutes >= 60) {
hours++;
minutes -= 60;
}
while (minutes < 0) {
hours--;
minutes += 60;
}
while (hours >= 24) hours -= 24;
while (hours < 0) hours += 24;
p("%-10s : %02d:%02d", displayNames[i], hours, minutes);
if (offsets[i] != 0) {
p(" (%+d)", offsets[i]);
}
// Tandai waktu yang terpengaruh koreksi elevasi
if (ELEVATION > 0 && (i == 2 || i == 6 || i == 7)) {
Serial.print(" *");
}
Serial.println();
}
Serial.println("=================================");
if (ELEVATION > 0) {
Serial.println("* = Sudah terkoreksi elevasi");
double elevCorr = 1.76 * sqrt(ELEVATION) / 60.0;
Serial.print(" Koreksi: ±");
Serial.print(elevCorr, 1);
Serial.println(" menit");
Serial.println("=================================");
}
Serial.println("Catatan:");
Serial.println("• Imsak = 10 menit sebelum Subuh");
Serial.println("• Dhuha = 20 menit setelah Terbit");
Serial.println("=================================");
}
// ========================
// MODE KONFIGURASI
// ========================
void showConfigMenu() {
Serial.println();
Serial.println("=================================");
Serial.println(" MODE KONFIGURASI");
Serial.println("=================================");
Serial.println("Perintah yang tersedia:");
Serial.println();
Serial.println("1. SET LOKASI");
Serial.println(" Format: LOC <nama>,<lat>,<lon>,<tz>,<elev>");
Serial.println(" Contoh: LOC Jakarta,-6.2088,106.8456,7,8");
Serial.println();
Serial.println("2. SET METODE");
Serial.println(" Format: METHOD <metode>");
Serial.println(" Pilihan: MUI, MWL, ISNA, Egypt,");
Serial.println(" Makkah, Karachi, Singapore");
Serial.println(" Contoh: METHOD MUI");
Serial.println();
Serial.println("3. SET KOREKSI MANUAL");
Serial.println(" Format: OFFSET <waktu> <menit>");
Serial.println(" Waktu: FAJR, SUNRISE, DHUHR,");
Serial.println(" ASR, MAGHRIB, ISHA");
Serial.println(" Contoh: OFFSET FAJR +3");
Serial.println(" Otomatis nonaktif jika pakai ini");
Serial.println();
Serial.println("4. TOGGLE AUTO-OFFSET");
Serial.println(" Perintah: AUTO [ON/OFF]");
Serial.println(" Contoh: AUTO ON");
Serial.println();
Serial.println("5. TAMPILKAN KONFIGURASI");
Serial.println(" Perintah: SHOW");
Serial.println();
Serial.println("6. KELUAR DARI CONFIG");
Serial.println(" Perintah: EXIT");
Serial.println("=================================");
Serial.println();
}
void showCurrentConfig() {
// Update offset dulu sebelum menampilkan
if (AUTO_OFFSET_ENABLED) {
updateMonthlyOffsets(inputMonth);
}
Serial.println();
Serial.println("=== KONFIGURASI SAAT INI ===");
Serial.print("Lokasi : ");
Serial.println(LOCATION_NAME);
Serial.print("Latitude : ");
Serial.println(LATITUDE, 6);
Serial.print("Longitude : ");
Serial.println(LONGITUDE, 6);
Serial.print("Elevasi : ");
Serial.print(ELEVATION, 1);
Serial.println(" meter");
Serial.print("Timezone : UTC");
if (TIMEZONE >= 0) Serial.print("+");
Serial.println(TIMEZONE);
Serial.print("Metode : ");
Serial.println(CALCULATION_METHOD);
Serial.print("Auto-Adjust: ");
Serial.println(AUTO_OFFSET_ENABLED ? "AKTIF" : "MANUAL");
Serial.println("Koreksi (menit):");
Serial.print(" Fajr : ");
Serial.println(FAJR_OFFSET);
Serial.print(" Sunrise : ");
Serial.println(SUNRISE_OFFSET);
Serial.print(" Dhuhr : ");
Serial.println(DHUHR_OFFSET);
Serial.print(" Asr : ");
Serial.println(ASR_OFFSET);
Serial.print(" Maghrib : ");
Serial.println(MAGHRIB_OFFSET);
Serial.print(" Isha : ");
Serial.println(ISHA_OFFSET);
if (AUTO_OFFSET_ENABLED) {
Serial.print(" (Bulan : ");
const char* months[] = {"", "Jan", "Feb", "Mar", "Apr", "Mei", "Jun",
"Jul", "Agu", "Sep", "Okt", "Nov", "Des"};
Serial.print(months[inputMonth]);
Serial.println(")");
}
Serial.println("============================");
Serial.println();
}
void processConfigCommand(String cmd) {
cmd.trim();
cmd.toUpperCase();
if (cmd == "HELP" || cmd == "?") {
showConfigMenu();
}
else if (cmd == "SHOW") {
showCurrentConfig();
}
else if (cmd == "EXIT") {
CONFIG_MODE = false;
Serial.println("Keluar dari mode konfigurasi.");
Serial.println("Masukkan tanggal (DD-MM-YYYY):");
}
else if (cmd.startsWith("AUTO ")) {
String param = cmd.substring(5);
param.trim();
if (param == "ON") {
AUTO_OFFSET_ENABLED = true;
Serial.println("✓ Auto-offset AKTIF");
updateMonthlyOffsets(inputMonth); // Update langsung
showCurrentConfig();
}
else if (param == "OFF") {
AUTO_OFFSET_ENABLED = false;
Serial.println("✓ Auto-offset NONAKTIF (mode manual)");
}
else {
Serial.println("✗ Gunakan: AUTO ON atau AUTO OFF");
}
}
else if (cmd.startsWith("LOC ")) {
String params = cmd.substring(4);
int comma1 = params.indexOf(',');
int comma2 = params.indexOf(',', comma1 + 1);
int comma3 = params.indexOf(',', comma2 + 1);
int comma4 = params.indexOf(',', comma3 + 1);
if (comma1 > 0 && comma2 > 0 && comma3 > 0) {
LOCATION_NAME = params.substring(0, comma1);
LATITUDE = params.substring(comma1 + 1, comma2).toDouble();
LONGITUDE = params.substring(comma2 + 1, comma3).toDouble();
TIMEZONE = params.substring(comma3 + 1, comma4 > 0 ? comma4 : params.length()).toInt();
if (comma4 > 0) {
ELEVATION = params.substring(comma4 + 1).toDouble();
}
Serial.println("✓ Lokasi berhasil diubah!");
showCurrentConfig();
} else {
Serial.println("✗ Format salah! Gunakan: LOC Nama,lat,lon,tz,elev");
}
}
else if (cmd.startsWith("METHOD ")) {
String method = cmd.substring(7);
method.trim();
if (method == "MUI" || method == "MWL" || method == "ISNA" ||
method == "EGYPT" || method == "MAKKAH" || method == "KARACHI" ||
method == "SINGAPORE" || method == "TURKEY") {
CALCULATION_METHOD = method;
Serial.println("✓ Metode berhasil diubah ke: " + method);
} else {
Serial.println("✗ Metode tidak valid!");
Serial.println("Pilihan: MUI, MWL, ISNA, Egypt, Makkah, Karachi, Singapore, Turkey");
}
}
else if (cmd.startsWith("OFFSET ")) {
String params = cmd.substring(7);
int spacePos = params.indexOf(' ');
if (spacePos > 0) {
String prayerTime = params.substring(0, spacePos);
int offset = params.substring(spacePos + 1).toInt();
// Nonaktifkan auto-offset jika setting manual
AUTO_OFFSET_ENABLED = false;
if (prayerTime == "FAJR") FAJR_OFFSET = offset;
else if (prayerTime == "SUNRISE") SUNRISE_OFFSET = offset;
else if (prayerTime == "DHUHR") DHUHR_OFFSET = offset;
else if (prayerTime == "ASR") ASR_OFFSET = offset;
else if (prayerTime == "MAGHRIB") MAGHRIB_OFFSET = offset;
else if (prayerTime == "ISHA") ISHA_OFFSET = offset;
else {
Serial.println("✗ Waktu tidak valid!");
AUTO_OFFSET_ENABLED = true; // Revert
return;
}
Serial.println("✓ Koreksi " + prayerTime + " diubah ke: " + String(offset));
Serial.println("Auto-offset NONAKTIF (mode manual)");
} else {
Serial.println("✗ Format salah! Gunakan: OFFSET FAJR +3");
}
}
else {
Serial.println("✗ Perintah tidak dikenal. Ketik HELP untuk bantuan.");
}
}
// ========================
// SETUP
// ========================
void setup() {
Serial.begin(9600);
delay(1000);
if (!rtc.begin()) {
Serial.println("Warning: RTC tidak terdeteksi");
} else {
if (rtc.lostPower()) {
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
DateTime now = rtc.now();
inputYear = now.year();
inputMonth = now.month();
inputDay = now.day();
}
// Update offset berdasarkan bulan saat ini
updateMonthlyOffsets(inputMonth);
Serial.println();
Serial.println("=================================");
Serial.println(" KALKULATOR WAKTU SHOLAT");
Serial.println(" Universal Prayer Times");
Serial.println("=================================");
Serial.println("Versi : 3.1 (Imsak & Dhuha)");
Serial.println("Author: CHIPPIKO");
Serial.println("=================================");
Serial.println();
showCurrentConfig();
Serial.println("CARA PENGGUNAAN:");
Serial.println("1. Masukkan tanggal: DD-MM-YYYY");
Serial.println(" Contoh: 03-02-2026");
Serial.println();
Serial.println("2. Ketik CONFIG untuk konfigurasi");
Serial.println(" (ubah lokasi, metode, elevasi)");
Serial.println("=================================");
Serial.println();
if (rtc.begin() && !rtc.lostPower()) {
Serial.println("Jadwal hari ini:");
displayPrayerTimes();
Serial.println();
}
Serial.println("Masukkan tanggal atau ketik CONFIG:");
}
// ========================
// LOOP
// ========================
void loop() {
if (Serial.available() > 0) {
String input = Serial.readStringUntil('\n');
input.trim();
if (input.length() == 0) return;
Serial.print("> ");
Serial.println(input);
if (input.equalsIgnoreCase("CONFIG")) {
CONFIG_MODE = true;
showConfigMenu();
return;
}
if (CONFIG_MODE) {
processConfigCommand(input);
return;
}
if (parseDate(input, inputYear, inputMonth, inputDay)) {
displayPrayerTimes();
Serial.println();
Serial.println("Masukkan tanggal atau CONFIG:");
} else {
Serial.println("✗ Format salah!");
Serial.println("Format: DD-MM-YYYY atau CONFIG");
Serial.println();
}
}
delay(100);
}Hasil
Setelah kode di upload ke Arduino, buka serial monitor dan kita akan mendapatkan hasil sebagai gambar berikut:

Versi Sebelumnya
v1.0 – Initial Release
- Perhitungan dasar waktu sholat (Fajr, Sunrise, Dhuhr, Asr, Sunset, Maghrib, Isha)
- Menggunakan library PrayerTimes original
- Support multiple calculation methods (MWL, ISNA, Egypt, Makkah, Karachi)
- Input manual tanggal DD-MM-YYYY
- RTC DS3231 integration
- Issue: Selisih waktu dengan referensi cukup besar (5-10 menit)
v1.5 – Manual Offset System
- Tambah koreksi manual per waktu sholat (FAJR_OFFSET, DHUHR_OFFSET, dll)
- Tampilkan indikator koreksi dalam format
(+3)atau(-2) - Issue: Offset harus diubah manual di kode setiap bulan
v2.0 – Julian Date Fix
- Perbaikan algoritma
get_julian_date()menggunakan metode Jean Meeus - Support elevasi/ketinggian lokasi (elevation correction)
- Formula dip angle:
1.76 * sqrt(elevation)untuk Sunrise/Sunset/Maghrib - Indikator
*untuk waktu yang terpengaruh koreksi elevasi - Issue: Selisih waktu meningkat seiring tahun (5 menit/tahun untuk 2027-2028)
v2.1 – Config Mode
- Mode konfigurasi interaktif via Serial
- Command:
LOC,METHOD,OFFSET,SHOW,EXIT - Ubah lokasi tanpa edit kode
- Ubah metode perhitungan on-the-fly
- Set koreksi manual per waktu sholat
v2.2 – High Precision Sun Position
- Algoritma
sun_position()dengan presisi tinggi (Meeus) - Menghitung dengan Julian Centuries (T) bukan day count
- Koreksi eccentricity orbit bumi
- Koreksi equation of center
- Koreksi nutasi (goyangan sumbu bumi)
- Obliquity yang dinamis
- Result: Selisih tahunan berkurang drastis
v3.0 – Auto Monthly Offset
- Auto-adjustment offset berdasarkan bulan
- Array
MONTHLY_OFFSETS[12][6]dengan data tuning per bulan - Toggle mode AUTO/MANUAL
- Command baru:
AUTO ON/AUTO OFF - Offset otomatis update saat ganti bulan
- Indikator status
Auto-Adj : AKTIF - Tampilkan bulan aktif:
(Bulan : Feb) - Result: Tidak perlu edit kode setiap bulan
v3.1 – Imsak & Dhuha (Current)
- Tambah waktu Imsak (10 menit sebelum Subuh)
- Tambah waktu Dhuha (20 menit setelah Terbit)
- Penamaan Bahasa Indonesia:
Fajr→SubuhSunrise→TerbitSunset→TerbenamDhuhr→DzuhurAsr→AsharIsha→Isya
- Urutan kronologis: Imsak → Subuh → Terbit → Dhuha → Dzuhur → Ashar → Terbenam → Maghrib → Isya
- Fix perhitungan: Imsak = (Subuh + koreksi) – 10 menit
- Fix perhitungan: Dhuha = (Terbit + koreksi) + 20 menit
- Catatan otomatis di output
- Update offset saat
setup()danshowCurrentConfig()
Semoga artikel Kode Progam Jadwal Sholat Arduino C++ ini bermanfaat. Jika teman-teman ada saran dan kritikan, mohon contact kami.
Update: 4 Feb 2026
