John Bradnam
Published © GPL3+

LCD Alarm Clock with many faces

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

IntermediateFull instructions provided8 hours2,743
LCD Alarm Clock with many faces

Things used in this project

Hardware components

Arduino UNO & Genuino UNO
Arduino UNO & Genuino UNO
If using the included PCB, use a ATmega328 DIL microprocessor, 16MHz crystal, 2x22pf capacitors, 7805 regulator, 100uF 16V capacitor, 3 x 0.1uF capaitors and 1uF capacitor.
×1
Resistor 10k ohm
Resistor 10k ohm
×4
Through Hole Resistor, 470 ohm
Through Hole Resistor, 470 ohm
×1
Standard LCD - 16x2 White on Blue
Adafruit Standard LCD - 16x2 White on Blue
×1
Buzzer
Buzzer
×1
Capacitor 10 µF
Capacitor 10 µF
×1
Real Time Clock (RTC)
Real Time Clock (RTC)
DS1302 RTC
×1
32.768 kHz Crystal
32.768 kHz Crystal
×1
Coin Cell Battery CR2032
Coin Cell Battery CR2032
+ holder. If using a PCB, use CR1220 battery and holder
×1
Tilt Switch, Encapsulated
Tilt Switch, Encapsulated
Mercury switch
×1
Trimmer Potentiometer, 10 kohm
Trimmer Potentiometer, 10 kohm
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

STL Files

Schematics

Fritzing drawing

Schematic

PCB

Eagle Files

Schematic and PCB in Eagle format

Code

DigitalClockAlarmV5.ino

C/C++
/*  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 
 */

//Libraries
#include <Wire.h>
#include <TimeLib.h>
#include <DS1302RTC.h>
#include <LiquidCrystal.h>
#include <EEPROM.h>

//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_TIMEOUT 10000

#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 BTN_SET         A0 //PC0
#define BTN_ADJUST      A1 //PC1
#define BTN_ALARM       A2 //PC2
#define BTN_TILT        A3 //PC3
#define SPEAKER         11 //PB3
#define RTC_CE          10 //PB2
#define RTC_IO          12 //PB4
#define RTC_SCLK        13 //PB5


//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);

//RTC_DS1307 rtc; //DS1307 i2c
char daysOfTheWeek[7][12] = {"Sunday","Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
long interval = 300;  
int melody[] = { 600, 800, 1000,1200 };

//Variables
int DD,MM,YY,H,M,S,set_state, adjust_state, alarm_state,AH,AM, shake_state;
int shakeTimes=0;
int i =0;
int btnCount = 0;
String sDD;
String sMM;
String sYY;
String sH;
String sM;
String sS;
String aH="12";
String aM="00";
String alarm = "     ";
long previousMillis = 0;    

//Boolean flags
boolean setupScreen = false;
boolean alarmON=false;
boolean turnItOn = false;

enum STYLE { STANDARD, DUAL_THICK, DUAL_BEVEL, DUAL_TREK, DUAL_THIN, WORD };
STYLE currentStyle = STANDARD;

bool backlightOn = false;
long backlightTimeout = 0;

byte customChar[8];

//--------------------- Word clock --------------------------------------
String units[] = {"HUNDRED", "ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE"};
String teens[] = {"TEN", "ELEVEN", "TWELVE", "THIRTEEN", "FOURTEEN", "FIFTEEN", "SIXTEEN", "SEVENTEEN", "EIGHTEEN", "NINETEEN"};
String tens[] = {"", "", "TWENTY", "THIRTY", "FORTY", "FIFTY"};

//---------------------- 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 and clock custom characters ----------------------------
#define BELL_CHAR 1
#define CLOCK_CHAR 2
const byte bell[8] PROGMEM  = {0x4, 0xe, 0xe, 0xe, 0x1f, 0x0, 0x4};
const byte clock[8] PROGMEM = {0x0,0xe,0x15,0x17,0x11,0xe,0x0};

//---------------------- 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
};
 
//---------------------- General initialisation ----------------------------
void setup() 
{
  Serial.begin(115200);

  //Set outputs/inputs
  pinMode(BTN_SET,INPUT);
  pinMode(BTN_ADJUST,INPUT);
  pinMode(BTN_ALARM, INPUT);
  pinMode(BTN_TILT, INPUT);
  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)
  {
    Serial.println("Setting default time");
    //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
    {
      Serial.println("RTC set failed!");
    }
  }

  delay(100);
  //Read alarm time from EEPROM memmory
  AH = EEPROM.read(0);
  AM = EEPROM.read(1);
  byte ao = EEPROM.read(2);
  alarmON = (ao != 0);
  byte cs = EEPROM.read(3);
  //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;
  }
  //Setup current style
  lcd.begin(16,2);
  currentStyle = (cs > (uint8_t)WORD) ? 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;
  }
}

//---------------------- Main program loop ----------------------------
void loop() 
{
  readBtns();       //Read buttons 
  getTimeDate();    //Read time and date from RTC
  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);
      }
    }
    //Serial.println("backlightTimeout=" + String(backlightTimeout) + ", millis()=" + String(millis()) + ", backlightOn=" + String(backlightOn));
    if (backlightOn && (millis() > backlightTimeout))
    {
      switchBacklight(false);
    }
  }
  else
  {
    timeSetup();    //If button set is pressed then call the time setup function
    switchBacklight(true);
  }
}

//--------------------------------------------------
//Read buttons state
void readBtns()
{
  set_state = digitalRead(BTN_SET);
  adjust_state = digitalRead(BTN_ADJUST);
  alarm_state = digitalRead(BTN_ALARM);
  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(2, (alarmON) ? 1 : 0);
        delay(500);
        switchBacklight(true);
      }
      else if (adjust_state == LOW)
      {
        currentStyle = (currentStyle == WORD) ? STANDARD : (STYLE)((int)currentStyle + 1);
        EEPROM.write(3, (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;
        }
        lcd.clear();
        lcdPrint();
        delay(500);
        switchBacklight(true);
      }
    }
    
    if (set_state == LOW)
    {
      if( btnCount < 7 )
      {
        btnCount++;
        setupScreen = true;
        if (btnCount == 1)
        {
          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
        {
          Serial.println("RTC set failed!");
        }
        //rtc.adjust(DateTime(YY, MM, DD, H, M, 0)); //Save time and date to RTC IC
        
        EEPROM.write(0, AH);  //Save the alarm hours to EEPROM 0
        EEPROM.write(1, AM);  //Save the alarm minuted to EEPROM 1
        
        lcd.print("Saving....");
        delay(2000);
        lcd.clear();
        setupScreen = false;
        btnCount=0;
        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);
  }
  //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);
  aH = ((AH < 10) ? "0" : "") + String(AH);
  aM = ((AM < 10) ? "0" : "") + String(AM);
}

//--------------------------------------------------
//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;
    #endif
  #endif
  backlightTimeout = millis() + BACKLIGHT_TIMEOUT;
}

//--------------------------------------------------
//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;
  }
}

//------------------------------------------------ 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++)
    {
      lcd.write(bevelChar[number][y][x]);      
    }
  }
}

//------------------------------------------------ Dual Trek layout ---------------------------------------------------------------------
void lcdDualTrekSetup()
{
  createCharP(K0, K_0);
  createCharP(K1, K_1);
  createCharP(K2, K_2);
  createCharP(K3, K_3);
  createCharP(K4, K_4);
  createCharP(K5, K_5);
  createCharP(K6, K_6);
  createCharP(K7, K_7);
}

void lcdDualTrekLayout()
{
  lcdDualTrekPrintNumber(10, S, true);
  lcdDualTrekPrintNumber(5, M, true);  
  lcdDualTrekPrintNumber(0, H, true);

  byte c = (S & 1) ? 165 : 32;
  lcd.setCursor(4,0);
  lcd.write(c);
  lcd.setCursor(4,1);
  lcd.write(c);
  lcd.setCursor(9,0);
  lcd.write(c);
  lcd.setCursor(9,1);
  lcd.write(c);

  bool alarm = (S & 0x01);
  lcdWordShowBell(15, 0, alarm, 65); //bottonm right corner
  lcdWordShowBell(15, 1, !alarm, 65); //bottonm right corner
}

//Draw a 2 line number
// pos - x position to draw number
// number - value to draw
// leadingZero - whether leading zeros should be displayed
void lcdDualTrekPrintNumber(int pos, int number, int leadingZero)
{
  int t = number / 10;
  int u = number % 10;
  if (t == 0 && !leadingZero)
  {
    t = 11;
  }
  lcdDualTrekPrintDigit(pos, t);
  lcdDualTrekPrintDigit(pos + 2, u);
}

//Draw a 2 line digit
// pos - x position to draw number
// number - value to draw
void lcdDualTrekPrintDigit(int pos, int number)
{  
  for (int y = 0; y < 2; y++)
  {
    lcd.setCursor(pos, y);
    for (int x = 0; x < 2; x++)
    {
      lcd.write(trekChar[number][y][x]);      
    }
  }
}

//------------------------------------------------ Dual Thin layout ---------------------------------------------------------------------
void lcdDualThinSetup()
{
  createCharP(T0, T_0);
  createCharP(T1, T_1);
  createCharP(T2, T_2);
  createCharP(T3, T_3);
  createCharP(T4, T_4);
  createCharP(T5, T_5);
  createCharP(T6, T_6);
  createCharP(T7, T_7);
}

void lcdDualThinLayout()
{
  
#ifdef DUAL_THIN_12HR

  int h = (H >= 12) ? H - 12 : H;
  if (h == 0)
  {
    h = 12;
  }
  lcdDualThinPrintNumber(6, S, true);
  lcdDualThinPrintNumber(3, M, true);  
  lcdDualThinPrintNumber(0, h, false);
  
  lcd.setCursor(9,0);
  lcd.print((H >= 12) ? "p" : "a");
  lcd.setCursor(9,1);
  lcd.print("m");
  
#else
  
  lcdDualThinPrintNumber(6, S, true);
  lcdDualThinPrintNumber(3, M, true);  
  lcdDualThinPrintNumber(0, H, true);

#endif

  byte c = (S & 1) ? 165 : 32;
  lcd.setCursor(2,0);
  lcd.write(c);
  lcd.setCursor(2,1);
  lcd.write(c);
  lcd.setCursor(5,0);
  lcd.write(c);
  lcd.setCursor(5,1);
  lcd.write(c);

  String line1 = aH+":"+aM;
  String line2 = (alarmON && (S & 0x01)) ? "ALARM" : "     ";
  lcd.setCursor(11,0); //First row
  lcd.print(line1);
  lcd.setCursor(11,1); //Second row
  lcd.print(line2);  
  
}

//Draw a 2 line number
// pos - x position to draw number
// number - value to draw
// leadingZero - whether leading zeros should be displayed
void lcdDualThinPrintNumber(int pos, int number, int leadingZero)
{
  int t = number / 10;
  int u = number % 10;
  if (t == 0 && !leadingZero)
  {
    t = 11;
  }
  lcdDualThinPrintDigit(pos, t);
  lcdDualThinPrintDigit(pos + 1, u);
}

//Draw a 2 line digit
// pos - x position to draw number
// number - value to draw
void lcdDualThinPrintDigit(int pos, int number)
{  
  for (int y = 0; y < 2; y++)
  {
    lcd.setCursor(pos, y);
    lcd.write(thinChar[number][y]);      
  }
}

//------------------------------------------------ Word layout ---------------------------------------------------------------------
void lcdWordSetup()
{
  createCharP(BELL_CHAR, &bell[0]);
}

void lcdWordLayout()
{
  String line1 = numberToWord(H, false);
  String line2 = numberToWord(M, true);
  lcd.setCursor(0,0); //First row
  printClear(line1, 13);
  lcd.setCursor(0,1); //Second row
  printClear(line2, 14);

  if (millis() > frameTimeout)
  {
    frameTimeout = millis() + FRAME_TIMEOUT;
    //lcd.createChar(HOURGLASS_CHAR, &hourglass[nextFrame][0]);
    createCharP(HOURGLASS_CHAR, &hourglass[nextFrame][0]);
    nextFrame = (nextFrame + 1) % HOURGLASS_FRAMES;
    lcd.setCursor(13,0); //First row
    lcd.write((int)HOURGLASS_CHAR);
    lcd.print(sS);
  }

  bool alarm = (S & 0x01);
  lcdWordShowBell(14, 1, alarm, BELL_CHAR); //Second row
  lcdWordShowBell(15, 1, !alarm, BELL_CHAR); //Second row
}

//Display the bell symbol if alarm is on
// x - x position (0..15)
// y - y position (0..1)
// show - true to show
void lcdWordShowBell(int x, int y, bool show, byte chr)  
{
  lcd.setCursor(x,y);
  lcd.print(" ");
  if (alarmON && show)
  {
    lcd.setCursor(x,y);
    lcd.write(chr);
  }
}

//Print character string and clear to right
// s - String to print
// len - length of area to clear including string length
void printClear(String s, int len)
{
  lcd.print(s);
  len = len - s.length();
  while (len > 0)
  {
    lcd.print(" ");
    len--;
  }
}

//Convert a number to a word
// number - value to convert
// minutes - true if this is a minute value
// returns text string
String numberToWord(int number, bool minutes)
{
  int t = number / 10;
  int u = number % 10;
  if (t == 0 && u == 0 && !minutes)
  {
    t = 2; u = 4;
  }
  
  if (t == 0)
  {
    return String((minutes && u != 0) ? "ZERO " : "") + String(units[u]);
  }
  else if (t == 1)
  {
    return String(teens[u]);
  }
  else if (u == 0)
  {
    return String(tens[t]);
  }
  else
  {
    return String(tens[t]) + " " + String(units[u]);
  }
}

//------------------------------------------------ Setup Screen ---------------------------------------------------------------------

void timeSetup()
{
  int up_state = adjust_state;
  int down_state = alarm_state;
  if(btnCount<=5){
    if (btnCount==1){         //Set Hour
      lcd.setCursor(4,0);
      lcd.print(">"); 
      if (up_state == LOW){   //Up button +
        if (H<23){
          H++;
        }
        else {
          H=0;
        }
        delay(350);
      }
      if (down_state == LOW){ //Down button -
        if (H>0){
          H--;
        }
        else {
          H=23;
        }
        delay(350);
      }
    }
    else if (btnCount==2){      //Set  Minutes
      lcd.setCursor(4,0);
      lcd.print(" ");
      lcd.setCursor(9,0);
      lcd.print(">");
      if (up_state == LOW){
        if (M<59){
          M++;
        }
        else {
          M=0;
        }
        delay(350);
      }
      if (down_state == LOW){
        if (M>0){
          M--;
        }
        else {
          M=59;
        }
        delay(350);
      }
    }
    else if (btnCount==3){      //Set Day
      lcd.setCursor(9,0);
      lcd.print(" ");
      lcd.setCursor(0,1);
      lcd.print(">");
      if (up_state == LOW){
        if (DD<31){
...

This file has been truncated, please download it to see its full contents.

Credits

John Bradnam

John Bradnam

31 projects • 23 followers

Comments