** This post is a work in progress! Last update 9/22/24

ClockUI Demo

Introduction

While scrolling through Amazon I stumbled upon a rather interesting piece of tech: a 5” round LCD display. While circular displays are quite common on smart watches, I’ve never seen one larger than an inch or two and decided to pick it up. A 5” round display provides a unique platform for a variety of projects, and I wanted to take advantage of the form factor by creating a Raspberry Pi based smart clock. At the heart of this smart clock lies ClockUI, a PyQt5 based application that features a collection of customizable clock faces. Check it out on GitHub here!

My intention with this project was to gain a bit of experience with UI design and the Qt Quick framework, and admittedly ClockUI in its current form is still quite basic. ClockUI features 4 different clock faces, and a basic settings page for selecting a global color theme. Perhaps calling ClockUI a “smart clock” is a bit of an exaggeration at its current stage; the extent of the smart functionality is live temperature data with the current temperature and daily high/low. However, I hope to add additional features in the future!

Background

I did some research on various UI frameworks I could utilize to make ClockUI a reality, but ultimately settled on PyQt5. While I had no prior experience with the Qt Quick framework, PyQt5’s reliance on Python to handle the application logic made it easy to get started and I found QML to be very intuitive for handing the UI side of things. This guide was a great starting point!.

Another plus with PyQt5 is that it can easily run on Linux without a windowing system thanks to the ‘eglfs’ plugin. This platform plugin allows ClockUI to be launched from the CLI and run in full screen mode without the overhead of a full Linux desktop. My goal with this project was to provide a seamless user experience where the Raspberry Pi boots straight into ClockUI (much like how an off-the-shelf embedded device would work).

Behind the scenes, getting the current date and time for the clock display was easy with python’s built-in time module. I opted not to use a real time clock module for the Pi, instead using the internet to gain accurate time info. For weather data, I used OpenWeather’s weather API. API calls are handled in a separate python script that is called from the main UI event loop. In hindsight, this is not a good way to implement the process of fetching data for the UI because if the weather script encounters an error that it can’t handle properly, the entire UI will lock up. In my other PyQt5 project, OBD2DashUI, I used a separate Python worker thread to handle API calls, which is the proper way to do this. Eventually I plan on updating ClockUI to do the same.

Tour of the UI

Clock Faces

As mentioned earlier, ClockUI features a collection of clock faces and a basic settings page. Currently, the settings page allows the user to select a global color theme. All of the available screens, in the order that they are accessible by the user, are shown below: all the available clock faces Switching between clock faces is achieved by swiping left or right on the touch screen, or clicking and dragging with a mouse cursor. The following GIF demonstrates switching between clock faces and changing the color theme: switching clock faces

Error Handling

In an effort to make the UI more user friendly, I’ve added a few error messages to ClockUI to help troubleshoot issues with the OpenWeather integration. These messages will be triggered under the following circumstances:

  1. The .env file containing the OpenWeather API key is missing
  2. The .env file is present, but the API key is missing or invalid
  3. The .env file is present, but the API url cannot be reached

The corresponding error messages are shown below: error messages

Hardware

Let’s take a closer look at the display itself. As mentioned earlier, it’s a 5” diameter LCD with 10-point multi-touch and a resolution of 1080x1080, sold by Waveshare. Here’s a link to the Amazon page, but be warned: the display is quite expensive. Unfortunately, the LCD driver doesn’t seem to have any scaling capabilities; you must send it a 1080x1080 signal, any other resolution will not work. The LCD itself uses IPS technology, so viewing angles are excellent and contrast is decent. It’s also quite bright, but this can actually be a bit of a problem because there is no way to adjust the backlight brightness. It’s a bit too bright for use at night, which is why I added the Night Mode theme to clockUI.

The back of the display has mounting points for a Raspberry Pi, and it incudes a couple small PCBs for making a direct connection to the Pi’s HDMI output and USB for touch capabilities. I ended up using a few of the extra standoffs that came with the display to put together a makeshift stand:

back of display

I used a Raspberry Pi 4 2GB for ClockUI, but I expect a Pi 3 should be able to handle it with no issues. There’s an alternate lite version that only loads one clock face at a time to lessen CPU load. I tested this version on a Pi B+ and while performance is fine within a clock face, swiping between them results in up to 10 seconds of lag before the next is loaded. Not an issue if you want to stick with a clock face, but definitly a deal breaker if you plan on switching between them frequently.

Setup

Installation

Setting up a smart clock with ClockUI is fairly simple. To start, you’ll need a Raspberry Pi (model 3 or later recommended) with Raspberry Pi OS Lite. There are many different ways to install RPi Os Lite on an SD card, the most beginner friendly way would be to use the Raspberry Pi Imager tool. With the Pi connected to a network, install git by running apt install git and clone the repository by running git clone https://github.com/danm11r/clockUI.

Then install Python and the necessary PyQt5 packages using the included install script. To run this script, make it executable with chmod +x install.sh and execute with ./install.sh.

Alternativly, you can install the packages manually by running sudo apt-get install python3 python3.pyqt5 python3-pyqt5.qtquick qml-module-qtquick-shapes qml-module-qtquick-controls2 qml-module-qtquick-window2 python3-dotenv qml-module-qt-labs-settings -y and sudo apt-get install fonts-noto -y.

You should now be able to run the app. From the clockUI directory, run the following command to launch the app using the OpenGL renderer, bypassing the need for a windowing system: python3 main.py --platform eglfs.

If you want to launch the app automatically upon startup, you can do so with crontab. First make the included start.sh script executable by running chmod +x start.sh. Then create a crontab to run the start script with sudo crontab -e. Add a new command to run at startup:

@reboot ~/clockUI/start.sh

Unless you cloned the clockUI repository to your home directory, you’ll need to customize the file path in the above command. Now clockUI should launch when the Pi boots up!

Screen Resolution

ClockUI is designed to easily scale to any display resolution. The size of the clock faces are determined by the height of the main application window, so if you launch ClockUI in full screen mode using the eglfs method it should automatically scale to fit the height of you display. If you need to change the size of the clock faces manually, you can do so by changing the clockRadius variable in the main.qml file.

OpenWeather Configuration

ClockUI expects to find a .env file that contains the OpenWeather API key, zip code, and units for temperature data. To set this up, simple copy the template file .env-example present in the repository, rename it to .env, and fill in the necessary information. The contents of .env-example are shown below:

# APU key, zip code, and weather data units for open weather API call

API_KEY = ""
ZIP_CODE = ""
UNITS = "" # either imperial or metric

If you wish to user metric units, you’ll also need to change the metric variable in the main.qml to true. You’ll find this variable under the general clock face settings, as shown:

// General clock face settings
property int clockRadius: height/2
property int animationDelay: 200
property int arcWidth: clockRadius*(1/30)
property bool metric: true

Conclusion

I had a lot of fun working on this project and I’m quite happy with how it’s turned out so far. I’ve been using ClockUI continuously for the past few months and I think the round LCD is a really neat platform for a smart clock, and I tend to stick with the analog clock face specifically because it serves as a great showcase of the form factor. I plan on returning to this project eventually with new clock faces and additional features, as time permits! Once again, here’s a link to the GitHub repository if you want to check out the project.