Big Font Clock - Relieve for Old Eyes
Updated: Dec 14, 2020
Recently the Maker Space that I frequent received a number of 4 line 40 character (4 x 40) LCD displays. They asked me to figure out how to use the displays, a task I took on eagerly. I have had quite a bit of experience working with LCD character displays but never one this large. And I did learn some new things working on the display.
Too Many Characters and Two (many) Controllers
The LCD displays I have used prior to this have been smaller, mostly 2 lines of 16 characters each (2 x 16) or 4 lines of 20 characters each (4 x 20). All these displays have the same basic pin out, and initialization and write routines since they are all have a controller IC that is equivalent to HD44780 or ST7066U. Each display runs on +5 volts with pins for R(ead)/W(rite), Enable, Vee (contrast voltage) and 8 data lines. The controller has on board RAM for up to 80 characters, this is where each character is stored.
The 4 x 40 LCD has a total of 160 characters so the ubiquitous HD44780 equivalent controller will not work by itself. Well if one isn't enough, just use two! The 4 x 40 LCD has two on board controllers with the LCD divided in two, with one controller for the top two lines and the other controller for the bottom two lines. All connections to the controllers are in parallel with the exception of separate enable inputs for each controller. Addressing and controlling this LCD is the same as its smaller cousins except it is done in two steps for each controller individually.
I was able to rewrite my C code LCD libraries for two controllers and the 4 x 40 LCD functioned right away. I played with the LCD and had fun with the separate controllers. After sending the LCD libraries to my Maker Space for their use I was informed that some scoundrel made off with all the other 4 x 40 LCDs!
The 4 x 40 LCD in standard configuration showing separate operation for top and bottom
Let's Do Something Large
Now that the LCD was working I decided to come up with a project that utilized the LCD's large size. I have a tube of large 1" seven segment LED displays that I was planning to use for a clock that I could see with my aging eyes. Perhaps I could use this LCD if I could figure out how to build large numbers using all four lines for each number. Since this LCD had so many characters in each line I could have both time and date displayed. This could be good!
I fooled around a bit with building big numbers and also did some Google searching. Eventually I found folks that have already built their own fonts to use all lines of a LCD. A particular good source was WoodUino.ca who developed big numbers for a 4 line LCD (BTW take a look at this website, there are 55 clock builds posted!). Fortunately the hard part, figuring out how to construct large 0 through 9 figures, was already done. WoodUino built the large numbers utilizing blank characters and block characters that are already part of the LCD font set, with the addition of half blocks and triangles of various orientations that are generated as custom characters. Each controller has 8 RAM locations for custom characters, fortunately only 7 of the 8 were required for 0 through 9 as we will see later.
This was very helpful but the code was written for Arduino and had to use program memory for the large array that is used to construct characters. I don't usually use an Arduino and did not want to fool around with program memory allocation. So I used my modified LCD libraries with my favorite PIC microcontroller and was able to generate custom numbers that used all four lines. But I ran into variable memory restrictions as well. But at least I knew I could generate custom numbers and characters. I could have used another PIC microcontroller with more variable memory space allocated but I had always envision using a Raspberry pi for this project since it is very easy to reach out to internet time servers with a Rpi. So I rewrote all my LCD libraries as well as the big number generating program with python so I could use a Rpi. In addition I wanted the clock to have the capability to display local as well as UTC time, not necessarily at the same instance. UTC time is often refer to as Zulu time and designated with a 'Z'. In order to generate a four line tall 'Z' I needed one more custom character. So it was fortunate that there was one custom character RAM location left!
Time to Build the Clock
Once I had the basic code for generating large numbers and the 'Z', putting it all together as a clock was pretty straightforward. I made it easier by not using backup in case power is removed since the clock will quickly update once power is reapplied, by relying on a WiFi connection to the internet to access NTP servers, using only hours and minutes for time and only month and day for date, and simply relying on Python time and date time modules.
For my use I wanted to see UTC time and date some of the time, local time and date at other times and both times in other situations. With this requirement, the clock has four modes:
mode 0: local and UTC time
mode 1: local time and date
mode 2: UTC time and date
mode 3: toggle between modes 1 and 2
Two toggle switches are used to select the mode of operation. Other than that, the clock boots up by itself and displays time and date according to the mode selected.
I used a Rpi zero W which is the smaller version of the Rpi with built in WiFi. A protoboard was hand wired to provide the interface between the Rpi zero W and the LCD. The protoboard has headers for the toggle switches and for +5V for the LCD and Rpi zero W.
But wait, the Rpi runs on 5 volts but its IOs are only 3.3 volt tolerant while the LCD is a 5 volt device! As it turns out, 3.3 volts is more than adequate to be seen as a high on the LCD inputs. And we are not reading any data from the LCD so there is no issue with 5 volts on the Rpi zero W inputs.
I built a acrylic case for the clock using laser cut pieces of 1/8" acrylic that were solvent welded together. I left the top piece friction fit so I had access to the inside of the enclosure.
The Clock Showing Local Time and Date
Top View of the Clock Showing the Toggle Switches for Mode Selection
Showing All Four Modes of the Clock
Clock software was written using Python 3.6. You can find the code on GitHub. To use the code be sure that all scripts are loading into the same directory so the main script can find the called scripts.
The main script is 4x40_big_font_clock_AI6XG.py This script sets up the GPIOs to interface to the LCD and the toggle switches, initializes the LCD and generates the custom characters to display large numbers, builds special characters for time and date display, detects the mode set by the toggle switches and displays the appropriate time and date. If you are writing a similar script be sure to preserve the order of GPIO set up, lcd initialization and custom character generation before trying to display large numbers. The sequence should be:
Set up GPIOs using: lcd_setup_gpios(DB4, DB5, DB6, DB7, EN0, EN1, RS)
Initiate and clear both controllers using: lcd_init(0) lcd_clear(0) lcd_init(1) lcd_clear(1)
Generate custom characters: generate_big()
Go to home: lcd_goto(0x00,0) lcd_goto(0x40,1)
colon_display(pos, col_on) function allows you to place a colon at position 'pos' and blinking set by col_on
time_display(time_bcd, left) function displays the time starting at position 'left', you may add a colon at position 'left' + 7
date_display( date_bcd, left, date_dash) function displays the date starting at position 'left', date_dash selects if a dash or period is used to separate the month and day
The script checks for time, date and mode at initial startup and then updates the time and date only if the minute or mode has changed in the while(1=1) loop.
The library folder contains three scripts for basic LCD functions, large number generation and return of time and date in BCD format.
LCD_2E_AI6XG.py generates the basic functions needed for the LCD to display characters. Functions to initialize the LCD, write to the LCD and generate custom characters are defined in this script. This script is similar to most LCD control scripts except it is built for two controllers (LCD = 0 or LCD = 1).
LCD_BIG_FONT_AI6XG.py contains the function that build the custom characters that are used to display the large numbers and the function that writes the large numbers and 'Z'.
generate_big() generates the custom characters used to build the large numbers, these are stored in locations 0 - 7 of the custom character RAM in each controller. Each custom character is 8 lines of 5 pixels, char_map[ ] contains the pixel map for each custom character. This function has to be run prior to the use of the big_write(big, left) function.
big_write(big, left) writes a large number using the custom characters generated by generate_big(). It uses char_gen[ ] to build the large number (big) starting at position 'left'. In char_gen[ ] 0x00 through 0x07 are custom characters in the LCD ROM, 0x20 is a blank and 0xff is a full block. Each large number 0 - 9 and 'Z' are three columns by four lines. Each line corresponds to a line on the LCD and each column is a character so each large number is 3 characters wide by 4 lines on the LCD.
return_time_date_AI6XG.py returns the date and time in BCD format.
return_time(UTC) returns hours and minutes in BCD format, UTC = 1 for UTC time and UTC = 0 for local time.
return_date(UTC) returns month and day in BCD format, UTC = 1 for UTC date and UTC = 0 for local date.
I wrote an auto run script so that 4x40_big_font_clock_AI6XG.py runs when the Rpi boots up.
The LCD and the Rpi zero W are interfaced with a hand built protoboard that plugs into the back of the LCD with the Rpi zero W plugged into the protoboard. The interface board has a header to connect to the mode setting toggle switches that are mounted on the case top. I also added a header for +5 volts and ground for the LCD and Rpi zero W. Later I added a micro-USB socket for power input.
Connections for the Interface Board
Top and Bottom Views of the Interface Boards
When building the boards I connected all eight LCD data pins but only DB4 through DB7 are needed. I did this for future experiments on 8 bit versus 4 bit data loads. There is nothing critical to which Rpi GPIOs are used, I chose for the most convenient interface board wiring.
Enclosure and Assembly
The enclosure is assembled from laser cut 1/8" acrylic panels that were glued together using a solvent weld. I have included the .svg file for the enclosure in the GitHub repository for your use. You can also design your own enclosure using Maker Case or equivalent tool.
I added a tab to each side panel and corresponding slots to the case top so it could be friction fit rather than cemented together. I did this so I could have access to inside the enclosure for LCD mounting and toggle switch assembly.
There are also two screw slots on the back panel for hanging the clock on a wall and slots for air flow. The side panels and bottom panel have a square hole for the power cord, the two small squares above the larger square are for a zip tie to anchor the power cord.
The LCD is mounted to the front panel using 6-32 hardware. Depending on the LCD, you may want to have space between the LCD board and front panel so that the LCD bezel lines up with the front of the panel. I used 6-32 nuts as spacers.
After mounting the LCD to the front panel, plug in the interface board and the Rpi zero W. Mount the toggle switches and connect to the header on the interface board. Snake the USB power supply cord through one of the squares, plug into the header or socket, and anchor the cord to the case. The clock does not take too much current, I use a 650 mA supply with no issue
Plug in the power supply and the Clock Digits should appear after the Rpi zero W boots up. If you don't see anything on the LCD adjust the contrast control until digits appear. The time and date will take about a minute to update to the current readings.
Enclosure Curing After Solvent Welding, The Clamp is on an Air Flow Cutout Used as a Support for the Top Panel
Large Font Clock Parts Ready for Assembly
Top View of Partially Assembled Clock Showing Spacer Between LCD and Front Panel
Back View of the Assembled Clock
The LCD I am using has no back light. I have designed a PWM dimmer for a back lit LCD and implemented it and the interface between the Rpi W and LCD on a PCB. The schematic and layout is on the github page , the code and connections for the Rpi W remains the same. Discussion of the dimmer and more set up information have been posted here. Check it out!
Enjoy your clock!