#include <Keypad.h>
#include <Wire.h>
#include "LiquidCrystal_I2C.h"
LiquidCrystal_I2C lcd(0x27, 16, 2);
const byte ROWS = 4;
const byte COLS = 4;
char keys [ROWS] [COLS] = { // Characters of the Keypad
{'1', '2', '3', '+'},
{'4', '5', '6', '-'},
{'7', '8', '9', '*'},
{'C', '0', '=', '/'}
};
byte rowPins[ROWS]={6, 7, 8, 9};
byte colPins[COLS]={5, 4, 3, 2};
Keypad myKeypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
boolean presentValue = false;
boolean next = false;
boolean final = false;
String num1, num2;
float answer ;
char op;
#define button_B A0
#define button_D A1
#define button_U A2
#define button_E A3
#define ONBOARD_LED 13
#define DEFAULT_DELAY 300
char buttonPressed = '0';
byte menuLevel = 0; // Level 0: no menu display, display anything you like
// Level 1: display main menu
// Level 2: display sub menu
byte menu = 1;
byte sub = 1;
unsigned long relay_val_1 = 0;
unsigned long relay_val_2 = 0;
unsigned long relay_val_3 = 0;
bool LED_STATE = false;
bool currState_B = HIGH;
bool currState_D = HIGH;
bool currState_U = HIGH;
bool currState_E = HIGH;
bool prevState_B = HIGH;
bool prevState_D = HIGH;
bool prevState_U = HIGH;
bool prevState_E = HIGH;
unsigned long prevTime_B = 0;
unsigned long prevTime_D = 0;
unsigned long prevTime_U = 0;
unsigned long prevTime_E = 0;
unsigned long waitTime_B = 50;
unsigned long waitTime_D = 50;
unsigned long waitTime_U = 50;
unsigned long waitTime_E = 50;
const int button1 = A3; // button1 pin number
const int button2 = A1; // button1 pin number
const int button3 = A2; // button1 pin number
const int alarm_pin = 10; // Alarms pin number
void setup() {
lcd.begin(16,2);
lcd.init();
Serial.begin(9600);
pinMode(button_B, INPUT_PULLUP);
pinMode(button_D, INPUT_PULLUP);
pinMode(button_U, INPUT_PULLUP);
pinMode(button_E, INPUT_PULLUP);
pinMode(A4,INPUT);
pinMode(A3, INPUT_PULLUP);
pinMode(A1, INPUT_PULLUP);
pinMode(A2, INPUT_PULLUP);
pinMode(10, OUTPUT);
digitalWrite(alarm_pin, LOW);
// set up the LCD's number of columns and rows
lcd.begin(16,2);
Wire.begin(); // Join i2c bus
attachInterrupt(digitalPinToInterrupt(2), Alarm, FALLING);
pinMode(ONBOARD_LED, OUTPUT);
digitalWrite(ONBOARD_LED, LED_STATE);
showHomeScreen();
}
bool alarm1_status, alarm2_status;
char Time[] = " : : ",
calendar[] = " / /20 ",
alarm1[] = "A1: : :00", alarm2[] = "A2: : :00",
temperature[] = "T: . C";
byte i, second, minute, hour, day, date, month, year,
alarm1_minute, alarm1_hour, alarm2_minute, alarm2_hour,
status_reg;
void Alarm(){
digitalWrite(alarm_pin, HIGH);
}
void DS3231_read(){ // Function to read time & calendar data
Wire.beginTransmission(0x68); // Start I2C protocol with DS3231 address
Wire.write(0); // Send register address
Wire.endTransmission(false); // I2C restart
Wire.requestFrom(0x68, 7); // Request 7 bytes from DS3231 and release I2C bus at end of reading
second = Wire.read(); // Read seconds from register 0
minute = Wire.read(); // Read minuts from register 1
hour = Wire.read(); // Read hour from register 2
day = Wire.read(); // Read day from register 3
date = Wire.read(); // Read date from register 4
month = Wire.read(); // Read month from register 5
year = Wire.read(); // Read year from register 6
}
void alarms_read_display(){ // Function to read and display alarm1, alarm2 and temperature data
byte control_reg, temperature_lsb;
char temperature_msb;
Wire.beginTransmission(0x68); // Start I2C protocol with DS3231 address
Wire.write(0x08); // Send register address
Wire.endTransmission(false); // I2C restart
Wire.requestFrom(0x68, 11); // Request 11 bytes from DS3231 and release I2C bus at end of reading
alarm1_minute = Wire.read(); // Read alarm1 minutes
alarm1_hour = Wire.read(); // Read alarm1 hours
Wire.read(); // Skip alarm1 day/date register
alarm2_minute = Wire.read(); // Read alarm2 minutes
alarm2_hour = Wire.read(); // Read alarm2 hours
Wire.read(); // Skip alarm2 day/date register
control_reg = Wire.read(); // Read the DS3231 control register
status_reg = Wire.read(); // Read the DS3231 status register
Wire.read(); // Skip aging offset register
temperature_msb = Wire.read(); // Read temperature MSB
temperature_lsb = Wire.read(); // Read temperature LSB
// Convert BCD to decimal
alarm1_minute = (alarm1_minute >> 4) * 10 + (alarm1_minute & 0x0F);
alarm1_hour = (alarm1_hour >> 4) * 10 + (alarm1_hour & 0x0F);
alarm2_minute = (alarm2_minute >> 4) * 10 + (alarm2_minute & 0x0F);
alarm2_hour = (alarm2_hour >> 4) * 10 + (alarm2_hour & 0x0F);
// End conversion
alarm1[8] = alarm1_minute % 10 + 48;
alarm1[7] = alarm1_minute / 10 + 48;
alarm1[5] = alarm1_hour % 10 + 48;
alarm1[4] = alarm1_hour / 10 + 48;
alarm2[8] = alarm2_minute % 10 + 48;
alarm2[7] = alarm2_minute / 10 + 48;
alarm2[5] = alarm2_hour % 10 + 48;
alarm2[4] = alarm2_hour / 10 + 48;
alarm1_status = bitRead(control_reg, 0); // Read alarm1 interrupt enable bit (A1IE) from DS3231 control register
alarm2_status = bitRead(control_reg, 1); // Read alarm2 interrupt enable bit (A2IE) from DS3231 control register
if(temperature_msb < 0){
temperature_msb = abs(temperature_msb);
temperature[2] = '-';
}
else
temperature[2] = ' ';
temperature_lsb >>= 6;
temperature[4] = temperature_msb % 10 + 48;
temperature[3] = temperature_msb / 10 + 48;
if(temperature_lsb == 0 || temperature_lsb == 2){
temperature[7] = '0';
if(temperature_lsb == 0) temperature[6] = '0';
else temperature[6] = '5';
}
if(temperature_lsb == 1 || temperature_lsb == 3){
temperature[7] = '5';
if(temperature_lsb == 1) temperature[6] = '2';
else temperature[6] = '7';
}
temperature[8] = 223; // Put the degree symbol
lcd.setCursor(10, 0);
lcd.print(temperature); // Display temperature
lcd.setCursor(0, 2);
lcd.print(alarm1); // Display alarm1
lcd.setCursor(17, 2);
if(alarm1_status) lcd.print("ON "); // If A1IE = 1 print 'ON'
else lcd.print("OFF"); // If A1IE = 0 print 'OFF'
lcd.setCursor(0, 0);
lcd.print(alarm2); // Display alarm2
lcd.setCursor(17, 2);
if(alarm2_status) lcd.print("ON "); // If A2IE = 1 print 'ON'
else lcd.print("OFF"); // If A2IE = 0 print 'OFF'
}
void calendar_display(){ // Function to display calendar
switch(day){
case 1: strcpy(calendar, "Sun / /20 "); break;
case 2: strcpy(calendar, "Mon / /20 "); break;
case 3: strcpy(calendar, "Tue / /20 "); break;
case 4: strcpy(calendar, "Wed / /20 "); break;
case 5: strcpy(calendar, "Thu / /20 "); break;
case 6: strcpy(calendar, "Fri / /20 "); break;
case 7: strcpy(calendar, "Sat / /20 "); break;
default: strcpy(calendar, "Sat / /20 ");
}
calendar[13] = year % 10 + 48;
calendar[12] = year / 10 + 48;
calendar[8] = month % 10 + 48;
calendar[7] = month / 10 + 48;
calendar[5] = date % 10 + 48;
calendar[4] = date / 10 + 48;
lcd.setCursor(0, 1);
lcd.print(calendar); // Display calendar
}
void DS3231_display(){
// Convert BCD to decimal
second = (second >> 4) * 10 + (second & 0x0F);
minute = (minute >> 4) * 10 + (minute & 0x0F);
hour = (hour >> 4) * 10 + (hour & 0x0F);
date = (date >> 4) * 10 + (date & 0x0F);
month = (month >> 4) * 10 + (month & 0x0F);
year = (year >> 4) * 10 + (year & 0x0F);
// End conversion
Time[7] = second % 10 + 48;
Time[6] = second / 10 + 48;
Time[4] = minute % 10 + 48;
Time[3] = minute / 10 + 48;
Time[1] = hour % 10 + 48;
Time[0] = hour / 10 + 48;
calendar_display(); // Call calendar display function
lcd.setCursor(0, 0);
lcd.print(Time); // Display time
}
void Blink(){
byte j = 0;
while(j < 10 && (digitalRead(button1) || i >= 5) && digitalRead(button2) && (digitalRead(button3) || i < 5)){
j++;
delay(25);
}
}
byte edit(byte x, byte y, byte parameter){
char text[3];
while(!digitalRead(button1) || !digitalRead(button3)); // Wait until button B1 is released
while(true){
while(!digitalRead(button2)){ // If button B2 is pressed
parameter++;
if(((i == 0) || (i == 5)) && parameter > 23) // If hours > 23 ==> hours = 0
parameter = 0;
if(((i == 1) || (i == 6)) && parameter > 59) // If minutes > 59 ==> minutes = 0
parameter = 0;
if(i == 2 && parameter > 31) // If date > 31 ==> date = 1
parameter = 1;
if(i == 3 && parameter > 12) // If month > 12 ==> month = 1
parameter = 1;
if(i == 4 && parameter > 99) // If year > 99 ==> year = 0
parameter = 0;
if(i == 7 && parameter > 1) // For alarms ON or OFF (1: alarm ON, 0: alarm OFF)
parameter = 0;
lcd.setCursor(x, y);
if(i == 7){ // For alarms ON & OFF
if(parameter == 1) lcd.print("ON ");
else lcd.print("OFF");
}
else{
sprintf(text,"%02u", parameter);
lcd.print(text);
}
if(i >= 5){
DS3231_read(); // Read data from DS3231
DS3231_display(); // Display DS3231 time and calendar
}
delay(200); // Wait 200ms
}
lcd.setCursor(x, y);
lcd.print(" "); // Print two spaces
if(i == 7) lcd.print(" "); // Print space (for alarms ON & OFF)
Blink(); // Call Blink function
lcd.setCursor(x, y);
if(i == 7){ // For alarms ON & OFF
if(parameter == 1) lcd.print("ON ");
else lcd.print("OFF");
}
else{
sprintf(text,"%02u", parameter);
lcd.print(text);
}
Blink();
if(i >= 5){
DS3231_read();
DS3231_display();}
if((!digitalRead(button1) && i < 5) || (!digitalRead(button3) && i >= 5)){
i++; // Increment 'i' for the next parameter
return parameter; // Return parameter value and exit
} lcd.setCursor(0, 1);
}
}
void buttonRTC() {
if(!digitalRead(button1)){ // If B1 button is pressed
i = 0;
hour = edit(0, 0, hour);
minute = edit(3, 0, minute);
while(!digitalRead(button1)); // Wait until button B1 released
while(true){
while(!digitalRead(button2)){ // If button B2 button is pressed
day++; // Increment day
if(day > 7) day = 1;
calendar_display(); // Call display_calendar function
lcd.print(calendar); // Display calendar
delay(200);
}
lcd.setCursor(0, 1);
lcd.print(" "); // Print 3 spaces
Blink();
lcd.setCursor(0, 1);
lcd.print(calendar); // Print calendar
Blink(); // Call Blink function
if(!digitalRead(button1)) // If button B1 is pressed
break;
}
date = edit(4, 1, date); // Edit date
month = edit(7, 1, month); // Edit month
year = edit(12, 1, year); // Edit year
// Convert decimal to BCD
minute = ((minute / 10) << 4) + (minute % 10);
hour = ((hour / 10) << 4) + (hour % 10);
date = ((date / 10) << 4) + (date % 10);
month = ((month / 10) << 4) + (month % 10);
year = ((year / 10) << 4) + (year % 10);
// End conversion
// Write time & calendar data to DS3231 RTC
Wire.beginTransmission(0x68); // Start I2C protocol with DS3231 address
Wire.write(0); // Send register address
Wire.write(0); // Reset sesonds and start oscillator
Wire.write(minute); // Write minute
Wire.write(hour); // Write hour
Wire.write(day); // Write day
Wire.write(date); // Write date
Wire.write(month); // Write month
Wire.write(year); // Write year
Wire.endTransmission(); // Stop transmission and release the I2C bus
delay(200);
}
if(!digitalRead(button3)){ // If B3 button is pressed
while(!digitalRead(button3)); // Wait until button B3 released
i = 5;
alarm1_hour = edit(4, 2, alarm1_hour);
alarm1_minute = edit(7, 2, alarm1_minute);
alarm1_status = edit(17, 2, alarm1_status);
i = 5;
alarm2_hour = edit(4, 3, alarm2_hour);
alarm2_minute = edit(7, 3, alarm2_minute);
alarm2_status = edit(17, 3, alarm2_status);
alarm1_minute = ((alarm1_minute / 10) << 4) + (alarm1_minute % 10);
alarm1_hour = ((alarm1_hour / 10) << 4) + (alarm1_hour % 10);
alarm2_minute = ((alarm2_minute / 10) << 4) + (alarm2_minute % 10);
alarm2_hour = ((alarm2_hour / 10) << 4) + (alarm2_hour % 10);
// Write alarms data to DS3231
Wire.beginTransmission(0x68); // Start I2C protocol with DS3231 address
Wire.write(7); // Send register address (alarm1 seconds)
Wire.write(0); // Write 0 to alarm1 seconds
Wire.write(alarm1_minute); // Write alarm1 minutes value to DS3231
Wire.write(alarm1_hour); // Write alarm1 hours value to DS3231
Wire.write(0x80); // Alarm1 when hours, minutes, and seconds match
Wire.write(alarm2_minute); // Write alarm2 minutes value to DS3231
Wire.write(alarm2_hour); // Write alarm2 hours value to DS3231
Wire.write(0x80); // Alarm2 when hours and minutes match
Wire.write(4 | alarm1_status | (alarm2_status << 1)); // Write data to DS3231 control register (enable interrupt when alarm)
Wire.write(0); // Clear alarm flag bits
Wire.endTransmission(); // Stop transmission and release the I2C bus
delay(200); // Wait 200ms
}
if(!digitalRead(button2) && digitalRead(alarm_pin)){ // When button B2 pressed with alarm (Reset and turn OFF the alarm)
digitalWrite(alarm_pin, LOW); // Turn OFF the alarm indicator
Wire.beginTransmission(0x68); // Start I2C protocol with DS3231 address
Wire.write(0x0E); // Send register address (control register)
// Write data to control register (Turn OFF the occurred alarm and keep the other as it is)
Wire.write(4 | (!bitRead(status_reg, 0) & alarm1_status) | ((!bitRead(status_reg, 1) & alarm2_status) << 1));
Wire.write(0); // Clear alarm flag bits
Wire.endTransmission(); // Stop transmission and release the I2C bus
}
// Read time and calendar parameters from DS3231 RTC
// Read and display alarms parameters
// Display time & // Wait 50ms
}
void loop() {
checkButton();
callculator();
// You can do other things here below
}
void callculator(){
char key = myKeypad.getKey();
if (key != NO_KEY && (key == '1' || key == '2' || key == '3' || key == '4' || key == '5' || key == '6' || key == '7' || key == '8' || key == '9' || key == '0'))
{
if (presentValue != true)
{
num1 = num1 + key;
int numLength = num1.length();
lcd.setCursor(0, 0);
lcd.print(num1);
}
else
{
num2 = num2 + key;
int numLength = num2.length();
int numLength1 = num1.length();
lcd.setCursor(1 + numLength1, 0);
lcd.print(num2);
final = true;
}
}
else if (presentValue == false && key != NO_KEY && (key == '/' || key == '*' || key == '-' || key == '+'))
{
if (presentValue == false)
{
int numLength = num1.length();
presentValue = true;
op = key;
lcd.setCursor(0 + numLength, 0);
lcd.print(op);
}
}
else if (final == true && key != NO_KEY && key == '=') {
if (op == '+') {
answer = num1.toInt() + num2.toInt(); // Addition input
}
else if (op == '-') {
answer = num1.toInt() - num2.toInt(); // Subtraction input
}
else if (op == '*') {
answer = num1.toInt() * num2.toInt(); // Multiplication input
}
else if (op == '/') {
answer = num1.toInt() / num2.toInt(); // Division input
}
lcd.clear();
lcd.setCursor(0,0);
lcd.print(num1);
lcd.print(op);
lcd.print(num2);
lcd.setCursor(0,1);
lcd.print("=");
lcd.print(answer); // Printing answer on LCD screen
presentValue = true;
}
else if (key != NO_KEY && key == 'C') { // Clearing input to clear everything on LCD screen
lcd.clear();
presentValue = false;
final = false;
num1 = "";
num2 = "";
answer = 0;
op = ' ';
}
}
void checkButton() {
// Button Debouncing
bool currRead_B = digitalRead(button_B);
bool currRead_D = digitalRead(button_D);
bool currRead_U = digitalRead(button_U);
bool currRead_E = digitalRead(button_E);
if (currRead_B != prevState_B) {
prevTime_B = millis();
}
if (currRead_D != prevState_D) {
prevTime_D = millis();
}
if (currRead_U != prevState_U) {
prevTime_U = millis();
}
if (currRead_E != prevState_E) {
prevTime_E = millis();
}
if ((millis() - prevTime_B) > waitTime_B) {
if (currRead_B != currState_B) {
currState_B = currRead_B;
if (currState_B == LOW) {
buttonPressed = 'B';
}
}
} else buttonPressed = '0';
if ((millis() - prevTime_D) > waitTime_D) {
if (currRead_D != currState_D) {
currState_D = currRead_D;
if (currState_D == LOW) {
buttonPressed = 'D';
}
}
} else buttonPressed = '0';
if ((millis() - prevTime_U) > waitTime_U) {
if (currRead_U != currState_U) {
currState_U = currRead_U;
if (currState_U == LOW) {
buttonPressed = 'U';
} else {
//buttonPressed = '0';
}
}
} else buttonPressed = '0';
if ((millis() - prevTime_E) > waitTime_E) {
if (currRead_E != currState_E) {
currState_E = currRead_E;
if (currState_E == LOW) {
buttonPressed = 'E';
}
}
} else buttonPressed = '0';
prevState_B = currRead_B;
prevState_D = currRead_D;
prevState_U = currRead_U;
prevState_E = currRead_E;
processButton(buttonPressed);
}
void processButton(char buttonPressed) {
switch(menuLevel) {
case 0: // Level 0
switch ( buttonPressed ) {
case 'E': // Enter
menuLevel = 1;
menu = 1;
updateMenu();
delay(DEFAULT_DELAY);
break;
case 'U': // Up
break;
case 'D': // Down
break;
case 'B': // Back
break;
default:
break;
}
break;
case 1: // Level 1, main menu
switch ( buttonPressed ) {
case 'E': // Enter
updateSub();
menuLevel = 2; // go to sub menu
updateSub();
delay(DEFAULT_DELAY);
break;
case 'U': // Up
menu++;
updateMenu();
delay(DEFAULT_DELAY);
break;
case 'D': // Down
menu--;
updateMenu();
delay(DEFAULT_DELAY);
break;
case 'B': // Back
menuLevel = 0; // hide menu, go back to level 0
showHomeScreen();
delay(DEFAULT_DELAY);
break;
default:
break;
}
break;
case 2: // Level 2, sub menu
switch ( buttonPressed ) {
case 'E':
menuLevel = 1;
updateMenu();
delay(DEFAULT_DELAY);
break;
case 'U': // U
if (menu == 1) {
if (relay_val_1 < 3600000) { // 1 hour max
relay_val_1 = relay_val_1 + 1;
} else {
relay_val_1 = 3600000;
}
} else if (menu == 2) {
if (relay_val_2 < 3600000) { // 1 hour max
relay_val_2 = relay_val_2 + 1;
} else {
relay_val_2 = 3600000;
}
} else if (menu == 3) {
if (relay_val_3 < 3600000) { // 1 hour max
relay_val_3 = relay_val_3 + 1;
} else {
relay_val_3 = 3600000;
}
}
updateSub();
delay(DEFAULT_DELAY);
break;
case 'D': // D
if (menu == 1) {
if (relay_val_1 == 0) {
relay_val_1 = 0;
} else {
relay_val_1 = relay_val_1 - 1;
}
} else if (menu == 2) {
if (relay_val_2 == 0) {
relay_val_2 = 0;
} else {
relay_val_2 = relay_val_2 - 1;
}
} else if (menu == 3) {
if (relay_val_3 == 0) {
relay_val_3 = 0;
} else {
relay_val_3 = relay_val_3 - 1;
}
}
updateSub();
delay(DEFAULT_DELAY);
break;
case 'B': // L
menuLevel = 1; // go back to main menu
updateMenu();
delay(DEFAULT_DELAY);
break;
default:
break;
}
break;
case 3: // Level 3, sub menu of sub menu
break;
default:
break;
}
}
void updateMenu() {
switch (menu) {
case 0:
menu = 1;
break;
case 1:
lcd.clear();
lcd.print(">JAM ");
lcd.print(relay_val_1);
lcd.setCursor(0, 1);
lcd.print(" ALARM ");
break;
case 2:
lcd.clear();
lcd.print(" JAM ");
lcd.setCursor(0, 1);
lcd.print(">ALARM ");
lcd.print(relay_val_2);
break;
case 3:
lcd.clear();
lcd.print(">KALKULATOR ");
lcd.print(relay_val_3);
lcd.setCursor(0, 1);
lcd.print(" ");
break;
case 4:
menu = 3;
break;
}
}
void updateSub() {
switch (menu) {
case 0:
break;
case 1:
lcd.clear();
void DS3231_read();
DS3231_display();
calendar_display();
break;
case 2:
lcd.clear();
alarms_read_display();
buttonRTC();
break;
case 3:
lcd.clear();
callculator();
break;
case 4:
menu = 3;
break;
}
}
void showHomeScreen() {
lcd.clear();
lcd.println(" TechToTinker ");
lcd.setCursor(0,1);
lcd.println(" - R for menu ");
}