David David - 3 months ago 24
Python Question

Displaying Raspberry Pi clock on LCD display

I have a Raspberry Pi running an LCD display. I found the Python script below which displays the time on the display, but I notice there's a delay of almost a second between the Pi's time and the LCD.

I tried removing the last line of the code (sleep(1)) and that removed the delay but the LCD flickers a lot.

Can anyone please explain the function of that last line, and whether there's a way to display the time on the LCD without a delay?

Thanks.

#!/usr/bin/python

import RPi.GPIO as GPIO
from Adafruit_CharLCD import Adafruit_CharLCD
from subprocess import *
from time import sleep, strftime
from datetime import datetime

lcd = Adafruit_CharLCD()

lcd.begin(16,1)

GPIO.setup(18, 0)
GPIO.output(18, 1)

while 1:
lcd.clear()
lcd.message(datetime.now().strftime('%b %d %H:%M:%S\n'))
sleep(1)

Answer

The sleep(1) instructs the program to "sleep" for 1 second. This is actually very significant because that means that the while loop (which is always true) will then only run once every second. When you take away the sleep(1) line, that means the while loop never takes a break and continues running as fast as the computer can go infinitely. One thing you might try is just reducing the sleep(1) to some smaller value. For example, you can specify sleep(0.1)

By reducing the amount of time it sleeps, that means the clock will update more frequently. Right now with sleep(1) the clock updates once every second, which makes perfect sense. However, you might increase accuracy by having the clock update 10 times every second with sleep(0.1). Let me know if this all makes sense.

The flickering is likely caused by the fact that the program is clearing and repopulating the display so many times a second.

Edit: Documentation suggests that sending decimal values to the sleep() function is valid

Edit 2: An extra bit about how to only refresh the display if right at the turn of a second

from datetime import datetime
from time import sleep, mktime

dti = mktime(datetime.now().timetuple())
while 1:
    ndti = mktime(datetime.now().timetuple())
    if dti < ndti:
        dti = ndti
        lcd.clear()
        lcd.message(datetime.now().strftime('%b %d  %H:%M:%S\n'))
        sleep(0.95)
    else:
        sleep(0.01)

In essence, here's how it works:

When starting the program, create a datetime in integer form (our var dti). By "integer form" I mean add up all the seconds from some arbitrary start date (e.g. 1970-01-01 00:00:00) and use that as a reference for time. For example, today (2016-08-18 00:00:00) might be something like 1471478400 seconds since 1970. Once we have that value, we start our loop.

At the beginning of the loop, we always create a new datetime integer (ndti) to track where we are since the last time we ran the loop. With this information handy, we hop into the if conditional. if our new datetime integer (ndti) has changed fully by one second compared to our old datetime integer (dti) then that means, obviously, one second has passed. Since that is the case, we will now set our reference datetime integer to the datetime now (dti = ndti). Then we display our message (lcd.clear() and lcd.message()). After that we will sleep the program for just under 1 whole second (sleep(0.95)). Since some computers can possibly sleep more than the exact allotted time, this gives us .05 seconds of cushion to be inaccurate. In the event that we are running through the if conditional and a second has not passed yet, we would sleep the program for a short time and continue repeating until a second has actually passed (sleep(0.01)).

If everything goes exactly as planned, then for each second our program should be refreshing the lcd screen only once, and it should also be sleeping for roughly 95% of that second, so that we aren't spinning our wheels for no reason the whole time. Another part to note is that since our else clause tells the program to sleep for 0.01 seconds, that means that, in most cases, our clock can only ever be inaccurate by a margin of 0.01 seconds, which is quite good. This is low enough to be mostly undetectable by humans. Let me know if all this makes sense.

I tested this out via command line (replacing the lcd stuff with simple print statements) and it seemed to line up exactly with another time tracking resource (http://time.is/)

Try it out and see if it works for you.