/***************************************
Time Lock program
by: R. Stevenson
Version 1
Dec 2022
To do list:
Add code for encoder
Menu
Adjust settings
Serial Send Settings
Sleep mode
Make Memory verification (xor)
Make Serial input / output
***************************************/
//Include
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
#include "RTClib.h"
#include <Servo.h>
//
// Defines for an SSD1306 display connected to I2C (SDA, SCL pins)
// these items will NEVER change while program is running, and are replaced with the number in the program
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define SERVO_WIRE 12
#define SERVO_POWER 11
#define OLED_POWER 10
#define RTC_POWER 9
#define RTC_INT 2
#define ENCODER_CLK 3
#define ENCODER_DT 4
//#define ENCODER_SW 2
//
// Global Variables defined here
RTC_DS1307 rtc;
int pos = 0;
int lastsec=99;
int menu_a = 1;
int menu_b = 0;
DateTime now;
DateTime dtopen = DateTime(1);
unsigned long memoffset = 0;
//
char motyear[13][4] = {" ", "Jan", "Feb", "Mar", "Apr",
"May", "Jun", "Jul", "Aug",
"Sep", "Oct", "Nov", "Dec"};
char dotweek[8][4] = {"-", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
// Global 'Items' defined here
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Servo myservo;
//
void setup() {
Serial.begin(19200);
Serial.println(1);
myservo.attach(SERVO_WIRE); // attaches the servo on pin 12 to the servo object
DDRD = 0b00000110; // set pin 2 as output (Keep 1 as output and 0 as input for Tx/Rx)
PORTD |= 0b00000100; // SET PIN 2 as High
DDRD = 0b00000010; // set pin 2 as input (with the High already set makes it input_pullup)
//(Keep 1 as output and 0 as input for Tx/Rx)
// 76543210
DDRB = 0b00001110; // sets pins 9-11 (PB1-PB3) as output
PORTB |= 0b00001110; // sets pins 9-11 (PB1-PB3) as High
attachInterrupt(digitalPinToInterrupt(RTC_INT), wakeup,FALLING);
attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), wakeup,FALLING);
if(!display.begin(SSD1306_SWITCHCAPVCC,0x3C)) { // Address 0x3D for 128x64
Serial.println(F("SSD1306 failed"));
for(;;);} // Don't proceed, loop forever
if (! rtc.begin()) {
Serial.println(F("RTC failed"));
for(;;);}
// sets the initial EEPROM as the simulation does not retain from run to run
// Comment out if runing on a physical device.
setEEPROM();
memoffset = EEPROM.read(0)+EEPROM.read(1)*256;
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.display();
delay(1000);// Pause for 1 second
// yyyy, mm, dd ,hh, mm,ss
//rtc.adjust(DateTime(2022, 12, 23, 22, 0, 0));
while(1){
//Serial.println(readVcc());
now = rtc.now();
if (lastsec!=now.second()){
lastsec=now.second();
myservo.write(pos);
if(menu_a == 1){sp_1();}}
if(menu_a == 2){sp_2();}
if(menu_a == 3){sp_3();}
if(menu_a == 4){sp_4();}
if(menu_a == 5){sp_5();}
if(menu_a == 255){sp_255();}
if (Serial.available()){SerialHandler();}
}}
void SerialHandler(){
String T;
T.reserve(35);
delay(500);
T="";
while (Serial.available()){T += Serial.readString();}
T.trim();
//Serial.println(T);
if (T.startsWith(F("►CS("))){
//►Rtc_Set(2022,12,24,15,30,00)
//012345678901234567890123456789
rtc.adjust(DateTime(T.substring(6, 10).toInt(),
T.substring(11, 11+2).toInt(),
T.substring(14, 14+2).toInt(),
T.substring(17, 17+2).toInt(),
T.substring(20, 20+2).toInt(),
T.substring(23, 23+2).toInt()));
now = rtc.now();}
if (T.startsWith(F("►SS("))){
//►Sch_Set(01E0,01E0,01E0,01E0,051F,F1E0,F03C)
}
if (T.startsWith(F("►CG()"))){now = rtc.now(); Rtc_send();}
if (T.startsWith(F("►SG()"))){now = rtc.now(); Sch_send();}
if (T.startsWith(F("►ma("))){menu_a=T.substring(10, 10+2).toInt();}
}
void Rtc_send(){
Serial.print(F("◄C("));
Serial.print(now.year());Serial.print(F(","));
Serial.print(addzero(now.month()));Serial.print(now.month());Serial.print(F(","));
Serial.print(addzero(now.day()));Serial.print(now.day());Serial.print(F(","));
Serial.print(addzero(now.hour()));Serial.print(now.hour());Serial.print(F(","));
Serial.print(addzero(now.minute()));Serial.print(now.minute());Serial.print(F(","));
Serial.print(addzero(now.second()));Serial.print(now.second());Serial.println(F(")"));
Serial.flush(); }
//
void Sch_send(){
//►Sch_Get()
int ada;
int dc=0;
Serial.print(F("◄S("));
while(dc<=18){
if(dc!=0){Serial.print(F(","));}
ada = EEPROM.read(dc+memoffset+3);Serial.print(addzerohex(ada));Serial.print(ada,HEX);
ada = EEPROM.read(dc+memoffset+4);Serial.print(addzerohex(ada));Serial.print(ada,HEX);
dc+=3;}
Serial.println(F(")"));
}
//
void wakeup(){
//sleep_disable();
int coder = 0;
int newClk = digitalRead(ENCODER_CLK);
int dtValue = digitalRead(ENCODER_DT);
if (newClk == LOW && dtValue == HIGH){coder = 2;}
if (newClk == LOW && dtValue == LOW) {coder = 3;}
newClk = digitalRead(RTC_INT);
if(newClk==LOW){coder = 1;}
if(menu_a==1 and coder==1) {menu_a=2;menu_b=0;coder=0;}
if(menu_a==2 and coder==2) {menu_b++;menu_b=menu_b%4;coder=0;}
if(menu_a==2 and coder==3) {menu_b=menu_b+3;menu_b=menu_b%4;coder=0;}
if(menu_a==2 and menu_b==0 and coder==1) {menu_a=255;menu_b=0;coder=0;dtnext();}
if(menu_a==2 and menu_b==1 and coder==1) {menu_a=3;menu_b=0;coder=0;dtnext();}
if(menu_a==2 and menu_b==2 and coder==1) {menu_a=4;menu_b=0;coder=0;}
if(menu_a==2 and menu_b==3 and coder==1) {menu_a=1;menu_b=0;coder=0;}
if(menu_a==3 and coder==1) {menu_b++;menu_b=menu_b%5;coder=0;}
if(menu_a==3 and menu_b==0 and coder==2) {dtopen=DateTime(dtopen.unixtime()+3600);}
if(menu_a==3 and menu_b==0 and coder==3) {dtopen=DateTime(dtopen.unixtime()-3600);}
if(menu_a==3 and menu_b==1 and coder==2) {dtopen=DateTime(dtopen.unixtime()+60);}
if(menu_a==3 and menu_b==1 and coder==3) {dtopen=DateTime(dtopen.unixtime()-60);}
if(menu_a==3 and menu_b==2 and coder==2) {dtopen=DateTime(dtopen.unixtime()+86400);}
if(menu_a==3 and menu_b==2 and coder==3) {dtopen=DateTime(dtopen.unixtime()-86400);}
if(menu_a==3 and menu_b==3 and coder==2) {menu_a=255;menu_b=0;coder=0;}
if(menu_a==3 and menu_b==3 and coder==3) {menu_a=255;menu_b=0;coder=0;}
if(menu_a==3 and menu_b==4 and coder==2) {menu_a=1;menu_b=0;coder=0;}
if(menu_a==3 and menu_b==4 and coder==3) {menu_a=1;menu_b=0;coder=0;}
if(menu_a==4 and coder==2) {menu_b++;menu_b=menu_b%6;coder=0;}
if(menu_a==4 and coder==3) {menu_b=menu_b+5;menu_b=menu_b%6;coder=0;}
if(menu_a==4 and menu_b==0 and coder==1) {coder=0;} // schedule
if(menu_a==4 and menu_b==1 and coder==1) {coder=0;Sch_send();} // xmit schedule
if(menu_a==4 and menu_b==2 and coder==1) {menu_a=5;menu_b=0;coder=0;dtopen = now;} // DT
if(menu_a==4 and menu_b==3 and coder==1) {coder=0;Rtc_send();}
if(menu_a==4 and menu_b==4 and coder==1) {coder=0;} // Aging
if(menu_a==4 and menu_b==5 and coder==1) {menu_a=1;menu_b=0;coder=0;}
if(menu_a==5 and coder==1) {menu_b++;menu_b=menu_b%7;coder=0;}
if(menu_a==5 and menu_b==0 and coder==2) {menu_b=menu_b+10;dtopen = DateTime(now.unixtime()+3600);}
if(menu_a==5 and menu_b==0 and coder==3) {menu_b=menu_b+10;dtopen = DateTime(now.unixtime()-3600);}
if(menu_a==5 and menu_b==1 and coder==2) {menu_b=menu_b+10;dtopen = DateTime(now.unixtime()+60);}
if(menu_a==5 and menu_b==1 and coder==3) {menu_b=menu_b+10;dtopen = DateTime(now.unixtime()-60);}
if(menu_a==5 and menu_b==2 and coder==2) {menu_b=menu_b+10;dtopen = DateTime(now.unixtime()+1);}
if(menu_a==5 and menu_b==2 and coder==3) {menu_b=menu_b+10;dtopen = DateTime(now.unixtime()-1);}
if(menu_a==5 and menu_b==3 and coder==2) {menu_b=menu_b+10;dtopen = DateTime(now.unixtime()+86400);}
if(menu_a==5 and menu_b==3 and coder==3) {menu_b=menu_b+10;dtopen = DateTime(now.unixtime()-86400);}
if(menu_a==5 and menu_b==4 and coder==2) {menu_b=menu_b+10;dtopen = DateTime(now.unixtime()+1);}
if(menu_a==5 and menu_b==4 and coder==3) {menu_b=menu_b+10;dtopen = DateTime(now.unixtime()-1);}
if(menu_a==5 and menu_b==5 and coder==2) {menu_b=menu_b+10;dtopen = DateTime(now.unixtime()+1);}
if(menu_a==5 and menu_b==5 and coder==3) {menu_b=menu_b+10;dtopen = DateTime(now.unixtime()-1);}
if(menu_a==5 and menu_b==6 and coder==2) {menu_a=1;menu_b=0;coder=0;}
if(menu_a==5 and menu_b==6 and coder==3) {menu_a=1;menu_b=0;coder=0;}
//Serial.print(menu_a);Serial.print(" ");Serial.print(menu_b);Serial.print(" ");Serial.println(coder);
}
// put this back in if I want to read VCC..
/*
long readVcc() {
long result;
// Read 1.1V reference against AVcc
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
delay(2);
// Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Convert
while (bit_is_set(ADCSRA,ADSC));
result = ADCL;
result |= ADCH<<8;
result = 1126400L / result;
// Back-calculate AVcc in mV
return result;
}
*/
String addzero(int a){if(a<10){return "0";}else {return "";}}
String addzerohex(int a){if(a<16){return "0";}else {return "";}}
String mtwtfss(){
String a; a.reserve(8);
if(EEPROM.read(memoffset+ 3)<16) {a = "M";}else{a = " ";}
if(EEPROM.read(memoffset+ 6)<16){a = a + "T";}else{a += " ";}
if(EEPROM.read(memoffset+ 9)<16){a = a + "W";}else{a += " ";}
if(EEPROM.read(memoffset+12)<16){a = a + "T";}else{a += " ";}
if(EEPROM.read(memoffset+15)<16){a = a + "F";}else{a += " ";}
if(EEPROM.read(memoffset+18)<16){a = a + "S";}else{a += " ";}
if(EEPROM.read(memoffset+21)<16){a = a + "S";}else{a += " ";}
return a;
}
//
void dtnext(){
unsigned long ada;
int dd = 0; int ww = 1;
dtopen = DateTime(now.year(), now.month(), now.day(), 0, 0, 0); //adjusts dtopen to now for default screen will adjust to next open
while(ww<15){
dd = 1+((6+dtopen.dayOfTheWeek()) % 7);
if(EEPROM.read(memoffset+3*dd)<16){
ada = EEPROM.read(((memoffset)+3*(dd)) +1 )+EEPROM.read(((memoffset)+3*(dd)))*256;
dtopen = DateTime(dtopen.unixtime()+ada*60);
if(dtopen>now){return;}
}
dtopen = DateTime(dtopen.year(), dtopen.month(),1+dtopen.day(), 0, 0, 0);
ww++;}
}
//
void sp_255(){ String b = String();
pos=180;
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(WHITE); // Draw white text
display.setCursor(0,0);
display.println(F("Armed")); //top line
//Next open time (hh:mm)
display.setCursor(0,48); // pos 0,48
display.print(addzero(dtopen.hour()));display.print(dtopen.hour());display.print(":");
display.print(addzero(dtopen.minute()));display.print(dtopen.minute());
//Next open (dd-mmm-yyy)
display.setCursor(0,56); // pos 0,56
display.print(addzero(dtopen.day())); display.print( dtopen.day()); display.print(dotweek[0]);
display.print(motyear[dtopen.month()]); display.print(dotweek[0]); display.print(dtopen.year());
//current time (now HH:mm:ss)
display.setTextSize(2.5); display.setCursor(0,15);
display.print(motyear[0]);
display.print(addzero(now.hour())); display.print(now.hour()); display.print(F(":"));
display.print(addzero(now.minute())); display.print(now.minute()); display.print(F(":"));
display.print(addzero(now.second())); display.print(now.second());
if(now>dtopen){pos=0;menu_a=1;}else{now=rtc.now();}
//don't forget to display what you just printed :-D
display.display();
}
void sp_5(){
if (menu_b>=10){rtc.adjust(dtopen);now=rtc.now();menu_b=menu_b-10;}
display.clearDisplay();
//Top Line (now dd-mmm-yyy)
display.setTextSize(1);
display.setCursor(0,0);
display.setTextColor (menu_b!=3,menu_b==3);display.print(addzero(now.day()));display.print(now.day());display.setTextColor (1,0); //Day
display.print(dotweek[0]);
display.setTextColor (menu_b!=4,menu_b==4);display.print(motyear[now.month()]);display.setTextColor (1,0); //Month
display.print(dotweek[0]);
display.setTextColor (menu_b!=5,menu_b==5);display.print(now.year());display.setTextColor (1,0);display.print(motyear[0]); //Year
display.setTextColor (menu_b!=6,menu_b==6);display.print(F("Exit"));display.setTextColor (1,0);
//current time (now HH:mm:ss)
display.setTextSize(2.5); display.setCursor(0,15);
display.print(motyear[0]);
display.setTextColor (menu_b!=0,menu_b==0);display.print(addzero(now.hour())); display.print(now.hour());display.setTextColor (1,0);
display.print(F(":"));
display.setTextColor (menu_b!=1,menu_b==1);display.print(addzero(now.minute())); display.print(now.minute());display.setTextColor (1,0);
display.print(F(":"));
display.setTextColor (menu_b!=2,menu_b==2);display.print(addzero(now.second())); display.print(now.second());display.setTextColor (1,0);
//don't forget to display what you just printed :-D
display.display();
}
void sp_4(){
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(WHITE); // Draw white text
display.setCursor(0,0);
display.println(F("Settings")); //top line
display.println();
display.setTextColor (menu_b!=0,menu_b==0);display.println(F("Schedule"));
display.setTextColor (menu_b!=1,menu_b==1);display.println(F("Transmit Schedule"));
display.setTextColor (menu_b!=2,menu_b==2);display.println(F("Date / Time"));
display.setTextColor (menu_b!=3,menu_b==3);display.println(F("Transmit DT"));
display.setTextColor (menu_b!=4,menu_b==4);display.println(F("Aging register"));
display.setTextColor (menu_b!=5,menu_b==5);display.println(F("Exit"));
//don't forget to display what you just printed :-D
display.display();
}
void sp_3(){
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(WHITE); // Draw white text
display.setCursor(0,0);
display.println(F("P2"));
//Next open (dd-mmm-yyy)
display.setTextSize(1);display.setCursor(0,56);
display.setTextColor (menu_b!=2,menu_b==2); display.print(addzero(dtopen.day())); display.print( dtopen.day());
display.setTextColor (1,0);display.print(dotweek[0]); display.print(motyear[dtopen.month()]);
display.setTextColor (1,0);display.print(dotweek[0]); display.print(dtopen.year()); display.print(motyear[0]);
display.setTextColor (menu_b!=3,menu_b==3); display.print(F("Arm"));
display.setTextColor (1,0); display.print(F(" "));
display.setTextColor (menu_b!=4,menu_b==4); display.print(F("Exit"));
display.setTextColor (1,0);
//current time (now HH:mm)
display.setTextSize(2.5); display.setCursor(0,15); display.print(motyear[0]);
display.setTextColor (menu_b!=0,menu_b==0); display.print(addzero(dtopen.hour())); display.print(dtopen.hour());
display.setTextColor (1,0); display.print(F(":"));
display.setTextColor (menu_b!=1,menu_b==1);display.print(addzero(dtopen.minute())); display.print(dtopen.minute());
display.setTextColor (1,0);
//don't forget to display what you just printed :-D
display.display();
}
void sp_2(){
String b = String();
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(WHITE); // Draw white text
display.setCursor(0,0);
display.println(F("Menu")); //top line
if (menu_b==0){b=dotweek[0];}else{b=motyear[0];} b = b + F("P1");display.println(b);
if (menu_b==1){b=dotweek[0];}else{b=motyear[0];} b = b + F("P2");display.println(b);
if (menu_b==2){b=dotweek[0];}else{b=motyear[0];} b = b + F("Settings");display.println(b);
if (menu_b==3){b=dotweek[0];}else{b=motyear[0];} b = b + F("Exit");display.println(b);
//don't forget to display what you just printed :-D
display.display();
}
void sp_1(){
unsigned long ada;
//dtopen = DateTime(2011, 11, 5, 8, 0, 0); //dtopen builder
//dtopen = DateTime(1671544067);
dtnext();
display.clearDisplay(); // Clear screen
//Top Line (now dd-mmm-yyy)
display.setTextSize(1);display.setTextColor(WHITE); // Size 1, white text
display.setCursor(0,0); // pos 0,0
display.print(addzero(now.day()));display.print(now.day());display.print(dotweek[0]);display.print(motyear[now.month()]);display.print(dotweek[0]);display.print(now.year());
//Next open time (hh:mm)
display.setCursor(0,48); // pos 0,48
display.print(addzero(dtopen.hour()));display.print(dtopen.hour());display.print(":");display.print(addzero(dtopen.minute()));display.print(dtopen.minute());
//Next open (dd-mmm-yyy) then the week schedule mtwtfss()
display.setCursor(0,56); // pos 0,56
display.print(addzero(dtopen.day())); display.print( dtopen.day()); display.print(dotweek[0]);
display.print(motyear[dtopen.month()]); display.print(dotweek[0]); display.print(dtopen.year());
display.print(motyear[0]);
display.println(mtwtfss());
//current time (now HH:mm:ss)
display.setTextSize(2.5); display.setCursor(0,15);
display.print(motyear[0]);
display.print(addzero(now.hour())); display.print(now.hour()); display.print(F(":"));
display.print(addzero(now.minute())); display.print(now.minute()); display.print(F(":"));
display.print(addzero(now.second())); display.print(now.second());
//don't forget to display what you just printed :-D
display.display();
}
void setEEPROM(){
EEPROM.write(0,0x00); //Address offset
EEPROM.write(1,0x00); //Address offset * 256
EEPROM.write(2,0xE1); //CheckSum
EEPROM.write(3,0x01); //Monday
EEPROM.write(4,0xE0); //Monday
EEPROM.write(5,0xE1); //CheckSum
EEPROM.write(6,0x01); //Tuesday
EEPROM.write(7,0xE0); //Tuesday
EEPROM.write(8,0xE1); //CheckSum
EEPROM.write(9,0x01); //Wednesday
EEPROM.write(10,0xE0); //Wednesday
EEPROM.write(11,0xE1); //CheckSum
EEPROM.write(12,0x01); //Thursday
EEPROM.write(13,0xE0); //Thursday
EEPROM.write(14,0x1A); //CheckSum
EEPROM.write(15,0x05); //Friday
EEPROM.write(16,0x1F); //Friday
EEPROM.write(17,0x11); //CheckSum
EEPROM.write(18,0xF1); //Saturday
EEPROM.write(19,0xE0); //Saturday
EEPROM.write(20,0xCC); //CheckSum
EEPROM.write(21,0xF0); //Sunday
EEPROM.write(22,0x3C); //Sunday
EEPROM.write(23,0x00); //CheckSum
EEPROM.write(24,0x00); //Epoch ar
EEPROM.write(25,0x00); //Epoch ar
EEPROM.write(26,0x00); //Epoch ar
EEPROM.write(27,0x00); //Epoch ar
EEPROM.write(28,0x00); //CheckSum
EEPROM.write(29,0x00); //Epoch disarm
EEPROM.write(30,0x00); //Epoch disarm
EEPROM.write(31,0x00); //Epoch disarm
EEPROM.write(32,0x00); //Epoch disarm
EEPROM.write(33,0x00); //CheckSum
EEPROM.write(34,0x00); //Epoch Wake / Sleep
EEPROM.write(35,0x00); //Epoch Wake / Sleep
EEPROM.write(36,0x00); //Epoch Wake / Sleep
EEPROM.write(37,0x00); //Epoch Wake / Sleep
}