Go Down

Topic: LCD clock demo, No RTC, Serial time set, LCD code help/suggestions please. (Read 1 time) previous topic - next topic

Ashton

I'm trying to create a simple clock displaying HH:MM:SS, DD/MM/YYYY, on a I2C LCD display, with an Uno, without an RTC.  With the time of day/date (Unix time)being set with a  command sent from a PC via serial port (Arduino USB cable) once to set time and start the lcd display.

Surprisingly I have not found an exact example sketch, but I did find the below sketch from here http://arduino.cc/forum/index.php?topic=12003.0 that I have modified.  I have changed serial outputs to the LCD, and it sort of operates, with the exception of my bad LCD display control.

I'd like to eliminate the clear() and delay() that causes the display to flash, rather than give the illusion of changing the 'seconds' digit only.  Code follows.

Code: [Select]
#include <LiquidCrystal_I2C.h>
#include <Wire.h>

/*
* TimeSerial sketch
* example code illustrating Time library set through serial port messages.
* Messages consist of the letter T followed by ten digit time
* (as seconds since Jan 1 1970)
* You can send the text on the next line using Serial Monitor to set the
* clock to noon Jan 1 2011:
* T1293883200
* A Processing example sketch to automatically send the messages is
* included in the Time library download
*/
#include <Time.h>

#define TIME_MSG_LEN  11   // time sync consists of a HEADER followed by ten
                          // ascii digits
#define TIME_HEADER  'T'   // Header tag for serial time sync message

LiquidCrystal_I2C lcd(0x27,20,4);

void setup()  {
 Serial.begin(9600);
 Serial.println("Waiting for time sync message");
 lcd.init();
  // lcd.backlight();
   lcd.setCursor(0, 0);
}

void loop(){
 if(Serial.available() )
 {
   processSyncMessage();
 }
 if(timeStatus()!= timeNotSet)
 {
   // here if the time has been set
   digitalClockDisplay();
 }
 delay(1000);
}

void digitalClockDisplay(){ //Need to correct LCD flash & only update seconds
 lcd.print(hour());
 lcd.print(":");
 printDigits(minute());
 lcd.print(minute());
 lcd.print(":");
 printDigits(second());
 lcd.print(second());
 lcd.print(" ");  //Was Serial.print(" ");
 lcd.print(month());
 lcd.print("/");
 lcd.print(day());
 lcd.print("/");
 lcd.print(year());
 delay(500);  // Causes LCD flash
 lcd.clear();
}

void printDigits(int digits){ // Still prints serial not a problem.
 // utility function for digital clock display: prints preceding colon & leading 0
 Serial.print(":");
 if(digits < 10)
   Serial.print('0');
 Serial.print(digits);
}

void processSyncMessage() {
 // if time sync available from serial port, update time and return true
 // time message consists of a header and ten ascii digits
 while(Serial.available() >=  TIME_MSG_LEN ){  
   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( isDigit(c)) {
         pctime = (10 * pctime) + (c - '0') ; // convert digits to a number
       }
     }
     setTime(pctime);   // Sync clock to the time received on serial port
   }
 }
}    


Any suggestions greatly appreciated.

qwertysimo

Use couple of variables to store hour, minute, second etc. displayed on LCD. In digitalClockDisplay() add couple of conditions to see what was changed. Example:

if( secondDisplayedOnLcd != second() )
{
 secondDisplayedOnLcd = second();
 lcd.setCursor( proper coordinates for second portion of time on LCD );
 lcd.print( secondDisplayedOnLcd );
}

This way you will update only changed information on LCD - no need for delay() neither clear() in digitalClockDisplay().

Plus, in loop(), you should call digitalClockDisplay() in non-blocking manner. See example sketch for LED flasher without delay() to get idea.

Ashton

I'll working on converting to variables before sending to the display.

But one question, I may have observed that without the clear(), the displayed characters just create one long string, instead of refreshing the string on LCD Line #1.  This continues to Line #3, then to 2, and 4.  Am I wrong? or should there be a command/character to prevent it from scrolling? 

Just a thought while I do the variables...  Thanks.

afremont

That's not going to keep time very well.  The delay(1000) assumes that everything else takes 0 time execute, which is silly of course.  Here is a better way to do it:

Code: [Select]

unsigned long int rolltime = millis() + 1000;
void loop() {

 //  do a signed comparison to handle rollover properly
 while((long)(millis() - rolltime) >= 0) {

   // While we are waiting, buffer any characters being typed  EXAMPLE ONLY

   if(Serial.available() > 0) {
     inChar = Serial.read();

     if(inChar == '\n') {
       inBuf[i] = '\0';
       i++;
       stringDone = 1;
     } else {
       inBuf[i] = inChar;
       if(i < 20)
         i++;
     } // end if
   } // end if
 } // end while

//  This is where you would tick your time fields and update the display
 
 // set next time to update display
 rolltime += 1000;
} //end loop()

Experience, it's what you get when you were expecting something else.

qwertysimo


But one question, I may have observed that without the clear(), the displayed characters just create one long string, instead of refreshing the string on LCD Line #1.  This continues to Line #3, then to 2, and 4.  Am I wrong? or should there be a command/character to prevent it from scrolling?

Without clear(), lcd.print will print character on last used position. You shoud use lcd.setCursor( 0, 0 ) if you want to start refreshing LCD line #1.

Go Up