Text Clock

I decided to design a sketch that generates a text clock on the LCD screen. Hardware needed:

  • Arduino Mega
  • Itead 3.2" LCD ITDB32S
  • Arduino Mega Shield including RTC clock
  • DS1307 library from Henning Karlsen
  • UTFT library from Henning Karlsen (needs a small change applied)

Design

The Desing of the clock is as follows:

 

 

 

 

 

 

 

 

 

 

 

 

The clock changes the highlighted text every five minutes. The bar at the left shows the seconds moving and the blocks at the bottom show the minutes moving.

The UTFT library needs a small change to be applied: Move the routine drawChar into the public area in UTFT.h

The program understand some serial commands:

  • You can set the clock using a serial command with the format ShhmmssDDMMYYYYE. This will set the RTC clock if selected.
  • SDEMOE toggles the demo mode. If demo mode is selected, the RTC readout is switched off and the minutes are advanced like seconds to show the clock update every 5 seconds. If switched back by resending SDEMOE, you still need to switch on the RTC clock
  • SRTCE toggles the use of the RTC clock. It may take up to 5 mkinutes until the minute blocks are updated correctly.

This is the code

/*
  textclock.ino - Text Clock using an Arduino Mega and the ITDB32S/RTC shield
  Copyright (C)2012 Dr. Mathias Wilhelm. All right reserved

  If you make any modifications or improvements to the code, I would
  appreciate that you share the code with me so that I might include
  it in the next release. I can be contacted through
  http://www.mathias-wilhelm.de/arduino

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.
*/
#include <UTFT.h>
#include <DS1307.h>

extern uint8_t SmallFont[];         // Declare which fonts we will be using
UTFT myGLCD(ITDB32S,38,39,40,41);   // ITDB02-3.2S on ITDB02 arduino shield on arduino MEGA
DS1307 rtc(20, 21);                 // build in RTC of the Arduino Mega Shield

#define StartTag   83  // S-Zeichen
#define EndTag     69  // E-Zeichen

boolean c_debug=false;
boolean c_demo=false;
int c_demoMin;
int elapsed;
boolean c_rtc=true; // use rtc clock

long t_ms;
Time t;

int lastsec = 0;
int lastmin = 0;
boolean sec_highlight=false;
boolean min_highlight=false;
int c_space = 23;
int bufferCount;    // Anzahl der eingelesenen Zeichen
char buffer[20];    // Serial Input-Buffer

int Clock_x = 13;
int Clock_y = 8;
char ClockMatrix[104] = {
  'I','T','R','I','S','C','T','E','N','H','A','L','F',
  'Q','U','A','R','T','E','R','T','W','E','N','T','Y',
  'F','I','V','E','C','M','I','N','U','T','E','S','H',
  'P','A','S','T','T','O','E','O','N','E','T','W','O',
  'T','H','R','E','E','F','O','U','R','F','I','V','E',
  'S','I','X','S','E','V','E','N','E','I','G','H','T',
  'N','I','N','E','T','E','N','E','L','E','V','E','N',
  'T','W','E','L','V','E','L','O','C','L','O','C','K'
};
char ClockColor[104];

/********************************************************
  standard setup routine
********************************************************/
void setup() {
  myGLCD.InitLCD();
  rtc.halt(false);   // Set the clock to run-mode

  if (!c_rtc) {
    t.hour = 0;
    t.min = 0;
    t.sec = 0;
    t.date = 0;
    t.mon = 0;
    t.year = 0;
    t.dow = 0;
  } else {
    t = rtc.getTime();
    c_demoMin = t.min;
  }
  Serial.begin(9600);
  Serial.println("Enter time ticket to set time");
  Serial.println("Format: ShhmmssddmmyyyyE");
  Serial.println("SDEMOE to toggle demo mode");
  Serial.println("SRTCE  to toggle RTC mode");
  Serial.println("SDEBUGE  to toggle debug mode");

  myGLCD.setFont(SmallFont);
  myGLCD.clrScr();
  myGLCD.setColor(127,127,127);
  for (int i=0; i<5; i++){
    myGLCD.fillRect((5+i)*c_space-2, 208, (5+i)*c_space+6, 216);    
  }
  myGLCD.setColor(255,255,255);
  t_ms = millis();
  if (c_debug) Serial.begin(9600);
  delay(1000);
  if (c_debug) Serial.println("TextClock started");
  c_clear();
  c_setMatrix();
  c_display();
  myGLCD.drawLine(197,187,197,190);
  myGLCD.setColor(127, 127, 127);
  myGLCD.drawLine(0,200,9,200);
  if (c_demo) myGLCD.print("minutes linked to seconds for demo",8,8);
  myGLCD.print("www.mathias-wilhelm.de/arduino",CENTER,227);
  c_drawMinScale(t.min);
  c_drawSecScale(t.sec);
  c_drawMinute(t.min);
  c_drawSecond(t.sec);
}

/********************************************************
  standard loop routine
********************************************************/
void loop() {
  c_update();
  if (t.sec != lastsec){
    lastsec = t.sec;
    if (c_debug){
      if(t.hour<10) Serial.print('0');
      Serial.print(t.hour);
      Serial.print(":");
      if(t.min<10) Serial.print('0');
      Serial.print(t.min);
      Serial.print(":");
      if(t.sec<10) Serial.print('0');
      Serial.print(t.sec);
      Serial.println(" ");
    }
    c_drawSecond(t.sec);
    if (c_demo) {
      if (t.sec<10) {
        myGLCD.printNumI(0,8,218);
        myGLCD.printNumI(t.sec,16,218);
      } else {
        myGLCD.printNumI(t.sec,8,218);
      }
    }
  }
  if (t.min != lastmin){
    lastmin = t.min;
    c_clear();
    c_setMatrix();
    c_display();
    c_drawMinute(t.min);
    if (c_demo) {
      if (t.min<10) {
        myGLCD.printNumI(0,218,218);
        myGLCD.printNumI(t.min,226,218);
      } else {
        myGLCD.printNumI(t.min,218,218);
      }
    }
  }
}

/********************************************************
  serial interaction routines
********************************************************/
void serialEvent(){
  char ch = Serial.read();
  buffer[bufferCount] = ch;
  bufferCount++;
  if(ch == 13) evalSerialData();
}

void evalSerialData(){
  int cls=0x0c;
  boolean sok = false;
  // toggle debug mode
  if((buffer[0] == StartTag) && (buffer[6] == EndTag))
  {
    if ((buffer[1] == 'D') && (buffer[2] == 'E') && (buffer[3] == 'B') && (buffer[4] == 'U') && (buffer[5] == 'G'))
    {
      sok = true;
      c_debug = !c_debug;
      if (c_rtc) {
        Serial.println("Switched to DEBUG mode");
      } else {
        Serial.println("Swicthed back to normal mode");
      }
    }
  }
  // toggle RTC mode
  if((buffer[0] == StartTag) && (buffer[4] == EndTag))
  {
    if ((buffer[1] == 'R') && (buffer[2] == 'T') && (buffer[3] == 'C'))
    {
      sok = true;
      c_rtc = !c_rtc;
      if (c_rtc) {
        Serial.println("Using RTC clock");
        t = rtc.getTime();
        c_drawMinScale(t.min);
        c_drawSecScale(t.sec);
        c_drawMinute(t.min);
        c_drawSecond(t.sec);
      } else {
        Serial.println("Using Arduino clock");
      }
    }
  }
  // toggle demo mode
  if((buffer[0] == StartTag) && (buffer[5] == EndTag))
  {
    if ((buffer[1] == 'D') && (buffer[2] == 'E') && (buffer[3] == 'M') && (buffer[4] == 'O'))
    {
      sok = true;
      c_demo = !c_demo;
      if (c_demo)
      {
        if (c_rtc) c_rtc=false;
        Serial.println("Minutes linked to seconds for Demo");
        Serial.println("RTC reading switched off  for Demo");
        myGLCD.setColor(255,255,255);
        myGLCD.print("Minutes linked to seconds for Demo",8,8);
      } else
      {
        Serial.println("Switched to normal time mode");
        Serial.println("Remember to switch clock back to RTC");
        myGLCD.setColor(0,0,0);
        myGLCD.fillRect(0,0,320,20);
        myGLCD.fillRect(0,218,320,230);
      }
    }
  }
  // serial time ticket: ShhmmssddmmyyyyE
  //                     0123456789012345
  //                               111111
  // i.e. S15364318092012E
  if((buffer[0] == StartTag) && (buffer[15] == EndTag))
  {
    sok = true;
    t.hour = (buffer[ 1] - 48) * 10   + (buffer[ 2] - 48);
    t.min  = (buffer[ 3] - 48) * 10   + (buffer[ 4] - 48);
    t.sec  = (buffer[ 5] - 48) * 10   + (buffer[ 6] - 48);
    t.date = (buffer[ 7] - 48) * 10   + (buffer[ 8] - 48);
    t.mon  = (buffer[ 9] - 48) * 10   + (buffer[10] - 48);
    t.year = (buffer[11] - 48) * 1000 + (buffer[12] - 48) * 100 + (buffer[13] - 48) * 10 + (buffer[14] - 48);
    t.hour = constrain(t.hour,0,23);
    t.min  = constrain(t.min ,0,59);
    t.sec  = constrain(t.sec ,0,59);
    t.date = constrain(t.date,0,31);
    t.mon  = constrain(t.mon ,0,12);
    t.year = constrain(t.year,0,9999);
    Serial.print("Time set to : ");
    if(t.hour<10) Serial.print('0');
    Serial.print(t.hour);
    Serial.print(":");
    if(t.min<10) Serial.print('0');
    Serial.print(t.min);
    Serial.print(":");
    if(t.sec<10) Serial.print('0');
    Serial.print(t.sec);
    Serial.print(" - ");
    if(t.date<10) Serial.print('0');
    Serial.print(t.date);
    Serial.print(".");
    if(t.mon<10) Serial.print('0');
    Serial.print(t.mon);
    Serial.print(".");
    Serial.print(t.year);
    Serial.println(" ");
    c_clear();
    c_setMatrix();
    c_display();
    c_drawMinute(t.min);
    c_drawMinScale(t.min);
    c_drawSecScale(t.sec);
    if (c_rtc) {
      rtc.setTime(t.hour,t.min,t.sec);
      rtc.setDate(t.date,t.mon,t.year);
    }
  }
  if (!sok) {
    Serial.print("Invalid Serial Command :");
    for (int i=0; i<bufferCount; i++) Serial.print(buffer[i]);
    Serial.println(" received");
  }
  buffer[0] = '.';
  buffer[5] = '.';
  buffer[15] = '.'; // set Buffer invalid
  bufferCount = 0;  // Reset Buffer Counter
}

/********************************************************
  drawing routines
********************************************************/
void c_drawMinScale(int c_min){
  boolean scaleHighlight;
  int mp;
  mp = c_min - 1;
  if (mp < 0) mp = 59;
  mp = mp % 5;
  // draw background
  if (mp==0) {
    scaleHighlight = !min_highlight;
  } else {
    scaleHighlight = min_highlight;
  }
  if (!scaleHighlight){
    myGLCD.setColor(255, 255, 255);
  }
  else {
    myGLCD.setColor(127, 127, 127);
  }
  for (int m=0; m<5;m++){
    myGLCD.fillRect((5+m)*c_space-2, 208, (5+m)*c_space+6, 216);
  }
  // draw foreground
  if (scaleHighlight){
    myGLCD.setColor(255, 255, 255);
  }
  else {
    myGLCD.setColor(127, 127, 127);
  }
  for (int m=0; m<=mp;m++){
    myGLCD.fillRect((5+m)*c_space-2, 208, (5+m)*c_space+6, 216);
  }
}

void c_drawSecScale(int c_sec){
  boolean scaleHighlight;
  int x1, x2, y;
  int ybase = 200;
  int yspace = 3;
  // draw background
  if (c_sec==0) {
    scaleHighlight = !sec_highlight;
  } else {
    scaleHighlight = sec_highlight;
  }
  if (!scaleHighlight){
    myGLCD.setColor(255, 255, 255);
  }
  else {
    myGLCD.setColor(127, 127, 127);
  }
  for (int m=0; m<60;m++){
    if ((m % 5) == 0) {
      x1 = 0;
      x2 = 9;
    }
    else {
      x1 = 2;
      x2 = 7;
    }
    y = ybase - m*yspace;
    myGLCD.drawLine(x1,y,x2,y);
  }
  // draw foreground
  if (scaleHighlight){
    myGLCD.setColor(255, 255, 255);
  }
  else {
    myGLCD.setColor(127, 127, 127);
  }
  for (int m=0; m<=c_sec;m++){
    if ((m % 5) == 0) {
      x1 = 0;
      x2 = 9;
    }
    else {
      x1 = 2;
      x2 = 7;
    }
    y = ybase - m*yspace;
    myGLCD.drawLine(x1,y,x2,y);
  }
}

void c_drawMinute(int c_min){
  int m;
  m = c_min - 1;
  if (m < 0) m = 59;
  m = m % 5;
  if (m==0) min_highlight = !min_highlight;
  if (min_highlight){
    myGLCD.setColor(255, 255, 255);
  }
  else {
    myGLCD.setColor(127, 127, 127);
  }
  myGLCD.fillRect((5+m)*c_space-2, 208, (5+m)*c_space+6, 216);
}

void c_drawSecond(int c_sec){
  int x1, x2, y;
  int ybase = 200;
  int yspace = 3;
  if (c_sec==0) sec_highlight = !sec_highlight;
  if (sec_highlight){
    myGLCD.setColor(255, 255, 255);
  }
  else {
    myGLCD.setColor(127, 127, 127);
  }
  if ((c_sec % 5) == 0) {
    x1 = 0;
    x2 = 9;
  }
  else {
    x1 = 2;
    x2 = 7;
  }
  y = ybase - c_sec*yspace;
  myGLCD.drawLine(x1,y,x2,y);
}

void c_display(){
  myGLCD.setColor(255, 255, 255);
  if (c_debug) Serial.println("Drawing Matrix");
  for (int i=0; i<8; i++){
    for (int j=0; j<13;j++){
      if (ClockColor[i*13+j] == 0){
        myGLCD.setColor(127, 127, 127);
      }
      else {
        myGLCD.setColor(255, 255, 255);
      }
      myGLCD.printChar(ClockMatrix[i*13+j], (j+1)*c_space, (i+1)*c_space);
    }
  }
}

/********************************************************
  fill Matrix routines
********************************************************/
void c_setMatrix(){
  int c_hour;
  c_hour = t.hour % 12;
  c_it();
  c_is();
  if (t.min<5) {
  } else {
    if (t.min<10) { c_five(); c_minutes(); c_past();
    } else {
      if (t.min<15) { c_ten(); c_minutes(); c_past();
      } else {
        if (t.min<20) { c_quarter(); c_past();
        } else {
          if (t.min<25) { c_twenty(); c_minutes(); c_past();
          } else {
            if (t.min<30) { c_twenty(); c_five(); c_minutes(); c_past();
            } else {
              if (t.min<35) { c_half(); c_past();
              } else {
                if (t.min<40) { c_twenty(); c_five(); c_minutes(); c_to(); c_hour++;
                } else {
                  if (t.min<45) { c_twenty(); c_minutes(); c_to(); c_hour++;
                  } else {
                    if (t.min<50) { c_quarter(); c_to(); c_hour++;
                    } else {
                      if (t.min<55) { c_ten(); c_minutes(); c_to(); c_hour++;
                      } else {
                        { c_five(); c_minutes(); c_to(); c_hour++;
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  if (c_hour>11) c_hour=0;
  switch (c_hour) {
  case 0: c_twelve();
          break;
  case 1: c_one();
          break;
  case 2: c_two();
          break;
  case 3: c_three();
          break;
  case 4: c_four();
          break;
  case 5: c_fiveh();
          break;
  case 6: c_six();
          break;
  case 7: c_seven();
          break;
  case 8: c_eight();
          break;
  case 9: c_nine();
          break;
  case 10: c_tenh();
          break;
  case 11: c_eleven();
          break;
  }
  c_oclock();
}

/********************************************************
  clock update routines
********************************************************/
void c_update(){
  long ctime;
  int daypermonth[12] = {
    31,28,31,30,31,30,31,31,30,31,30,31};
  if (!c_rtc){
    ctime = millis();
    elapsed= (ctime - t_ms)/1000;
    if (elapsed>0) {
      t_ms = ctime;
      t.sec = t.sec + elapsed;
      if (c_demo) t.min++; // DEMO ONLY ADVANCE TIME FASTER
      if (t.sec>59){
        t.sec = 0;
        t.min++;
      }
      if (t.min>59){
        t.min = 0;
        t.hour++;
      }
      if (t.hour>23){
        t.hour = 0;
        t.date++;
        t.dow++;
      }
      if (t.date>daypermonth[t.mon-1]){
        t.mon = 1;
        t.year++;
      }
      if (t.dow>6) t.dow=0;
    }
  } else {
    t = rtc.getTime();
  }
}

void c_clear(){
  // grey out matrix
  for (int i=0; i<8; i++){
    for (int j=0; j<13;j++){
      ClockColor[i*13+j] = 0;
    }
  }
}

void c_it()      { if (c_debug) Serial.print("it ");       ClockColor[0*13+ 0]=1; ClockColor[0*13+1]=1;}
void c_is()      { if (c_debug) Serial.print("is ");       ClockColor[0*13+ 3]=1; ClockColor[0*13+ 4]=1;}
void c_ten()     { if (c_debug) Serial.print("ten ");      ClockColor[0*13+ 6]=1; ClockColor[0*13+ 7]=1; ClockColor[0*13+ 8]=1;}
void c_half()    { if (c_debug) Serial.print("half ");     ClockColor[0*13+ 9]=1; ClockColor[0*13+10]=1; ClockColor[0*13+11]=1; ClockColor[0*13+12]=1;}
void c_quarter() { if (c_debug) Serial.print("quarter ");  ClockColor[1*13+ 0]=1; ClockColor[1*13+ 1]=1; ClockColor[1*13+ 2]=1; ClockColor[1*13+ 3]=1; ClockColor[1*13+ 4]=1; ClockColor[1*13+ 5]=1; ClockColor[1*13+ 6]=1;}
void c_twenty()  { if (c_debug) Serial.print("twenty ");   ClockColor[1*13+ 7]=1; ClockColor[1*13+ 8]=1; ClockColor[1*13+ 9]=1; ClockColor[1*13+10]=1; ClockColor[1*13+11]=1; ClockColor[1*13+12]=1;}
void c_five()    { if (c_debug) Serial.print("five ");     ClockColor[2*13+ 0]=1; ClockColor[2*13+ 1]=1; ClockColor[2*13+ 2]=1; ClockColor[2*13+ 3]=1;}
void c_minutes() { if (c_debug) Serial.print("minutes ");  ClockColor[2*13+ 5]=1; ClockColor[2*13+ 6]=1; ClockColor[2*13+ 7]=1; ClockColor[2*13+ 8]=1; ClockColor[2*13+ 9]=1; ClockColor[2*13+10]=1; ClockColor[2*13+11]=1;}
void c_past()    { if (c_debug) Serial.print("past ");     ClockColor[3*13+ 0]=1; ClockColor[3*13+ 1]=1; ClockColor[3*13+ 2]=1; ClockColor[3*13+ 3]=1;}
void c_to()      { if (c_debug) Serial.print("to ");       ClockColor[3*13+ 4]=1; ClockColor[3*13+ 5]=1;}
void c_one()     { if (c_debug) Serial.print("one ");      ClockColor[3*13+ 7]=1; ClockColor[3*13+ 8]=1; ClockColor[3*13+ 9]=1;}
void c_two()     { if (c_debug) Serial.print("two ");      ClockColor[3*13+10]=1; ClockColor[3*13+11]=1; ClockColor[3*13+12]=1;}
void c_three()   { if (c_debug) Serial.print("three ");    ClockColor[4*13+ 0]=1; ClockColor[4*13+ 1]=1; ClockColor[4*13+ 2]=1; ClockColor[4*13+ 3]=1; ClockColor[4*13+ 2]=4;}
void c_four()    { if (c_debug) Serial.print("four ");     ClockColor[4*13+ 5]=1; ClockColor[4*13+ 6]=1; ClockColor[4*13+ 7]=1; ClockColor[4*13+ 8]=1;}
void c_fiveh()   { if (c_debug) Serial.print("five ");     ClockColor[4*13+ 9]=1; ClockColor[4*13+10]=1; ClockColor[4*13+11]=1; ClockColor[4*13+12]=1;}
void c_six()     { if (c_debug) Serial.print("six ");      ClockColor[5*13+ 0]=1; ClockColor[5*13+ 1]=1; ClockColor[5*13+ 2]=1;}
void c_seven()   { if (c_debug) Serial.print("seven ");    ClockColor[5*13+ 3]=1; ClockColor[5*13+ 4]=1; ClockColor[5*13+ 5]=1; ClockColor[5*13+ 6]=1; ClockColor[5*13+ 7]=1;}
void c_eight()   { if (c_debug) Serial.print("eight ");    ClockColor[5*13+ 8]=1; ClockColor[5*13+ 9]=1; ClockColor[5*13+10]=1; ClockColor[5*13+11]=1; ClockColor[5*13+12]=1;}
void c_nine()    { if (c_debug) Serial.print("nine ");     ClockColor[6*13+ 0]=1; ClockColor[6*13+ 1]=1; ClockColor[6*13+ 2]=1; ClockColor[6*13+ 3]=1;}
void c_tenh()    { if (c_debug) Serial.print("ten ");      ClockColor[6*13+ 4]=1; ClockColor[6*13+ 5]=1; ClockColor[6*13+ 6]=1;}
void c_eleven()  { if (c_debug) Serial.print("eleven ");   ClockColor[6*13+ 7]=1; ClockColor[6*13+ 8]=1; ClockColor[6*13+ 9]=1; ClockColor[6*13+10]=1; ClockColor[6*13+11]=1; ClockColor[6*13+12]=1;}
void c_twelve()  { if (c_debug) Serial.print("twelve ");   ClockColor[7*13+ 0]=1; ClockColor[7*13+ 1]=1; ClockColor[7*13+ 2]=1; ClockColor[7*13+ 3]=1; ClockColor[7*13+ 4]=1; ClockColor[7*13+ 5]=1;}
void c_oclock()  { if (c_debug) Serial.println("oclock "); ClockColor[7*13+ 7]=1; ClockColor[7*13+ 8]=1; ClockColor[7*13+ 9]=1; ClockColor[7*13+10]=1; ClockColor[7*13+11]=1; ClockColor[7*13+12]=1;}

// end of code