#include <Wire.h>
#include <RTClib.h>
#include <EEPROM.h>
#define MAX_SCHEDULES 10 // Max number of schedules
#define NUM_RELAYS 4 // Number of relays
// Relay pins
const uint8_t relayPins[NUM_RELAYS] = {25, 26, 27, 14};
RTC_DS1307 rtc;
struct Schedule {
uint8_t relayID; // Relay index
uint8_t mode; // 0=Once, 1=Daily, 2=Weekly, 3=Custom
uint8_t hour; // 12-hour format
uint8_t minute;
uint8_t am_pm; // 0 = AM, 1 = PM
uint8_t dayOfWeek; // For weekly mode
uint8_t day; // For once/custom
uint8_t month;
uint16_t year;
};
Schedule schedules[MAX_SCHEDULES];
bool relayStates[NUM_RELAYS] = {false}; // Relay states
// ✅ SETUP
void setup() {
Serial.begin(115200);
Wire.begin();
EEPROM.begin(512);
// Initialize relay pins
for (int i = 0; i < NUM_RELAYS; i++) {
pinMode(relayPins[i], OUTPUT);
digitalWrite(relayPins[i], LOW);
}
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
if (!rtc.isrunning()) {
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
loadSchedules(); // ✅ Load saved schedules from EEPROM
// ✅ Example schedules (no auto-off)
addOnceToday(0, 8, 15, 0); // Relay 0 ON today at 8:15 AM (no auto-off)
// addDaily(0, 8, 10, 0); // Relay 0 ON daily at 9:10 PM (no auto-off)
// addWeekly(1, 2, 10, 30, 0); // Relay 1 ON every Tuesday at 10:30 AM (no auto-off)
// addCustom(2, 2025, 3, 21, 8, 15, 0); // Relay 2 ON March 21, 2025, 8:15 AM (no auto-off)
saveSchedules();
}
// ✅ LOOP
void loop() {
DateTime now = rtc.now();
bool scheduleTriggered[NUM_RELAYS] = {false};
for (int i = 0; i < MAX_SCHEDULES; i++) {
if (isScheduledTime(now, schedules[i])) {
relayStates[schedules[i].relayID] = true; // Turn ON the relay
scheduleTriggered[schedules[i].relayID] = true;
Serial.print("Relay ON: ");
Serial.println(schedules[i].relayID);
}
}
// Control relay states
for (int i = 0; i < NUM_RELAYS; i++) {
digitalWrite(relayPins[i], relayStates[i] ? HIGH : LOW);
}
delay(1000); // 1 second delay
}
// ✅ Function to check if current time matches a schedule
bool isScheduledTime(DateTime now, Schedule schedule) {
uint8_t currentHour = now.hour() % 12;
currentHour = (currentHour == 0) ? 12 : currentHour; // Convert 0 to 12-hour format
uint8_t currentAmPm = (now.hour() >= 12) ? 1 : 0;
switch (schedule.mode) {
case 0: // Once (Today)
return (now.year() == schedule.year &&
now.month() == schedule.month &&
now.day() == schedule.day &&
currentHour == schedule.hour &&
now.minute() == schedule.minute &&
currentAmPm == schedule.am_pm);
case 1: // Daily
return (currentHour == schedule.hour &&
now.minute() == schedule.minute &&
currentAmPm == schedule.am_pm);
case 2: // Weekly
return (now.dayOfTheWeek() == schedule.dayOfWeek &&
currentHour == schedule.hour &&
now.minute() == schedule.minute &&
currentAmPm == schedule.am_pm);
case 3: // Custom (Specific date)
return (now.year() == schedule.year &&
now.month() == schedule.month &&
now.day() == schedule.day &&
currentHour == schedule.hour &&
now.minute() == schedule.minute &&
currentAmPm == schedule.am_pm);
default:
return false;
}
}
// ✅ Multi-relay scheduling functions
void addOnceToday(uint8_t relayID, uint8_t hour, uint8_t minute, uint8_t am_pm) {
DateTime now = rtc.now();
addSchedule(relayID, 0, hour, minute, am_pm, 0, now.day(), now.month(), now.year());
}
void addDaily(uint8_t relayID, uint8_t hour, uint8_t minute, uint8_t am_pm) {
addSchedule(relayID, 1, hour, minute, am_pm, 0, 0, 0, 0);
}
void addWeekly(uint8_t relayID, uint8_t dayOfWeek, uint8_t hour, uint8_t minute, uint8_t am_pm) {
addSchedule(relayID, 2, hour, minute, am_pm, dayOfWeek, 0, 0, 0);
}
void addCustom(uint8_t relayID, uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t am_pm) {
addSchedule(relayID, 3, hour, minute, am_pm, 0, day, month, year);
}
// ✅ Add a schedule
void addSchedule(uint8_t relayID, uint8_t mode, uint8_t hour, uint8_t minute, uint8_t am_pm,
uint8_t dayOfWeek, uint8_t day, uint8_t month, uint16_t year) {
for (int i = 0; i < MAX_SCHEDULES; i++) {
if (schedules[i].mode == 255) { // Empty slot
schedules[i].relayID = relayID;
schedules[i].mode = mode;
schedules[i].hour = hour;
schedules[i].minute = minute;
schedules[i].am_pm = am_pm;
schedules[i].dayOfWeek = dayOfWeek;
schedules[i].day = day;
schedules[i].month = month;
schedules[i].year = year;
Serial.print("Added schedule at slot ");
Serial.println(i + 1);
return;
}
}
Serial.println("No empty slot available!");
}
// ✅ Delete a specific schedule
void deleteSchedule(int index) {
if (index >= 0 && index < MAX_SCHEDULES) {
schedules[index].mode = 255; // Mark as empty
Serial.print("Deleted schedule at slot ");
Serial.println(index + 1);
saveSchedules();
} else {
Serial.println("Invalid schedule index!");
}
}
// ✅ Manual relay control
void manualRelayOn(uint8_t relayID) {
if (relayID < NUM_RELAYS) {
relayStates[relayID] = true;
Serial.print("Relay ");
Serial.print(relayID);
Serial.println(" turned ON manually.");
}
}
void manualRelayOff(uint8_t relayID) {
if (relayID < NUM_RELAYS) {
relayStates[relayID] = false;
Serial.print("Relay ");
Serial.print(relayID);
Serial.println(" turned OFF manually.");
}
}
// ✅ Save and load schedules in EEPROM
void saveSchedules() {
EEPROM.put(0, schedules);
EEPROM.commit();
Serial.println("Schedules saved!");
}
void loadSchedules() {
EEPROM.get(0, schedules);
for (int i = 0; i < MAX_SCHEDULES; i++) {
if (schedules[i].mode > 3) {
schedules[i].mode = 255; // Mark as empty
}
}
Serial.println("Schedules loaded!");
}