GLCD Digital clock

A custom made gift clock with temprature reading and graphics , day / night graphics and indication for birthday and other special days

This alarm clock was given as a gift to my girlfriend on her birthday some years ago. I used simple balsa wood sheet for the case that was layin around , glued and screwed together , and 4 buttons for all the controls.

I tried to make it as simple as i could since she isn`t tech shavy but in the same time i wanted it to have as many features as possible.

The user can adjust the basics , time , date and alarm clock , and in the code i`ve embeded some special alarms-indicators (only visual stuff not sound) specialy for her birthday , for christmass and new years eve etc.

 

 

Also since the RTC i used had a thermometer (DS3231) i`ve adjusted the graphics according to the temprature.For example below a certain temprature it shows a snowman and in the summer when its blistering hot here in athens , it shows some palm trees (like from a desert oasis).

Also the day and night cicle changes some graphic , it shows a sun during day and a moon during night..Of course in the winter the days are shorter so it begins kinda earlier with the moon.. eh ok.

The 128×64 glcd Display is based on  ST7920  so i am using the U8glib for st7920 that can be found on this github page https://github.com/olikraus/u8glib
Also the arduino controls the

backlight through a bc337 NPN transistor connected to D10. The backlight lights up when we press any button and turns off when a few seconds of inactivity passes. When the alarm goes of the backlight flashes on and off

So we have the arduino nano , the lcd the rtc , four tactile buttons , a buzzer for the alarm , a transistor for the backlight and some resistors

 

The parts needed are

1x arduino (nano , uno , one with atmega328)
1x 128×64 ST7920 GLCD
1x DS3231 Real time clock
1x piezoelectric speaker
1xBC337 NPN transisor or whatever npn you have with similar specs
4x button switches
4x 10k resistors
1×100Ω resistor
1×220Ω resistor
1x1k resitor
1x10k potensiometer (for lcd contrast)

and here is the program

#include <DS3231.h>
#include <U8glib.h>

U8GLIB_ST7920_128X64 u8g(13, 11, 12, U8G_PIN_NONE); 
DS3231  rtc(SDA, SCL);

const byte button_set = 2;
const byte button_up = 3;
const byte button_down = 4;
const byte button_next = 5;
const byte backlight_pin = 10;
const byte speaker_pin = 9;

byte button_set_state = 0;
byte button_up_state = 0;
byte button_down_state = 0;
byte button_next_state = 0;

byte screenMode = 0;
byte valset = 0;//epilogeas parametrou otan ry8mizoume kati
byte backlight_state = 0;

//ksypnitiri
byte alarm_Hour=12;
byte alarm_Min=12;
byte alarm_Ring=0;
byte alarm_Active=0;

//epoxh mera nyxta
byte summer = 0;
byte day = 0;

Time  t;//global time
unsigned long previousMillis = 0;

//sprites
const uint8_t bell_inactive[] U8G_PROGMEM = {0b00100,0b01110,0b01010,0b01010,0b01010,0b10001,0b11111,0b00100};//energo kampanaki
const uint8_t bell_active[] U8G_PROGMEM = {0b00100,0b01110,0b01110,0b01110,0b01110,0b11111,0b11111,0b00100};//anenergo kampanaki

const uint8_t moon[] U8G_PROGMEM =  { 
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x3, 0x80, 
0x0, 0x0, 0x7, 0x80, 0x0, 0x0, 0x7, 0x80, 0x0, 0x14, 0xf, 0x80, 0x0, 0x1c, 0x1f, 
0x80, 0x0, 0x18, 0x1f, 0x0, 0x0, 0x10, 0x3f, 0x0, 0x98, 0x0, 0x3f, 0x0, 0x78, 0x0, 
0x3f, 0x80, 0xfe, 0x0, 0x7f, 0x80, 0xb8, 0x0, 0x7f, 0x80, 0x18, 0x0, 0x7f, 0x80, 0x8, 
0x0, 0x7f, 0x80, 0x0, 0x0, 0x7f, 0xc0, 0x0, 0x0, 0x7f, 0xc0, 0x0, 0x0, 0x3f, 0xe0, 
0x0, 0x0, 0x3f, 0xf0, 0x0, 0x0, 0x3f, 0xf0, 0x0, 0x0, 0x1f, 0xfc, 0x0, 0x10, 0x1f, 
0xfe, 0x0, 0x19, 0xf, 0xff, 0x80, 0x1e, 0x7, 0xff, 0xf8, 0x1e, 0x3, 0xff, 0xf0, 0x7e, 
0x0, 0xff, 0x80, 0x1f, 0x0, 0x0, 0x0, 0x19, 0x80, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 
0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
 };

const uint8_t palm[] U8G_PROGMEM =  { 
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe3, 0x0, 
0x0, 0x0, 0x73, 0x0, 0x0, 0x1f, 0xff, 0xfc, 0x0, 0x0, 0x7f, 0xc0, 0x0, 0x0, 0x3f, 
0xc0, 0x0, 0x1, 0xff, 0xe0, 0x0, 0x3, 0x3f, 0x30, 0x0, 0x4, 0x25, 0x88, 0x0, 0x8, 
0x24, 0xb0, 0x0, 0x0, 0x25, 0x97, 0x0, 0x0, 0x7, 0xff, 0xf8, 0x0, 0x4, 0x7e, 0x0, 
0x0, 0x6, 0xff, 0x80, 0x0, 0x7, 0x3e, 0xc0, 0x0, 0x6, 0x2a, 0x20, 0x0, 0x2, 0x4a, 
0x10, 0x0, 0x3, 0x8, 0x0, 0x0, 0x3, 0x18, 0x0, 0x0, 0x1, 0x10, 0x0, 0x0, 0x1, 
0x90, 0x0, 0x0, 0x1, 0x90, 0x0, 0x0, 0x1, 0xb0, 0x0, 0x0, 0x0, 0xa0, 0x0, 0x0, 
0x0, 0xe0, 0x0, 0x0, 0x0, 0xe0, 0x0, 0x0, 0x0, 0xe0, 0x0, 0x0, 0x1, 0xe0, 0x0, 
0x0, 0x2, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 
 };

 const uint8_t palm2[] U8G_PROGMEM =  { 
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x1, 0xc, 0x0, 0x0, 0x51, 0x38, 
0x0, 0x1, 0xfd, 0x70, 0x0, 0x3, 0xff, 0xef, 0xe0, 0x4, 0x1f, 0xff, 0xf0, 0x0, 0xf, 
0xfe, 0x0, 0x0, 0x3f, 0xf8, 0x0, 0x0, 0x7f, 0xfe, 0x0, 0x1, 0x87, 0xff, 0x0, 0x0, 
0xb, 0xef, 0x80, 0x0, 0x1b, 0xe3, 0x80, 0x0, 0x33, 0x70, 0xc0, 0x0, 0x31, 0x70, 0x40, 
0x0, 0x23, 0x30, 0x0, 0x0, 0x63, 0x30, 0x0, 0x0, 0x43, 0x10, 0x0, 0x0, 0x43, 0x10, 
0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0x3, 
0x80, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 
0x3, 0xc0, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0x3, 0x80, 0x0, 
0x0, 0x3, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0,
 };
 
const uint8_t sun[] U8G_PROGMEM =  { 
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1c, 0x7, 
0x0, 0x0, 0x1f, 0xdf, 0x0, 0x1, 0xf8, 0x72, 0x0, 0x1, 0x9c, 0x72, 0x0, 0x1, 0x8c, 
0x62, 0x0, 0x0, 0x84, 0x47, 0x18, 0x3, 0xcf, 0xf5, 0xf8, 0x1f, 0xdf, 0xf8, 0x18, 0x18, 
0x3f, 0xfc, 0x30, 0x3c, 0x7f, 0xfe, 0x60, 0x1f, 0xff, 0xfe, 0xfc, 0x1c, 0x7f, 0xfe, 0x6e, 
0x30, 0x7f, 0xff, 0x1c, 0x60, 0x7f, 0xfe, 0x10, 0x3f, 0xff, 0xff, 0xf8, 0xf, 0x7f, 0xfe, 
0x64, 0x18, 0x3f, 0xfe, 0xc, 0x10, 0x3f, 0xfd, 0x18, 0x1f, 0x9f, 0xf9, 0xf0, 0x19, 0xf, 
0xf0, 0xc0, 0x1, 0x38, 0x1e, 0x40, 0x1, 0xf1, 0x1f, 0xe0, 0x1, 0xb3, 0xd0, 0xe0, 0x0, 
0x3e, 0x50, 0x40, 0x0, 0x3c, 0x70, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,   
 };

const uint8_t mountain[] U8G_PROGMEM =  { 
0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xc0, 0x0, 0x0, 0x0, 0x0, 
0xf, 0xe0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0xf8, 0x0, 
0x0, 0x0, 0x0, 0x3f, 0x78, 0x0, 0x0, 0x0, 0x0, 0x7b, 0x3c, 0x0, 0x0, 0x0, 0x0, 
0xf3, 0x8e, 0x0, 0x0, 0x0, 0x81, 0xb1, 0xc7, 0x80, 0x0, 0x1, 0xc3, 0x30, 0xe1, 0xe0, 
0x0, 0x3, 0xe3, 0x21, 0xf0, 0x70, 0x0, 0x7, 0xe2, 0x60, 0x0, 0x1c, 0x0, 0x7, 0xf6, 
0x60, 0x0, 0x6, 0x0, 0x1f, 0xfe, 0xc0, 0x0, 0x2, 0x0, 0x3e, 0x3c, 0x80, 0x0, 0x0, 
0x0, 0x3c, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x78, 0xb, 0x0, 0x0, 0x0, 0x0, 0x78, 0x1a, 
0x0, 0x0, 0x0, 0x0, 0xd8, 0x1a, 0x0, 0x0, 0x0, 0x0, 0xd8, 0x30, 0x0, 0x0, 0x0, 
0x1, 0xb0, 0x24, 0x0, 0x0, 0x0, 0x1, 0x30, 0x60, 0x0, 0x0, 0x0, 0x3, 0x70, 0x40, 
0x0, 0x0, 0x0, 0x2, 0x60, 0xc0, 0x0, 0x0, 0x0, 0x6, 0x81, 0x80, 0x0, 0x0, 0x0, 
0x5, 0x83, 0x0, 0x0, 0x0, 0x0, 0xc, 0xe, 0x0, 0x0, 0x0, 0x0, 0x8, 0x1c, 0x0, 
0x0, 0x0, 0x0, 0x18, 0x70, 0x0, 0x0, 0x0, 0x0, 0x30, 0xc0, 0x0, 0x0, 0x0, 0x0, 
0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
0x0, 0x0, 0x0, 
 };

//notes
#define  c     3830    // 261 Hz 
#define  d     3400    // 294 Hz 
#define  e     3038    // 329 Hz 
#define  f     2864    // 349 Hz 
#define  g     2550    // 392 Hz 
#define  a     2272    // 440 Hz 
#define  b     2028    // 493 Hz 
#define  C     1912    // 523 Hz 


int melody[] = {  C,  b,  g,  C,  b,   e};
byte ni=0;
int MAX_COUNT = 5;

void setup(){
   pinMode(button_set, INPUT);
   pinMode(button_up, INPUT);
   pinMode(button_down, INPUT);
   pinMode(button_next, INPUT);
   pinMode(backlight_pin, OUTPUT);
   pinMode(speaker_pin , OUTPUT);
   
  Serial.begin(115200);
  rtc.begin();

  u8g.setRot180();
}

void buttons(){
  //katastash buttons
  button_set_state = digitalRead(button_set);
  button_up_state = digitalRead(button_up);
  button_down_state = digitalRead(button_down);
  button_next_state = digitalRead(button_next);
}

void detectAlarm(){
  if(t.hour == alarm_Hour && t.min == alarm_Min && t.sec == 0 && alarm_Active == 1){
    alarm_Ring = 1;
  }
}

void defineEpoch(){
  if(t.mon<=10 && t.mon>=5) summer = 1;
  else summer = 0;
  if(t.hour<=20 && t.hour>=6) day=1;
  else day=0;
}

void detectKeypress(){
  byte tempHr = t.hour;
  byte tempMn = t.min;
  byte tempDay = t.date;
  byte tempMonth = t.mon;
  int tempYear = t.year;
 
  //apenergopoihsh ksypnitiriou
  if(alarm_Ring == 1){
    if(button_set_state == HIGH || button_up_state == HIGH || button_down_state == HIGH || button_next_state == HIGH){
      alarm_Ring = 0;
      noTone(speaker_pin);
    }
  } else {

    //anama backlight otan patame koumpia
     if(button_set_state == HIGH || button_up_state == HIGH || button_down_state == HIGH || button_next_state == HIGH) {
      previousMillis = millis();
      backlight_state = 1;
     }
    
    //epilogh o8onwn
    if(button_set_state == HIGH){
      if(screenMode ==3) screenMode = 0;
      else screenMode++;
      valset = 0;//gia na ksekimame apo thn arxh panta
      delay(200);
    }
    
    if(screenMode == 1){//ry8mish wras
        if(button_next_state == HIGH){
          if(valset == 1) valset = 0;
          else valset++;
          delay(200);
        }
        if(valset == 0){//wra
          if(button_up_state == HIGH){
            if(tempHr==23) tempHr=0;
            else tempHr++;
            rtc.setTime(tempHr, tempMn, 0);
            delay(200);
          }
          if(button_down_state == HIGH){
            if(tempHr==0) tempHr = 23;
            else tempHr--;
            rtc.setTime(tempHr, tempMn, 0);
            delay(200);
          }
        } else if(valset==1){//lepta
          if(button_up_state == HIGH){
            if(tempMn==59) tempMn=0;
            else tempMn++;
            rtc.setTime(tempHr, tempMn, 0);
            delay(200);
          }
          if(button_down_state == HIGH){
            if(tempMn==0) tempMn = 59;
            else tempMn--;
            rtc.setTime(tempHr, tempMn, 0);
            delay(200);
          }
        }
    } else if(screenMode ==2){//ry8mish hmeromhnias
       if(button_next_state == HIGH){
        if(valset == 2) valset = 0;
        else valset++;
        delay(200);
      }//next btt
      
      if(valset == 0){//mera
          if(button_up_state == HIGH){
            if(tempDay==31) tempDay=0;
            else tempDay++;
            rtc.setDate(tempDay, tempMonth, tempYear);
            delay(200);
          }
          if(button_down_state == HIGH){
            if(tempDay==0) tempDay = 31;
            else tempDay--;
            rtc.setDate(tempDay, tempMonth, tempYear);
            delay(200);
          }
        } else if(valset == 1){//mhnas
           if(button_up_state == HIGH){
            if(tempMonth==12) tempDay=0;
            else tempMonth++;
            rtc.setDate(tempDay, tempMonth, tempYear);
            delay(200);
          }
          if(button_down_state == HIGH){
            if(tempMonth==0) tempMonth = 12;
            else tempMonth--;
            rtc.setDate(tempDay, tempMonth, tempYear);
            delay(200);
          }
        } else if(valset == 2){//xronos
          if(button_up_state == HIGH){
            tempYear++;
            rtc.setDate(tempDay, tempMonth, tempYear);
            delay(200);
          }
          if(button_down_state == HIGH){
            tempYear--;
            rtc.setDate(tempDay, tempMonth, tempYear);
            delay(200);
          }
        }
    } else if(screenMode ==3){//ry8mish ksypnitiriou
      if(button_next_state == HIGH){
          if(valset == 2) valset = 0;
          else valset++;
          delay(200);
        }
       if(valset == 0){//wra
          if(button_up_state == HIGH){
            if(alarm_Hour==23) alarm_Hour=0;
            else alarm_Hour++;
            delay(200);
          }
          if(button_down_state == HIGH){
            if(alarm_Hour==0) alarm_Hour = 23;
            else alarm_Hour--;
            delay(200);
          }
        }  else if(valset==1){//lepta
          if(button_up_state == HIGH){
            if(alarm_Min==59) alarm_Min=0;
            else alarm_Min++;
            delay(200);
          }
          if(button_down_state == HIGH){
            if(alarm_Min==0) alarm_Min = 59;
            else alarm_Min--;
            delay(200);
          }
        } else if(valset==2){//energopoish ksypnitiriou
            if(button_up_state == HIGH) alarm_Active = 1;
            if(button_down_state == HIGH) alarm_Active = 0;
        }
    }

  }
}

void loop(){
  t = rtc.getTime();
  unsigned long currentMillis = millis();
    
  buttons();
  detectAlarm();
  detectKeypress();
  defineEpoch();
  
  //backlight o8onhs
  if(backlight_state == 1) {
    if (currentMillis - previousMillis >= 10000) backlight_state = 0;
    digitalWrite(backlight_pin,HIGH);
  } else digitalWrite(backlight_pin,LOW);

  //otan xtypaei to ksypnitiri
  if(alarm_Ring == 1){
    u8g.firstPage(); 
    do {
      ringAlarm();

      
      
      //backlight ksypnitirou
      if (currentMillis - previousMillis >= 250) {
        previousMillis = currentMillis;
        if (backlight_state == 0) {
          backlight_state = 1;
          digitalWrite(backlight_pin,HIGH);
          tone(speaker_pin , melody[ni]);
          if(ni==MAX_COUNT) ni=0;
          else ni++;
          
        } else {
          backlight_state = 0;
          digitalWrite(backlight_pin,LOW);
          tone(speaker_pin , melody[ni]);
          if(ni==MAX_COUNT) ni=0;
           else ni++;

        }
        
      }
    } while( u8g.nextPage() );
    
  } else {
    
    if(screenMode == 0){//screen1 default wra
      u8g.firstPage(); 
      do {
        showTime();  
      } while( u8g.nextPage() );
    } else if(screenMode == 1){//screen2 ry8mish wras
      u8g.firstPage(); 
      do {
        showTimeSet();  
      } while( u8g.nextPage() );
    } else if(screenMode == 2){//screen3 ry8mish hmeromhnias
      u8g.firstPage(); 
      do {
        showDateSet();  
      } while( u8g.nextPage() );
    } else if(screenMode == 3){//screen4 ry8mish ksypnitiriou
      u8g.firstPage(); 
      do {
        showAlarmSet();  
      } while( u8g.nextPage() );
    }
  }
}

//Screen modes
void showTime(){
    
    //wra
    u8g.setFont(u8g_font_fub20n);
    u8g.setPrintPos(0, 22);
    u8g.print(rtc.getTimeStr(FORMAT_SHORT));
    
    //defterolepta
    char secbuffer[2];
    sprintf(secbuffer,"%02u",t.sec);
    u8g.setFont(u8g_font_5x8);
    u8g.print(":");
    u8g.print(secbuffer);

    //kampanaki ksypnitiriou
    if(alarm_Active == 1) u8g.drawBitmapP( 73, 5, 1, 8, bell_active);
    
    //lektiko meras
    u8g.setPrintPos(0,32);
    u8g.print(rtc.getDOWStr());

    //hmeromhnia
    u8g.setPrintPos(0, 47);
    u8g.setFont(u8g_font_helvR10);
    u8g.print(rtc.getDateStr(FORMAT_LONG,FORMAT_LITTLEENDIAN,'/'));

    //diamorfwsh 8emrokrasias
    float temp = rtc.getTemp();
    static char outstr[15];
    dtostrf(temp,4, 1, outstr);
    u8g.setPrintPos(0, 62);
    u8g.print(outstr);
    u8g.print("\260C");//celcius

    if(day ==1) u8g.drawBitmapP( 95, 0, 4, 30, sun);//mera  
    else u8g.drawBitmapP( 95, 0, 4, 32, moon);//nyxta

    if(summer == 1){
      u8g.drawBitmapP( 82, 36, 4, 32, palm2);//kalokairi
      u8g.drawBitmapP( 105, 34, 4, 32, palm);    
    } else  u8g.drawBitmapP( 85, 36, 6, 48, mountain);//xeimwnas
      
    
    
}

void ringAlarm(){//mhnyma ksipnitirioy
  u8g.drawStr(10,22,"ALARM ACTIVE!");
}

void showTimeSet(){
  u8g.setColorIndex(1);
  u8g.drawBox(0,0,128,18);
  u8g.setColorIndex(0);
  u8g.drawStr(30,15,"SET TIME");
  u8g.setColorIndex(1);

  //wra
  u8g.setPrintPos(12, 25); 
  u8g.setScale2x2();
  u8g.print(rtc.getTimeStr(FORMAT_SHORT));
  u8g.undoScale();
  if(valset == 0) u8g.drawLine(25,53,53,53);
  if(valset == 1) u8g.drawLine(65,53,90,53);
}

void showDateSet(){
  u8g.setColorIndex(1);
  u8g.drawBox(0,0,128,18);
  u8g.setColorIndex(0);
  u8g.drawStr(30,15,"SET DATE");
  u8g.setColorIndex(1);

  //hmerominia
  u8g.setPrintPos(24, 35);
  u8g.print(rtc.getDateStr(FORMAT_LONG,FORMAT_LITTLEENDIAN,'/'));
  if(valset == 0) u8g.drawLine(24,36,40,36);
  if(valset == 1) u8g.drawLine(45,36,58,36);//20 
  if(valset == 2) u8g.drawLine(65,36,93,36);//20 40  
}

void showAlarmSet(){

  u8g.setFont(u8g_font_helvR10);
  u8g.setColorIndex(1);
  u8g.drawBox(0,0,128,18);
  u8g.setColorIndex(0);
  u8g.drawStr(23,15,"SET ALARM");
  u8g.setColorIndex(1);
 
  //wra k lepta ksypnitiriou
  u8g.setPrintPos(9, 25);
  u8g.setScale2x2();
  if(alarm_Hour<10) u8g.print("0");
  u8g.print(alarm_Hour);
  u8g.print(":");
  if(alarm_Min<10) u8g.print("0");
  u8g.print(alarm_Min);
  u8g.undoScale();
  if(valset == 0) u8g.drawLine(20,53,48,53);
  if(valset == 1) u8g.drawLine(60,53,87,53);
  if(valset == 2) u8g.drawLine(95,53,105,53);

  //kampanaki k titlos
  u8g.setScale2x2();
  if(alarm_Active == 1) u8g.drawBitmapP( 45, 13, 1, 8, bell_active);
  else u8g.drawBitmapP( 45, 13, 1, 8, bell_inactive);
  u8g.undoScale();
  
  u8g.setFont(u8g_font_5x8);
  if(alarm_Active == 1) {
    u8g.setPrintPos(95,50);
    u8g.print("ON");
  }
  else {
    u8g.setPrintPos(93,50);
    u8g.print("OFF");
  } 
}

Well thats all , i hope i`ve documented everything right. Oh well.. If you are thinking about making it first build it on a breadboad just in case 😉