/* 1602 LCD alarm clock
* by John Bradnam (jbrad2089@gmail.com)
*
* Display 16x2: Setup: Setup Alarm
* +----------------+ +----------------+ +----------------+
* |HH:MM:SS | HH:MM| | >HH :>MM | | Set Alarm |
* |DD/MM/YY | ALARM| |>DD />MM />YYYY | | >HH :>MM |
* +----------------+ +----------------+ +----------------+
*
* 25/06/2020
* - Took Michalis Vasilakis's clock as code base (https://www.instructables.com/id/Arduino-Digital-Clock-With-Alarm-Function-custom-P/)
* - Modified to suit hardware - DS1302 RTC and LCD backlight
* - Added support for different display styles
* - Standard screen design by Michalis Vasilakis
* - Dual Thick font by Arduino World (https://www.hackster.io/thearduinoworld/arduino-digital-clock-version-1-b1a328)
* - Dual Bevelled font by Arduino Forum (https://forum.arduino.cc/index.php/topic,8882.0.html)
* - Dual Trek font by Carrie Sundra (https://www.alpenglowindustries.com/blog/the-big-numbers-go-marching-2x2)
* - Dual Thin font by Arduino World (https://www.hackster.io/thearduinoworld/arduino-digital-clock-version-2-5bab65)
* - Word concept by LAGSILVA (https://www.hackster.io/lagsilva/text-clock-bilingual-en-pt-with-arduino-881a6e)
* 29/06/20
* - Fixed spelling mistakes in WORD clock
* - Added #defines to control backlight
* - Increased backlight timeout from 5 to 10 seconds
* 22/11/20
* - Added Birth date setup and EEPROM storage
* - Added Biorhythm clock face
* - Cleaned up Setup screen coding
* 06/12/20
* - Added DHT21 Support
* - Added Thermometer and Humidity clock face
* 28/12/20
* - Move switches to a single analog pin
* - Added a LDR to support automatic backlight (always on during daylight or lighted room)
* - Moved Word tables from SRAM to Flash to free up more RAM for more clock faces
* - Added message clock face - See msgs table to set special dates and messages
* - Added world time clock face - See Timezone definitions to set the local and world timezones
*
* Current Stats
* Sketch uses 26436 bytes (81%) of program storage space. Maximum is 32256 bytes.
* Global variables use 996 bytes (48%) of dynamic memory, leaving 1052 bytes for local variables. Maximum is 2048 bytes.
*
*/
//Libraries
#include <Wire.h>
#include <TimeLib.h>
#include <Timezone.h>
#include <DS1302RTC.h>
#include <LiquidCrystal.h>
#include <EEPROM.h>
#include <dht.h>
//uncomment if you want to see console messages
//#define DEBUG
//uncomment if you want the dual thick or thin display variant to show 12hr format
//#define DUAL_THICK_12HR
//#define DUAL_THIN_12HR
//uncomment to control backlight
//#define NO_BACKLIGHT
//#define BACKLIGHT_ALWAYS_ON
#define BACKLIGHT_LDR //Use LDR to switch on backlight when in lit up room
#define BACKLIGHT_TIMEOUT 10000
//uncomment to test biorhythm graphs
//#define TEST_BIO_GRAPHS
#define LIGHT 2 //PD2
#define LCD_D7 3 //PD3
#define LCD_D6 4 //PD4
#define LCD_D5 5 //PD5
#define LCD_D4 6 //PD6
#define LCD_E 7 //PD7
#define LCD_RS 8 //PB0
#define BUTTONS A0 //PC0
#define LDR A1 //PC1
#define BTN_TILT A2 //PC2
#define SPEAKER 11 //PB3
#define DHT21 9 //PB1
#define RTC_CE 10 //PB2
#define RTC_IO 12 //PB4
#define RTC_SCLK 13 //PB5
enum ANALOG_BUTTONS { BTN_SET, BTN_ADJUST,BTN_ALARM };
//Connections and constants
LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7);
DS1302RTC rtc(RTC_CE, RTC_IO, RTC_SCLK);
dht DHT;
const int monthDays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
long interval = 300;
int melody[] = { 600, 800, 1000,1200 };
//Variables
int DD, MM, YY, DOW, H, M, S, temp, hum, set_state, adjust_state, alarm_state, AH, AM, shake_state, BY, BM, BD;
int shakeTimes = 0;
int i = 0;
String sDD;
String sMM;
String sYY;
String sH;
String sM;
String sS;
String sBD;
String sBM;
String sBY;
String aH="12";
String aM="00";
String sTMP;
String sHUM;
//String alarm = " ";
long prevAlarmMillis = 0;
long prevDhtMillis = 0;
//Boolean flags
boolean setupScreen = false;
boolean alarmON=false;
boolean turnItOn = false;
enum STYLE { STANDARD, DUAL_THICK, DUAL_BEVEL, DUAL_TREK, DUAL_THIN, WORD, BIO, THERMO, MESSAGE, WORLD };
STYLE currentStyle = STANDARD;
enum SETUP { CLOCK, TIME_HOUR, TIME_MIN, TIME_DAY, TIME_MONTH, TIME_YEAR, BIRTH_DAY, BIRTH_MONTH, BIRTH_YEAR, ALARM_HOUR, ALARM_MIN };
SETUP setupMode = CLOCK;
bool backlightOn = false;
long backlightTimeout = 0;
byte customChar[8];
//--------------------- EEPROM ------------------------------------------
#define EEPROM_AH 0 //Alarm Hours
#define EEPROM_AM 1 //Alarm Minutes
#define EEPROM_AO 2 //Alarm On/Off
#define EEPROM_CS 3 //Current style
#define EEPROM_BY 4 //Birth Year
#define EEPROM_BM 6 //Birth Month
#define EEPROM_BD 7 //Birth Day
//--------------------- Word clock --------------------------------------
const char x0[] PROGMEM = "HUNDRED";
const char x1[] PROGMEM = "ONE";
const char x2[] PROGMEM = "TWO";
const char x3[] PROGMEM = "THREE";
const char x4[] PROGMEM = "FOUR";
const char x5[] PROGMEM = "FIVE";
const char x6[] PROGMEM = "SIX";
const char x7[] PROGMEM = "SEVEN";
const char x8[] PROGMEM = "EIGHT";
const char x9[] PROGMEM = "NINE";
const char* const units[] PROGMEM = {x0, x1, x2, x3, x4, x5, x6, x7, x8, x9};
const char x10[] PROGMEM = "TEN";
const char x11[] PROGMEM = "ELEVEN";
const char x12[] PROGMEM = "TWELVE";
const char x13[] PROGMEM = "THIRTEEN";
const char x14[] PROGMEM = "FOURTEEN";
const char x15[] PROGMEM = "FIFTEEN";
const char x16[] PROGMEM = "SIXTEEN";
const char x17[] PROGMEM = "SEVENTEEN";
const char x18[] PROGMEM = "EIGHTEEN";
const char x19[] PROGMEM = "NINETEEN";
const char* const teens[] PROGMEM = {x10, x11, x12, x13, x14, x15, x16, x17, x18, x19};
const char t00[] PROGMEM = "";
const char t10[] PROGMEM = "";
const char t20[] PROGMEM = "TWENTY";
const char t30[] PROGMEM = "THIRTY";
const char t40[] PROGMEM = "FORTY";
const char t50[] PROGMEM = "FIFTY";
const char* const tens[] PROGMEM = {t00, t10, t20, t30, t40, t50};
//---------------------- Hourglass animation ----------------------------
#define HOURGLASS_FRAMES 8
#define HOURGLASS_CHAR 0
#define FRAME_TIMEOUT 200;
int nextFrame = 0;
long frameTimeout = 0;
const byte hourglass[HOURGLASS_FRAMES][8] PROGMEM = {
{ B11111, B11111, B01010, B01010, B01010, B01010, B10001, B11111 },
{ B11111, B11011, B01110, B01010, B01010, B01010, B10001, B11111 },
{ B11111, B10001, B01110, B01110, B01010, B01010, B10001, B11111 },
{ B11111, B10001, B01010, B01110, B01110, B01010, B10001, B11111 },
{ B11111, B10001, B01010, B01010, B01110, B01110, B10001, B11111 },
{ B11111, B10001, B01010, B01010, B01010, B01110, B10101, B11111 },
{ B11111, B10001, B01010, B01010, B01010, B01110, B11011, B11111 },
{ B11111, B10001, B01010, B01010, B01010, B01010, B11111, B11111 },
//{ B11111, B10001, B01010, B01010, B01010, B01010, B10001, B11111 }
};
//---------------------- Alarm, clock and DHT custom characters ----------------------------
#define BELL_CHAR 1
#define CLOCK_CHAR 2
#define THERMOMETER_CHAR 3
#define DROPLET_CHAR 4
const byte bell[8] PROGMEM = {0x4, 0xe, 0xe, 0xe, 0x1f, 0x0, 0x4};
const byte clock[8] PROGMEM = {0x0, 0xe, 0x15, 0x17, 0x11, 0xe, 0x0};
const byte thermometer[8] PROGMEM = {0x4, 0xa, 0xa, 0xe, 0xe, 0x1f, 0x1f, 0xe};
const byte droplet[8] PROGMEM = {0x4, 0x4, 0xa, 0xa, 0x11, 0x11, 0x11, 0xe};
#define DHT_UPDATE_INTERVAL 6000
//---------------------- BioRhythm Clock ----------------------------
//Custom character constants (M is MSB or upper bar character, L is LSB or lower bar character
#define PHYSICAL_M 3
#define PHYSICAL_L 4
#define EMOTIONAL_M 5
#define EMOTIONAL_L 6
#define INTELLECTUAL_M 7
#define INTELLECTUAL_L 8
//---------------------- Thick Square Font ----------------------------
#define C0 3
#define C1 4
#define C2 5
#define C3 6
const byte C_0[8] PROGMEM = {0x1F,0x1F,0x1F,0x00,0x00,0x00,0x00,0x00};
const byte C_1[8] PROGMEM = {0x1F,0x1F,0x1F,0x00,0x00,0x1F,0x1F,0x1F};
const byte C_2[8] PROGMEM = {0x00,0x00,0x00,0x00,0x00,0x1F,0x1F,0x1F};
const byte C_3[8] PROGMEM = {0x00,0x00,0x0E,0x0A,0x0A,0x0E,0x00,0x00};
const byte blockChar[11][2][3] = {
{{ 255, C0, 255}, {255, C2, 255}}, //0
{{ C0, 255, 32}, {C2, 255, C2}}, //1
{{ C0, C0, 255}, {255, C1, C2}}, //2
{{ C1, C1, 255}, {C1, C1, 255}}, //3
{{ 255, C2, 255}, {32, 32, 255}}, //4
{{ 255, C1, C1}, {C2, C2, 255}}, //5
{{ 255, C0, C0}, {255, C1, 255}}, //6
{{ C0, C1, 255}, {32, C0, 255}}, //7
{{ 255, C1, 255}, {255, C1, 255}}, //8
{{ 255, C1, 255}, {C2, C2, 255}}, //9
{{ 32, 32, 32}, {32, 32, 32}}, //Blank
};
//---------------------- Thick Bevel Font ----------------------------
#define LT 0
#define UB 1
#define RT 2
#define LL 3
#define LB 4
#define LR 5
#define UMB 6
#define LMB 7
const byte _LT[8] PROGMEM = { B00111, B01111, B11111, B11111, B11111, B11111, B11111, B11111};
const byte _UB[8] PROGMEM = { B11111, B11111, B11111, B00000, B00000, B00000, B00000, B00000};
const byte _RT[8] PROGMEM = { B11100, B11110, B11111, B11111, B11111, B11111, B11111, B11111};
const byte _LL[8] PROGMEM = { B11111, B11111, B11111, B11111, B11111, B11111, B01111, B00111};
const byte _LB[8] PROGMEM = { B00000, B00000, B00000, B00000, B00000, B11111, B11111, B11111};
const byte _LR[8] PROGMEM = { B11111, B11111, B11111, B11111, B11111, B11111, B11110, B11100};
const byte _UMB[8] PROGMEM = { B11111, B11111, B11111, B00000, B00000, B00000, B11111, B11111};
const byte _LMB[8] PROGMEM = { B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111};
const byte bevelChar[11][2][3] = {
{{LT, UB, RT}, {LL, LB, LR}}, //0
{{UB, RT, 32}, {LB, LMB, LB}}, //1
{{UMB, UMB, RT}, {LL, LB, LB}}, //2
{{UMB, UMB, RT}, {LB, LB, LR}}, //3
{{LL, LB, LMB}, {32, 32, LMB}}, //4
{{LT, UMB, UMB}, {LB, LB, LR}}, //5
{{LT, UMB, UMB}, {LL, LB, LR}}, //6
{{UB, UB, RT}, {32, 32, LT}}, //7
{{LT, UMB, RT}, {LL, LB, LR}}, //8
{{LT, UMB, RT}, {32, 32, LR}}, //9
{{ 32, 32, 32}, {32, 32, 32}} //Blank
};
//---------------------- Trek Font ----------------------------
#define K0 0
#define K1 1
#define K2 2
#define K3 3
#define K4 4
#define K5 5
#define K6 6
#define K7 7
const byte K_0[8] PROGMEM = {0x1F,0x1F,0x00,0x00,0x00,0x00,0x00,0x00};
const byte K_1[8] PROGMEM = {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18};
const byte K_2[8] PROGMEM = {0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x1F};
const byte K_3[8] PROGMEM = {0x1F,0x1F,0x03,0x03,0x03,0x03,0x1F,0x1F};
const byte K_4[8] PROGMEM = {0x1F,0x1F,0x18,0x18,0x18,0x18,0x1F,0x1F};
const byte K_5[8] PROGMEM = {0x1F,0x1F,0x18,0x18,0x18,0x18,0x18,0x18};
const byte K_6[8] PROGMEM = {0x03,0x03,0x03,0x03,0x03,0x03,0x1F,0x1F};
const byte K_7[8] PROGMEM = {0x1F,0x1F,0x03,0x03,0x03,0x03,0x03,0x03};
const byte trekChar[11][2][2] = {
{{ K5, K7}, {255, K6}}, //0
{{ K0, K1}, {K2, 255}}, //1
{{ K0, K3}, {255, K2}}, //2
{{ K0, K3}, {K2, 255}}, //3
{{ K1, 255}, {K0, K1}}, //4
{{ K4, K0}, {K2, 255}}, //5
{{ K5, K0}, {K4, 255}}, //6
{{ K0, 255}, {32, K1}}, //7
{{ 255, K3}, {K4, 255}}, //8
{{ 255, K3}, {K2, K6}}, //9
{{ 32, 32}, {32, 32}}, //Blank
};
//---------------------- Thin Font ----------------------------
#define T0 0
#define T1 1
#define T2 2
#define T3 3
#define T4 4
#define T5 5
#define T6 6
#define T7 7
const byte T_0[8] PROGMEM = {0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02};
const byte T_1[8] PROGMEM = {0x0E,0x02,0x02,0x02,0x02,0x02,0x02,0x0E};
const byte T_2[8] PROGMEM = {0x0E,0x08,0x08,0x08,0x08,0x08,0x08,0x0E};
const byte T_3[8] PROGMEM = {0x0E,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0E};
const byte T_5[8] PROGMEM = {0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0E};
const byte T_4[8] PROGMEM = {0x0E,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A};
const byte T_6[8] PROGMEM = {0x0E,0x02,0x02,0x02,0x02,0x02,0x02,0x02};
const byte T_7[8] PROGMEM = {0x18,0x18,0x18,0x18,0x18,0x1E,0x1F,0x1F};
//lcd draw character functions
const byte thinChar[11][2] = {
{T4, T5}, //0
{T0, T0}, //1
{T1, T2}, //2
{T1, T1}, //3
{T5, T6}, //4
{T2, T1}, //5
{T2, T3}, //6
{T6, T0}, //7
{T3, T3}, //8
{T3, T1}, //9
{32, 32} //blank
};
//--------------------- Message clock ------------------------------------
#define MSG_SPEED 200;
int msgIndex = 0;
String message;
uint32_t msgTimeout;
const char D00[] PROGMEM = "Today is ";
const char D01[] PROGMEM = "Your Brithday";
const char D02[] PROGMEM = "New Years Day";
const char D03[] PROGMEM = "Australia Day";
const char D04[] PROGMEM = "Good Friday";
const char D05[] PROGMEM = "Easter Monday";
const char D06[] PROGMEM = "Anzac Day";
const char D07[] PROGMEM = "Mother's Day";
const char D08[] PROGMEM = "the Queen's Birthday";
const char D09[] PROGMEM = "Father's Day";
const char D10[] PROGMEM = "Labour Day";
const char D11[] PROGMEM = "Xmas Day";
const char D12[] PROGMEM = "Good";
const char D13[] PROGMEM = "morning";
const char D14[] PROGMEM = "afternoon";
const char D15[] PROGMEM = "evening";
const char D16[] PROGMEM = ", it is ";
struct MSG {
uint16_t y;
uint8_t m;
uint8_t d;
char* s;
};
const MSG msgs[] = {
{ 0, 1, 1, D02 },
{ 0, 1, 26, D03 },
{ 2021, 4, 2, D04 },
{ 2021, 4, 5, D05 },
{ 0, 4, 25, D06 },
{ 0, 5, 9, D07 },
{ 2021, 6, 14, D08 },
{ 0, 9, 5, D09 },
{ 2021, 10, 4, D10 },
{ 0, 12, 25, D11 },
{ 0, 0, 0, NULL }
};
const char sun[] PROGMEM = "Sunday";
const char mon[] PROGMEM = "Monday";
const char tue[] PROGMEM = "Tuesday";
const char wed[] PROGMEM = "Wednesday";
const char thr[] PROGMEM = "Thursday";
const char fri[] PROGMEM = "Friday";
const char sat[] PROGMEM = "Saturday";
const char* const daysOfTheWeek[7] PROGMEM = {sun, mon, tue, wed, thr, fri, sat};
const char jan[] PROGMEM = "January";
const char feb[] PROGMEM = "February";
const char mar[] PROGMEM = "March";
const char apr[] PROGMEM = "April";
const char may[] PROGMEM = "May";
const char jun[] PROGMEM = "June";
const char jul[] PROGMEM = "July";
const char aug[] PROGMEM = "August";
const char sep[] PROGMEM = "September";
const char oct[] PROGMEM = "October";
const char nov[] PROGMEM = "November";
const char dec[] PROGMEM = "December";
const char* const months[12] PROGMEM = {jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec};
//--------------------- World clock ------------------------------------
//Define your timezone and daylight saving dates here
//Australia Eastern Time Zone (Sydney, Melbourne)
#define LOCAL_CITY_ABV "SYD" //3 Character city name
#define LOCAL_TIMEZONE(loc) ausET.toUTC(loc) //Function to access Timezone function
TimeChangeRule aEDT = {"AEDT", First, Sun, Oct, 2, 660}; //UTC + 11 hours
TimeChangeRule aEST = {"AEST", First, Sun, Apr, 3, 600}; //UTC + 10 hours
Timezone ausET(aEDT, aEST);
// United Kingdom (London, Belfast)
/*
#define WORLD_CITY_ABV "LND" //3 Character city name
#define WORLD_TIMEZONE(utc) UK.toLocal(utc) //Function to access Timezone function
TimeChangeRule BST = {"BST", Last, Sun, Mar, 1, 60}; // British Summer Time
TimeChangeRule GMT = {"GMT", Last, Sun, Oct, 2, 0}; // Standard Time
Timezone UK(BST, GMT);
*/
//US Eastern Time Zone (New York, Detroit)
#define WORLD_CITY_ABV "NYC" //3 Character city name
#define WORLD_TIMEZONE(utc) usET.toLocal(utc) //Function to access Timezone function
TimeChangeRule usEDT = {"EDT", Second, Sun, Mar, 2, -240}; //Eastern Daylight Time = UTC - 4 hours
TimeChangeRule usEST = {"EST", First, Sun, Nov, 2, -300}; //Eastern Standard Time = UTC - 5 hours
Timezone usET(usEDT, usEST);
//Other time zone examples
//Central European Time (Frankfurt, Paris)
//TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; //Central European Summer Time
//TimeChangeRule CET = {"CET", Last, Sun, Oct, 3, 60}; //Central European Standard Time
//Timezone CE(CEST, CET);
//US Eastern Time Zone (New York, Detroit)
//TimeChangeRule usEDT = {"EDT", Second, Sun, Mar, 2, -240}; //Eastern Daylight Time = UTC - 4 hours
//TimeChangeRule usEST = {"EST", First, Sun, Nov, 2, -300}; //Eastern Standard Time = UTC - 5 hours
//Timezone usET(usEDT, usEST);
//---------------------- General initialisation ----------------------------
void setup()
{
#ifdef DEBUG
Serial.begin(115200);
#endif
//Set outputs/inputs
pinMode(BUTTONS,INPUT);
pinMode(LDR,INPUT);
pinMode(BTN_TILT, INPUT_PULLUP);
pinMode(SPEAKER, OUTPUT);
pinMode(LIGHT, OUTPUT);
//Check if RTC has a valid time/date, if not set it to 00:00:00 01/01/2018.
//This will run only at first time or if the coin battery is low.
//setSyncProvider() causes the Time library to synchronize with the
//external RTC by calling RTC.get() every five minutes by default.
setSyncProvider(rtc.get);
if (timeStatus() != timeSet)
{
#ifdef DEBUG
Serial.println("Setting default time");
#endif
//Set RTC
tmElements_t tm;
tm.Year = CalendarYrToTm(2020);
tm.Month = 06;
tm.Day = 26;
tm.Hour = 7;
tm.Minute = 52;
tm.Second = 0;
time_t t = makeTime(tm);
//use the time_t value to ensure correct weekday is set
if (rtc.set(t) == 0)
{ // Success
setTime(t);
}
else
{
#ifdef DEBUG
Serial.println("RTC set failed!");
#endif
}
}
delay(100);
//Read alarm time from EEPROM memmory
AH = EEPROM.read(EEPROM_AH);
AM = EEPROM.read(EEPROM_AM);
byte ao = EEPROM.read(EEPROM_AO);
alarmON = (ao != 0);
byte cs = EEPROM.read(EEPROM_CS);
//Check if the numbers that you read are valid. (Hours:0-23 and Minutes: 0-59)
if (AH > 23)
{
AH = 0;
}
if (AM > 59){
AM = 0;
}
//Read Birth date from EEPROM
BY = (EEPROM.read(EEPROM_BY + 0) << 8) | EEPROM.read(EEPROM_BY + 1);
if (BY < 1900 || BY > 2099)
{
BY = 2000;
}
BM = EEPROM.read(EEPROM_BM);
if (BM < 0 || BM > 12)
{
BM = 1;
}
BD = EEPROM.read(EEPROM_BD);
if (BD < 0 || BD > 31)
{
BD = 1;
}
//Strings allocations
message.reserve(100);
//Setup current style
lcd.begin(16,2);
currentStyle = (cs > (uint8_t)WORLD) ? STANDARD : (STYLE)cs;
switch (currentStyle)
{
case STANDARD: lcdStandardSetup(); break;
case DUAL_THICK: lcdDualThickSetup(); break;
case DUAL_BEVEL: lcdDualBevelSetup(); break;
case DUAL_TREK: lcdDualTrekSetup(); break;
case DUAL_THIN: lcdDualThinSetup(); break;
case WORD: lcdWordSetup(); break;
case BIO: lcdBioRhythmSetup(); break;
case THERMO: lcdThermometerSetup(); break;
case MESSAGE: lcdMessageSetup(); break;
case WORLD: lcdWorldSetup(); break;
}
#ifdef BACKLIGHT_ALWAYS_ON
switchBacklight(true);
#endif
}
//---------------------- Main program loop ----------------------------
void loop()
{
readBtns(); //Read buttons
getTimeDate(); //Read time and date from RTC
getTempHum(); //Read temperature and humidity
if (!setupScreen)
{
lcdPrint(); //Normanlly print the current time/date/alarm to the LCD
if (alarmON)
{
callAlarm(); // and check the alarm if set on
if (turnItOn)
{
switchBacklight(true);
}
}
#ifdef BACKLIGHT_TIMEOUT
if (backlightOn && (millis() > backlightTimeout))
{
switchBacklight(false);
}
#endif
}
else
{
timeSetup(); //If button set is pressed then call the time setup function
switchBacklight(true);
}
}
//--------------------------------------------------
//Read buttons state
void readBtns()
{
set_state = analogReadButtonState(BTN_SET);
adjust_state = analogReadButtonState(BTN_ADJUST);
alarm_state = analogReadButtonState(BTN_ALARM);
#ifdef BACKLIGHT_LDR
//Test LDR and switch on backlight if Light on LDR
if (!backlightOn && (digitalRead(LDR) == HIGH))
{
switchBacklight(true);
}
#endif
if (!backlightOn && !setupScreen)
{
if (set_state == LOW || adjust_state == LOW || alarm_state == LOW)
{
//Turn on backlight
switchBacklight(true);
//need to hold down button for at least 1/2 a second
delay(500);
}
}
else
{
if(!setupScreen)
{
if (alarm_state == LOW)
{
alarmON = !alarmON;
EEPROM.write(EEPROM_AO, (alarmON) ? 1 : 0);
delay(500);
switchBacklight(true);
}
else if (adjust_state == LOW)
{
currentStyle = (currentStyle == WORLD) ? STANDARD : (STYLE)((int)currentStyle + 1);
EEPROM.write(EEPROM_CS, (byte)currentStyle);
switch (currentStyle)
{
case STANDARD: lcdStandardSetup(); break;
case DUAL_THICK: lcdDualThickSetup(); break;
case DUAL_BEVEL: lcdDualBevelSetup(); break;
case DUAL_TREK: lcdDualTrekSetup(); break;
case DUAL_THIN: lcdDualThinSetup(); break;
case WORD: lcdWordSetup(); break;
case BIO: lcdBioRhythmSetup(); break;
case THERMO: lcdThermometerSetup(); break;
case MESSAGE: lcdMessageSetup(); break;
case WORLD: lcdWorldSetup(); break;
}
lcd.clear();
lcdPrint();
delay(500);
switchBacklight(true);
}
}
if (set_state == LOW)
{
setupMode = (setupMode == ALARM_MIN) ? CLOCK : (SETUP)((int)setupMode + 1);
if( setupMode != CLOCK )
{
setupScreen = true;
if (setupMode == TIME_HOUR)
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("------SET------");
lcd.setCursor(0,1);
lcd.print("-TIME and DATE-");
delay(2000);
lcd.clear();
}
}
else
{
lcd.clear();
//Set RTC
tmElements_t tm;
tm.Year = CalendarYrToTm(YY);
tm.Month = MM;
tm.Day = DD;
tm.Hour = H;
tm.Minute = M;
tm.Second = 0;
time_t t = makeTime(tm);
//use the time_t value to ensure correct weekday is set
if (rtc.set(t) == 0)
{ // Success
setTime(t);
}
else
{
#ifdef DEBUG
Serial.println("RTC set failed!");
#endif
}
//rtc.adjust(DateTime(YY, MM, DD, H, M, 0)); //Save time and date to RTC IC
EEPROM.write(EEPROM_AH, AH); //Save the alarm hours to EEPROM
EEPROM.write(EEPROM_AM, AM); //Save the alarm minuted to EEPROM
EEPROM.write(EEPROM_BY + 0, BY >> 8); //Save the birth year to EEPROM
EEPROM.write(EEPROM_BY + 1, BY & 0xFF); //Save the birth year to EEPROM
EEPROM.write(EEPROM_BM, BM); //Save the birth month to EEPROM
EEPROM.write(EEPROM_BD, BD); //Save the birth day to EEPROM
lcd.print("Saving....");
delay(2000);
lcd.clear();
setupScreen = false;
setupMode = CLOCK;
switchBacklight(true);
}
delay(500);
}
}
}
//--------------------------------------------------
//Read time and date from rtc ic
void getTimeDate()
{
if (!setupScreen)
{
//DateTime now = rtc.now();
time_t t = now();
DD = day(t);
MM = month(t);
YY = year(t);
H = hour(t);
M = minute(t);
S = second(t);
DOW = weekday();
}
//Make some fixes...
sDD = ((DD < 10) ? "0" : "") + String(DD);
sMM = ((MM < 10) ? "0" : "") + String(MM);
sYY = String(YY-2000);
sH = ((H < 10) ? "0" : "") + String(H);
sM = ((M < 10) ? "0" : "") + String(M);
sS = ((S < 10) ? "0" : "") + String(S);
sBD = ((BD < 10) ? "0" : "") + String(BD);
sBM = ((BM < 10) ? "0" : "") + String(BM);
sBY = String(BY);
aH = ((AH < 10) ? "0" : "") + String(AH);
aM = ((AM < 10) ? "0" : "") + String(AM);
}
//--------------------------------------------------
//Read temperature and humidity every 6 seconds from DHT sensor
void getTempHum()
{
unsigned long currentMillis = millis();
if (currentMillis - prevDhtMillis >= DHT_UPDATE_INTERVAL)
{
int chk = DHT.read21(DHT21);
prevDhtMillis = currentMillis;
hum = min(round(DHT.humidity),99);
temp = min(round(DHT.temperature),99);
sTMP = ((temp > 9) ? "" : " ") + String(temp);
sHUM = ((hum > 9) ? "" : " ") + String(hum);
}
}
//--------------------------------------------------
//Read the analog port and test if specified button is pressed
// btn - Button constant to test
// Returns LOW if switch is pressed otherwise HIGH
// set - 767
// adjust - 510
// alarm - 0
int analogReadButtonState(ANALOG_BUTTONS btn)
{
int v = analogRead(BUTTONS);
int r = HIGH;
if (v < 1000)
{
switch (btn)
{
case BTN_SET: r = (v > 600) ? LOW : HIGH; break;
case BTN_ADJUST: r = (v < 600 && v > 400) ? LOW : HIGH; break;
case BTN_ALARM: r = (v < 100) ? LOW : HIGH; break;
break;
}
}
return r;
}
//--------------------------------------------------
//Switch on or off backlight
void switchBacklight(bool on)
{
#ifdef NO_BACKLIGHT
digitalWrite(LIGHT, LOW);
backlightOn = true; //Fool software into thinking its on even though it isn't
#else
#ifdef BACKLIGHT_ALWAYS_ON
digitalWrite(LIGHT, HIGH);
backlightOn = true;
#else
digitalWrite(LIGHT, (on) ? HIGH : LOW);
backlightOn = on;
backlightTimeout = millis() + BACKLIGHT_TIMEOUT;
#endif
#endif
}
//--------------------------------------------------
//Print values to the display
void lcdPrint()
{
switch (currentStyle)
{
case STANDARD: lcdStandardLayout(); break;
case DUAL_THICK: lcdDualThickLayout(); break;
case DUAL_BEVEL: lcdDualBevelLayout(); break;
case DUAL_TREK: lcdDualTrekLayout(); break;
case DUAL_THIN: lcdDualThinLayout(); break;
case WORD: lcdWordLayout(); break;
case BIO: lcdBioRhythmLayout(); break;
case THERMO: lcdThermometerLayout(); break;
case MESSAGE: lcdMessageLayout(); break;
case WORLD: lcdWorldLayout(); break;
}
}
//------------------------------------------------ Standard layout ---------------------------------------------------------------------
void lcdStandardSetup()
{
}
void lcdStandardLayout()
{
String line1 = sH+":"+sM+":"+sS+" | "+aH+":"+aM;
String line2 = sDD+"/"+sMM+"/"+sYY +" | " + ((alarmON && (S & 0x01)) ? "ALARM" : " ");
lcd.setCursor(0,0); //First row
lcd.print(line1);
lcd.setCursor(0,1); //Second row
lcd.print(line2);
}
//Create a custom character from program memory
void createCharP(byte slot, byte* p)
{
for (int i = 0; i < 8; i++)
{
customChar[i] = pgm_read_byte(p++);
}
lcd.createChar(slot, customChar);
}
//------------------------------------------------ Dual Thick layout ---------------------------------------------------------------------
void lcdDualThickSetup()
{
createCharP(C0, C_0);
createCharP(C1, C_1);
createCharP(C2, C_2);
createCharP(C3, C_3);
createCharP(BELL_CHAR, bell);
}
void lcdDualThickLayout()
{
#ifdef DUAL_THICK_12HR
int h = (H >= 12) ? H - 12 : H;
if (h == 0)
{
h = 12;
}
lcdDualThickPrintNumber(8, M, true);
lcdDualThickPrintNumber(0, h, false);
lcd.setCursor(15,0);
lcd.print((H >= 12) ? "p" : "a");
lcd.setCursor(15,1);
lcd.print("m");
#else
lcdDualThickPrintNumber(8, M, true);
lcdDualThickPrintNumber(0, H, true);
bool alarm = (S & 0x01);
lcdWordShowBell(15, 0, alarm, BELL_CHAR); //bottonm right corner
lcdWordShowBell(15, 1, !alarm, BELL_CHAR); //bottonm right corner
#endif
byte c = (S & 1) ? C3 : 32;
lcd.setCursor(7,0);
lcd.write(c);
lcd.setCursor(7,1);
lcd.write(c);
}
//Draw a 2 line number
// pos - x position to draw number
// number - value to draw
// leadingZero - whether leading zeros should be displayed
void lcdDualThickPrintNumber(int pos, int number, int leadingZero)
{
int t = number / 10;
int u = number % 10;
if (t == 0 && !leadingZero)
{
t = 11;
}
lcdDualThickPrintDigit(pos, t);
lcdDualThickPrintDigit(pos + 4, u);
}
//Draw a 2 line digit
// pos - x position to draw number
// number - value to draw
void lcdDualThickPrintDigit(int pos, int number)
{
for (int y = 0; y < 2; y++)
{
lcd.setCursor(pos, y);
for (int x = 0; x < 3; x++)
{
lcd.write(blockChar[number][y][x]);
}
}
}
//------------------------------------------------ Dual Bevel layout ---------------------------------------------------------------------
void lcdDualBevelSetup()
{
createCharP(LT, _LT);
createCharP(UB, _UB);
createCharP(RT, _RT);
createCharP(LL, _LL);
createCharP(LB, _LB);
createCharP(LR, _LR);
createCharP(UMB, _UMB);
createCharP(LMB, _LMB);
}
void lcdDualBevelLayout()
{
#ifdef DUAL_THICK_12HR
int h = (H >= 12) ? H - 12 : H;
if (h == 0)
{
h = 12;
}
lcdDualBevelPrintNumber(8, M, true);
lcdDualBevelPrintNumber(0, h, false);
lcd.setCursor(15,0);
lcd.print((H >= 12) ? "p" : "a");
lcd.setCursor(15,1);
lcd.print("m");
#else
lcdDualBevelPrintNumber(8, M, true);
lcdDualBevelPrintNumber(0, H, true);
bool alarm = (S & 0x01);
lcdWordShowBell(15, 0, alarm, 65); //bottonm right corner
lcdWordShowBell(15, 1, !alarm, 65); //bottonm right corner
#endif
byte c = (S & 1) ? 58 : 32;
lcd.setCursor(7,0);
lcd.write(c);
lcd.setCursor(7,1);
lcd.write(c);
}
//Draw a 2 line number
// pos - x position to draw number
// number - value to draw
// leadingZero - whether leading zeros should be displayed
void lcdDualBevelPrintNumber(int pos, int number, int leadingZero)
{
int t = number / 10;
int u = number % 10;
if (t == 0 && !leadingZero)
{
t = 11;
}
lcdDualBevelPrintDigit(pos, t);
lcdDualBevelPrintDigit(pos + 4, u);
}
//Draw a 2 line digit
// pos - x position to draw number
// number - value to draw
void lcdDualBevelPrintDigit(int pos, int number)
{
for (int y = 0; y < 2; y++)
{
lcd.setCursor(pos, y);
for (int x = 0; x < 3; x++)
...
This file has been truncated, please download it to see its full contents.
Comments
Please log in or sign up to comment.