#include <Wire.h>
#include <RTClib.h>
#include <LiquidCrystal_I2C.h>
// ----------- Objects ----------
RTC_DS1307 rtc;
LiquidCrystal_I2C lcd(0x27, 16, 2);
// ----------- Pins ----------
const int RELAY_PIN = 6;
const int BTN_MODE = 2;
const int BTN_UP = 3;
const int BTN_BELL = 4;
const int BTN_FIRE = 5;
// ----------- Bell Settings ----------
int bellHour[]={8, 8, 9, 9, 10, 10, 10, 11, 12, 12, 13, 13, 13, 14, 14, 15, 16, 16, 16, 17, 18};
int bellMinute[]={0, 40, 20, 25, 5, 45, 55, 35, 15, 20, 0, 15, 55, 35, 40, 20, 0, 10, 50, 30, 10};
int bellCount = 21;
int lastMinuteTriggered = -1;
unsigned long bellStartTime = 0;
const int BELL_DURATION = 10000;
bool isRinging = false;
// ----------- Weekdays ----------
const char* weekDays[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
uint8_t hour_v, minute_v, second_v, date_v, month_v, day_v;
uint16_t year_v;
// ------------------ Functions ------------------
// 🕒 Цаг + өдөр LCD-д харуулах
void displayTime(DateTime now) {
char buf[20];
sprintf(buf, "%02d:%02d:%02d %s", now.hour(), now.minute(), now.second(), weekDays[now.dayOfTheWeek()]);
lcd.setCursor(0, 0);
lcd.print(buf);
}
// 🔔 Хонх дуугаргах
void ringBell() {
if (digitalRead(RELAY_PIN) == LOW) { // Хонх дуугарч эхэлж байгаа үед
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("🔔 Bell Now! 🔔");
digitalWrite(RELAY_PIN, HIGH);
bellStartTime = millis();
}
}
// 🔥 Галын дохиолол
void fireAlarm() {
for (int i = 0; i < 3; i++) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("!!! CAUTION !!!");
digitalWrite(RELAY_PIN, HIGH);
millis();
digitalWrite(RELAY_PIN, LOW);
lcd.clear();
millis();
}
}
// ⏰ Хонхны цаг шалгах
void checkBell(DateTime now) {
if(now.dayOfTheWeek() == 0 || now.dayOfTheWeek() == 6) return;
if (now.minute() == lastMinuteTriggered || isRinging) return;
for (int i = 0; i < bellCount; i++) {
if (now.hour() == bellHour[i] && now.minute() == bellMinute[i]) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("🔔 Bell Now! 🔔");
digitalWrite(RELAY_PIN, HIGH);
bellStartTime = millis();
isRinging = true;
lastMinuteTriggered = now.minute();
return;
}
}
}
// 🔔 Дараагийн хонхны цагийг олох
String nextBell(DateTime now) {
if(now.dayOfTheWeek() == 0 || now.dayOfTheWeek() == 6) return "WEEKEND";
for (int i = 0; i < bellCount; i++) {
if (bellHour[i] > now.hour() || (bellHour[i] == now.hour() && bellMinute[i] > now.minute())) {
char buf[6];
sprintf(buf, "%02d:%02d", bellHour[i], bellMinute[i]);
return String(buf);
}
}
return "--:--";
}
void DS1307_display() {
DateTime now = rtc.now();
// Break out the components
uint8_t hh = now.hour();
uint8_t mm = now.minute();
uint8_t ss = now.second();
uint8_t dd = now.day();
uint8_t mon = now.month();
uint8_t wday = now.dayOfTheWeek(); // 0 = Sun
uint16_t yy = now.year();
// First line: time
char line1[17];
snprintf(line1, sizeof(line1), "%s %02u:%02u:%02u", weekDays[wday], hh, mm, ss);
lcd.setCursor(0, 0);
lcd.print(line1);
// Second line: date
char line2[17];
// print as 2025/11/13 or yy/mm/dd if you prefer 2-digit year:
snprintf(line2, sizeof(line2), "%04u/%02u/%02u", yy, mon, dd);
lcd.setCursor(0, 1);
lcd.print(line2);
}
// ---------------- small helper for blinking delay ----------------
void blink_parameter() {
byte j = 0;
// wait a short amount while both buttons are not pressed
while (j < 10 && digitalRead(BTN_MODE) && digitalRead(BTN_UP)) {
j++;
delay(25);
}
}
// ---------------- edit single numeric parameter on LCD ----------------
// x,y = cursor, parameter = initial value, index = which parameter (0=hour,1=min,2=year,3=month,4=date,5=day)
byte edit(byte x, byte y, byte parameter, byte index) {
char text[4];
// wait until MODE released if it was pressed entering edit
while (!digitalRead(BTN_MODE)) { delay(10); }
while (true) {
// when UP pressed => increment
if (!digitalRead(BTN_UP)) {
// increment with wrapping according to index
if (index == 0) { // hour
parameter = (parameter + 1) % 24;
} else if (index == 1) { // minute
parameter = (parameter + 1) % 60;
} else if (index == 2) { // year (we treat as two-digit 0..99 representing 2000..2099)
parameter++;
if (parameter > 99) parameter = 0;
} else if (index == 3) { // month
parameter++;
if (parameter > 12) parameter = 1;
} else if (index == 4) { // date
parameter++;
if (parameter > 31) parameter = 1;
}
// show updated value
lcd.setCursor(x, y);
snprintf(text, sizeof(text), "%02u", parameter);
lcd.print(text);
delay(180); // debouncing speed
}
// blink effect when idle
lcd.setCursor(x, y);
lcd.print(" ");
blink_parameter();
lcd.setCursor(x, y);
snprintf(text, sizeof(text), "%02u", parameter);
lcd.print(text);
blink_parameter();
// MODE pressed => accept and move to next
if (!digitalRead(BTN_MODE)) {
// wait for release to avoid bouncing into next state
while (!digitalRead(BTN_MODE)) delay(10);
return parameter;
}
}
}
// ---------------- setTime() fully corrected ----------------
void setTime() {
// get current values from RTC (so we start from sensible defaults)
DateTime now = rtc.now();
hour_v = now.hour();
minute_v = now.minute();
second_v = now.second();
date_v = now.day();
day_v = now.dayOfTheWeek() + 1; // convert 0..6 -> 1..7 if you want 1..7 convention
month_v = now.month();
year_v = now.year(); // full year (e.g., 2025)
// If you prefer to edit two-digit year (00..99) representing 2000..2099:
uint8_t yearTwo = year_v % 100;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("SET TIME");
delay(400);
// i corresponds to which field we are editing; we'll step through: hour, minute, year(yy), month, date, day
byte i = 0;
// Edit hour
lcd.setCursor(0,0);
lcd.print("Hour:");
hour_v = edit(5, 0, hour_v, 0); // place at col 5,row0
// Edit minute
lcd.setCursor(0,0);
lcd.print("Min :");
minute_v = edit(5, 0, minute_v, 1);
// Edit year (two-digit)
lcd.setCursor(0,0);
lcd.print("YY ");
yearTwo = edit(5, 0, yearTwo, 2);
// Edit month
lcd.setCursor(0,0);
lcd.print("Mon ");
month_v = edit(5, 0, month_v, 3);
// Edit date (day of month)
lcd.setCursor(0,0);
lcd.print("Date ");
date_v = edit(5, 0, date_v, 4);
// If you want to allow editing seconds too, add similar step.
// Now compose full year
year_v = 2000 + yearTwo; // assuming 2000..2099 range
// Write to RTC using RTClib DateTime - pass (year, month, day, hour, minute, second)
rtc.adjust(DateTime((uint16_t)year_v, (uint8_t)month_v, (uint8_t)date_v,
(uint8_t)hour_v, (uint8_t)minute_v, (uint8_t)second_v));
lcd.clear();
lcd.print("Time Saved!");
delay(1000);
lcd.clear();
}
// 🔁 Өдөр бүр ресет хийх
/*void autoReset(DateTime now) {
int savedResetDay = EEPROM.read(RESET_ADDR);
// өдөр 03:00 болсон, мөн өмнөхөд энэ өдөр ресет хийгдээгүй бол
if (now.hour() == 3 && now.minute() == 0 && now.second() < 10 && savedResetDay != now.day()) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Daily Reset...");
delay(2000);
lcd.clear();
// тухайн өдрийг EEPROM-д бичнэ
EEPROM.write(RESET_ADDR, now.day());
// RTC-г өөрчлөхгүй — зөвхөн Arduino-г restart хийх
void (*resetFunc)(void) = 0; // safer reset
resetFunc();
}
}*/
// ------------------ Setup ------------------
void setup() {
Serial.begin(9600);
Wire.begin();
lcd.init();
lcd.backlight();
pinMode(RELAY_PIN, OUTPUT); //D6 pin
digitalWrite(RELAY_PIN, LOW);
pinMode(BTN_MODE, INPUT_PULLUP); //D5 pin
pinMode(BTN_UP, INPUT_PULLUP); //D4 pin
pinMode(BTN_BELL, INPUT_PULLUP); //D3 pin
pinMode(BTN_FIRE, INPUT_PULLUP); //D2 pin
if (!rtc.begin()) {
lcd.print("RTC not found!");
while (1);
}
lcd.setCursor(0, 0);
lcd.print("Automatic Bell");
lcd.setCursor(0, 1);
lcd.print("Loading Memory...");
delay(1500);
//loadFromEEPROM();
lcd.clear();
lcd.print("System Ready!");
delay(1000);
lcd.clear();
}
// ------------------ Loop ------------------
void loop() {
DateTime now = rtc.now();
displayTime(now);
if (isRinging && millis() - bellStartTime >= BELL_DURATION) {
digitalWrite(RELAY_PIN, LOW);
isRinging = false;
lcd.clear();
}
// LCD-д дараагийн хонхны цаг харуулах
lcd.setCursor(0,1);
lcd.print("Next: " + nextBell(now) + " ");
if (!digitalRead(BTN_FIRE)) fireAlarm();
if (!digitalRead(BTN_MODE)) setTime();
checkBell(now);
//autoReset(now);
delay(200);
}