#define SERIAL_OPTION 0
#define SW_PIN 6
#define B0_PIN 13 // MENU BUTTON
#define B1_PIN 12 // EXIT BUTTON
#define B2_PIN 11 // <-- BUTTON
#define B3_PIN 10 // --> BUTTON
#define B4_PIN 9 // ENTER & CHANGE BUTTON
#include <EEPROM.h>
#include <LiquidCrystal_I2C.h> // LCD I2C
#include <RTClib.h> // RTC
#include <Wire.h> // I2C COMM
LiquidCrystal_I2C lcd(0x27,20,4); // ADDRESS: 0x3F or 0x27
RTC_DS1307 RTC;
/*
switchSchedule[0] == B000000000000000000000000000001 --> AT 0H, 0-2 MIN SWITCH ON
switchSchedule[1] == B000000000000000000000000011111 --> AT 1H, 0-10 MIN SWITHC ON
*/
/*
PUSH MENU BUTTON TO ENTER ON TIME SETTING
MOVE AROUND 0H, 1H, 2H, ... 23H HOUR SCHEDULES
PUSH ENTER/CHANGE BUTTON TO ENTER INTO THAT HOUR
MOVE AROUND 0M, 2M, 4M, .. 58M MIN SCHEDULE
PUSH ENTER/CHANGE BUTTON TO TOGGLE ON/OFF AT THAT MIN
*/
const char switchString[2][4]={"OFF","ON"};
const char starString[2][2]={" ","*"};
int i;
char buf[25];
DateTime now;
long switchSchedule[24]={0}; // SWITCHING SCHEDULE FOR 24 HOUR
byte minuteSchedule[30]={0}; // TEMPORARY 2MINUTE SCHEDULE FOR SELECTED HOUR
int currentSwitch=0;
int plannedSchedule(int,int);
int normalLCDUpdate=0;
int currentState_B0=0,currentState_B1=0,currentState_B2=0,currentState_B3=0,B2=0,currentState_B4=0;
int previousState_B0=0,previousState_B1=0,previousState_B2=0,previousState_B3=0,previousState_B4=0;
int pointHour=0,point2Minute=0;
void normalLCD();
void menuLCD_H();
void menuLCD_M();
void loadSchedule();
void saveSchedule();
void setSchedule_H();
void setSchedule_M();
void setup() {
//switchSchedule[14]=long(B000000<<24)+long(B00000000<<16)+long(B00000000<<8)+long(B00001110); // AT 14H 2-8 MIN TEST
loadSchedule();
pinMode(B0_PIN, INPUT_PULLUP);
pinMode(B1_PIN, INPUT_PULLUP);
pinMode(B2_PIN, INPUT_PULLUP);
pinMode(B3_PIN, INPUT_PULLUP);
pinMode(B4_PIN, INPUT_PULLUP);
pinMode(SW_PIN, OUTPUT);
lcd.init(); // LCD 초기화
lcd.backlight(); // LCD 백라이트 켜기
if (SERIAL_OPTION) Serial.begin(9600); // 시리얼통신 초기화
RTC.begin(); // 실시간시계 시작
// RTC.adjust(DateTime(2022,9,30,18,13,40)); // 처음 한번만 적절한 날짜 시간으로 설정, 이후 주석처리
}
void loop() {
currentState_B0=!digitalRead(B0_PIN);
if (currentState_B0==1 && previousState_B0==0) { // (Menu) Button Newly Pressed
digitalWrite(SW_PIN,0); // Turn Off Switch While Setting
currentSwitch=0; // & Record Switch State as Off
setSchedule_H(); // Call Set Hour Funciton
}
now=RTC.now();
if (SERIAL_OPTION) {
Serial.print(now.year()); Serial.print("/");
Serial.print(now.month()); Serial.print("/");
Serial.print(now.day()); Serial.print(" ");
Serial.print(now.hour()); Serial.print(":");
Serial.print(now.minute()); Serial.print(":");
Serial.print(now.second()); Serial.print("\n");
}
if (plannedSwitch(now.hour(),now.minute())!=currentSwitch) {
currentSwitch=!currentSwitch;
digitalWrite(SW_PIN,currentSwitch);
}
delay(20);
if (millis()%1000<100 && normalLCDUpdate==0) { normalLCD(); normalLCDUpdate=1; }
if (millis()%1000>=100 && normalLCDUpdate==1) { normalLCDUpdate=0; }
previousState_B0=currentState_B0;
}
int plannedSwitch(int h,int m){
unsigned long scheduleBit=switchSchedule[h]>>(m/2);
return(int(scheduleBit&B1));
}
void normalLCD(void){
{
lcd.setCursor(0,0); sprintf(buf,"<DAILY TIMER SWITCH>");
lcd.print(buf);
lcd.setCursor(0,1); sprintf(buf,"%4d/%02d/%02d %02d:%02d:%02d",now.year(),now.month(),now.day(),now.hour(),now.minute(),now.second());
lcd.print(buf);
lcd.setCursor(0,2); lcd.print("CUR SW |");
for (i=0;i<12;i++) lcd.print(starString[!!switchSchedule[i]]);
lcd.setCursor(0,3); sprintf(buf," = %3s |",switchString[currentSwitch]); lcd.print(buf);
for (i=12;i<24;i++) lcd.print(starString[!!switchSchedule[i]]);
}
}
void loadSchedule(void){
if (EEPROM.read(0)==123) { // Previous Setting Exists! Loading Procedure!
for (i=1;i<24;i++)
switchSchedule[i]=long(EEPROM.read(4*i-3)<<24)+long(EEPROM.read(4*i-2)<<16)+long(EEPROM.read(4*i-1)<<8)+long(EEPROM.read(4*i));
}
else { // Previous Setting NON-Exists! ERASING Procedure!
for (i=1;i<24;i++) {
EEPROM.update(4*i-3,0); EEPROM.update(4*i-2,0);
EEPROM.update(4*i-1,0); EEPROM.update(4*i ,0);
}
switchSchedule[i]=0;
}
}
void saveSchedule(void){
EEPROM.update(0,123); // Setting Saved Flag!
for (i=1;i<24;i++) {
EEPROM.update(4*i-3,switchSchedule[i]>>24);
EEPROM.update(4*i-2,switchSchedule[i]>>16);
EEPROM.update(4*i-1,switchSchedule[i]>>8);
EEPROM.update(4*i ,switchSchedule[i]>>0);
}
}
void setSchedule_H(void) {
delay(500);
pointHour=0;
currentState_B1=currentState_B2=currentState_B3=currentState_B4=1;
previousState_B1=previousState_B2=previousState_B3=previousState_B4=1;
while (1) {
currentState_B1=!digitalRead(B1_PIN);
currentState_B2=!digitalRead(B2_PIN);
currentState_B3=!digitalRead(B3_PIN);
currentState_B4=!digitalRead(B4_PIN);
if (currentState_B4==1 && previousState_B4==0) { // (Enter) Button Newly Pressed
previousState_B4=1; // Update Button 4 State before Function Call to Avoid Duplicate Button Press
setSchedule_M(); // Call Set Minute Schedule Funciton Call
}
else if (currentState_B3==1 && previousState_B3==0) { // (+) Button Newly Pressed
pointHour=pointHour+1;
if (pointHour>23) pointHour=0;
}
else if (currentState_B2==1 && previousState_B2==0) { // (-) Button Newly Pressed
pointHour=pointHour-1;
if (pointHour<0) pointHour=23;
}
else if (currentState_B1==1 && previousState_B1==0) { // (Exit) Button Newly Pressed
currentState_B0=previousState_B0=1; // Update Button 0 State before Function Return to Avoid Duplicate Button Press
saveSchedule();
lcd.clear();
lcd.setCursor(0,1); lcd.print("NOW, SAVING SETTINGS");
lcd.setCursor(0,2); lcd.print("Please wait");
for (i=1;i<=9;i++) { lcd.print("."); delay(150); }
return;
}
menuLCD_H();
previousState_B1=currentState_B1;
previousState_B2=currentState_B2;
previousState_B3=currentState_B3;
previousState_B4=currentState_B4;
}
}
void setSchedule_M(void) {
delay(500);
point2Minute=0;
currentState_B1=currentState_B2=currentState_B3=currentState_B4=1;
previousState_B1=previousState_B2=previousState_B3=previousState_B4=1;
// Make minuteSchedule[] array from switchSchedule[] array at current pointHour
for (i=0;i<30;i++) minuteSchedule[i]=byte(switchSchedule[pointHour]>>i)&(B00000001);
while (1) {
currentState_B1=!digitalRead(B1_PIN);
currentState_B2=!digitalRead(B2_PIN);
currentState_B3=!digitalRead(B3_PIN);
currentState_B4=!digitalRead(B4_PIN);
if (currentState_B4==1 && previousState_B4==0) { // (Change) Button Newly Pressed
if (minuteSchedule[point2Minute]==0) minuteSchedule[point2Minute]=1; // Reverse Current 2Minute On/Off Option: 0-->1 or 1-->0
else minuteSchedule[point2Minute]=0;
}
else if (currentState_B3==1 && previousState_B3==0) { // (+) Button Newly Pressed
point2Minute=point2Minute+1;
if (point2Minute>29) point2Minute=0;
}
else if (currentState_B2==1 && previousState_B2==0) { // (-) Button Newly Pressed
point2Minute=point2Minute-1;
if (point2Minute<0) point2Minute=29;
}
else if (currentState_B1==1 && previousState_B1==0) { // (Exit) Button Newly Pressed
currentState_B1=previousState_B1=1; // Update Button 1 State before Function Return to Avoid Duplicate Button Press
switchSchedule[pointHour]=0;
for(i=29;i>=0;i--) switchSchedule[pointHour]=switchSchedule[pointHour]*2L+long(minuteSchedule[i]);
return;
}
menuLCD_M();
previousState_B1=currentState_B1;
previousState_B2=currentState_B2;
previousState_B3=currentState_B3;
previousState_B4=currentState_B4;
}
}
void menuLCD_H() {
lcd.setCursor(0,0); lcd.print("HOUR SWITCH SCHEDULE");
lcd.setCursor(0,1); lcd.print("Move Cursor & Enter!");
lcd.setCursor(0,2); lcd.print(" 0-11H |");
lcd.setCursor(8,2); for (i=0;i<12;i++) lcd.print(starString[!!switchSchedule[i]]);
lcd.setCursor(0,3); lcd.print("12-23H |");
lcd.setCursor(8,3); for (i=12;i<24;i++) lcd.print(starString[!!switchSchedule[i]]);
lcd.setCursor(pointHour%12+8,pointHour/12+2);
lcd.cursor();
delay(100);
lcd.noCursor();
}
void menuLCD_M(){
lcd.setCursor(0,0); lcd.print("2MIN SWITCH SCHEDULE");
lcd.setCursor(0,1); lcd.print("Move Cursor & Change");
lcd.setCursor(0,2); lcd.print("00-M|");
lcd.setCursor(5,2); for (i=0;i<15;i++) lcd.print(starString[minuteSchedule[i]]);
lcd.setCursor(0,3); lcd.print("30-M|");
lcd.setCursor(5,3); for (i=15;i<30;i++) lcd.print(starString[minuteSchedule[i]]);
lcd.setCursor(point2Minute%15+5,point2Minute/15+2);
lcd.cursor();
delay(100);
lcd.noCursor();
}