Search This Blog

Monday, February 14, 2011

Arduino LCD Countdown Clock

Quickie project for a desktop clock that counts down to when my work project has to be done, then counts how many weeks it is late.


The design is the "raw electronics" look.  The arduino is mounted on the back of the large LCD display by nothing more than a stacked header, and hot glued to a small piece of wood for a base.

The code is built on top of the basic example for a clock that syncs from the PC, found here:
The Arduino Time Library on the Arduino Playground
http://www.arduino.cc/playground/Code/Time

When plugged into the PC and using the Arduino serial monitor, it waits for you to enter a unix timestamp.
You can generate these from web sites like these ones:
http://www.timestampgenerator.com/
http://www.unixtimestamp.com/index.php

They have the form of seconds since Jan 01 1970, type in T1297687386
Type that in the start the clock.  There are PC programs out there that run a host to send that string, like gobetwino if you want to do that to, you don't need to.  This is just a manual start.

Change this variable in the code to the unix timestamp of the event you want to count down to
signed long tapeout = 1309539600;

The hardware is nothing more than an arduino interfaced to a huge 20x4 backlit LCD module, using the 4 bit wide parallel communication mode.  The pinout is a very slight mod to the descriptions in the Arduino examples, because I like to group the pins all on one header.   This is exactly like the setup in the Arduino examples.

I use an external wall wart as power, so it doesn't have to be plugged into the PC all the time.

Here is the code




* Sketch for 4x20 LCD to display current time and date, and countdown of weeks, days, hours, min to an event
 *
 */

#include <Time.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6 , 5, 4, 3, 2);

#define TIME_MSG_LEN  11   // time sync to PC is HEADER followed by unix time_t as ten ascii digits
#define TIME_HEADER  'T'   // Header tag for serial time sync message
#define TIME_REQUEST  7    // ASCII bell character requests a time sync message

signed long tapeout = 1309539600;

// to make sure the display is cleared once a day
int yesterday = 0;
int weeksleft = 0;
int daysleft = 0;
int hoursleft = 0;
int minleft = 0;


void setup()  {
  lcd.begin(20, 4);
  lcd.clear();
  //backlight on pin 8
  pinMode(8, OUTPUT);
  digitalWrite(8, LOW);
  Serial.begin(9600);
  setSyncProvider( requestSync);  //set function to call when sync required
  Serial.println("Waiting for sync message");
  Serial.println("Unix Timestamp");
  Serial.println("Example T1297674000");
  lcd.setCursor(0,0);
  lcd.print("Waiting for PC time sync...");
}

void loop(){  
  if(Serial.available() )
  {
    processSyncMessage();
    digitalWrite(8, HIGH);
  }
  if(timeStatus()!= timeNotSet)
  {
    digitalClockDisplay();
  
    // clear the display if the day changes to clear glitches
    if (day() != yesterday) {
        //lcd.clear();
        yesterday = day();
    }
  }
  delay(20000);  //can be set to 1000, this makes the display more stable
}

void digitalClockDisplay(){

  //turn on backlight only during workday
  if((hour() >= 8) & (hour() < 19)) digitalWrite(8, HIGH);
  else   digitalWrite(8, LOW);

  // digital clock display of the time
  lcd.setCursor(0,0);
  Serial.print(hour());
  lcd.print(hourFormat12());
  printDigits(minute());
  //printDigits(second());
  Serial.print("   ");
  if (isAM()) lcd.print(" AM ");
  if (isPM()) lcd.print(" PM ");
  Serial.print(dayStr(weekday()));
  lcd.print(dayStr(weekday()));
  Serial.print(" ");
  lcd.print(" ");
  lcd.setCursor(0,1);
  Serial.print(day());
  lcd.print(day());
  Serial.print(" ");
    lcd.print(" ");
  Serial.print(monthShortStr(month()));
    lcd.print(monthShortStr(month()));
  Serial.print(" ");
    lcd.print(" ");
  Serial.print(year());
    lcd.print(year());
  Serial.println();
  lcd.setCursor(0,2);
  if (tapeout < now()) {
      weeksleft = (now() - tapeout)/60/60/24/7;
      daysleft = (now() - tapeout)/60/60/24 - weeksleft*7;
      hoursleft = (now() - tapeout)/60/60 - daysleft*24 - weeksleft*7*24;
      minleft =  (now() - tapeout)/60 - hoursleft*60 - daysleft*24*60 - weeksleft*7*24*60;
      lcd.print(   weeksleft   );
      lcd.print("wk ");
      lcd.print(   daysleft   );
      lcd.print("d ");
      lcd.print(   hoursleft   );
      lcd.print("h ");
      lcd.print(   minleft  );
      lcd.print("m ");
      lcd.setCursor(0,3);
      lcd.print("Late! OMG! Ship It!   ");
  } else {
      weeksleft = (tapeout - now())/60/60/24/7;
      daysleft = (tapeout - now())/60/60/24 - weeksleft*7 ;
      hoursleft = (tapeout - now())/60/60 - daysleft*24 - weeksleft*7*24;
      minleft =  (tapeout - now())/60 - hoursleft*60 - daysleft*24*60 - weeksleft*7*24*60;
      lcd.print(   weeksleft   );
      lcd.print("wk ");
      lcd.print(   daysleft   );
      lcd.print("d ");
      lcd.print(   hoursleft   );
      lcd.print("h ");
      lcd.print(   minleft  );
      lcd.print("m  ");
      lcd.setCursor(0,3);
      lcd.print("To Santan Tapeout! ");
  }
}

void printDigits(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  lcd.print(":");
  if(digits < 10){
    Serial.print('0');
    lcd.print('0');
  }
  Serial.print(digits);
  lcd.print(digits);
}

void processSyncMessage() {
  lcd.clear();
  // if time sync available from serial port, update time and return true
  while(Serial.available() >=  TIME_MSG_LEN ){  // time message consists of a header and ten ascii digits
    char c = Serial.read() ;
    Serial.print(c);
    if( c == TIME_HEADER ) {    
      time_t pctime = 0;
      for(int i=0; i < TIME_MSG_LEN -1; i++){
        c = Serial.read();        
        if( c >= '0' && c <= '9'){
          pctime = (10 * pctime) + (c - '0') ; // convert digits to a number  
        }
      }
      setTime(pctime);   // Sync Arduino clock to the time received on the serial port
    }
  }
}

time_t requestSync()
{
  Serial.print(TIME_REQUEST,BYTE);
  return 0; // the time will be sent later in response to serial mesg
}


2 comments:

  1. does not compile on latest IDE error at
    Serial.print(TIME_REQUEST,BYTE); but i'm a noob so don't know why ..

    ReplyDelete
  2. More fascinating that Mathematicians utilize "LCD" as Lowest Common Denominator. Atkins Diet utilized LCD as Low Carbohydrate Diet. Back to LCD as Liquid Crystal Display, raise projection HDTVs ordinarily are LCD, DLP or LCoS which implies Liquid Crystal on Silicon, they are gradually getting to be adversaries of plasma.Best 55 inch tvs

    ReplyDelete