John Bradnam
Published © GPL3+

LCD Alarm Clock with many faces (NEW VERSION)

An LCD1602 alarm clock that includes many of the other LCD1602 clocks found on maker sites.

IntermediateFull instructions provided8 hours2,149
LCD Alarm Clock with many faces (NEW VERSION)

Things used in this project

Hardware components

ATmega328
Microchip ATmega328
28 PIN DIL Variant. See schematic for other components
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

STL Files

Files for Slicing software - Use a 0.2mm layer height with no supports

Schematics

Schematic

PCB

Eagle Files

Schematic and PCB in Eagle format

Code

DigitalClockAlarmV8.ino

Arduino
/*  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.

Time-master.zip

C/C++
This is the Time library that compiles without errors
No preview (download only).

Credits

John Bradnam

John Bradnam

141 projects • 167 followers

Comments