Building a Word-Clock (QLOCKTWO Clone)

After recently remodeling my apartment and with some time off over the Christmas holidays I felt like I should add something special to my living space. During this time I saw a post on Reddit of someone who had built a clone of the famous QLOCKTWO LED-clock, which inspired me to follow suit.

Since I had stopped playing around with electronics a long time ago and wasn’t in the mood to go all out and build an LED-matrix myself, I decided to do things differently and use individually addressable RGB-LED-strips. This way I avoided having to either order custom PCBs or having to build the circuits on breadboards since all that was needed on the electronics-side were a raspberry pi, the strips, a power supply and an appropriate connector as well as some wires.

If you want to build a similar clock, you’ll mainly need these parts (plus some tools like a soldering iron, wire strippers, solder, wires, and basic electronics stuff):



You’ll also need a deep picture frame (with at least 3cm space on the inside), preferably a 50×50 Ribba frame from Ikea, as well as a strong 5v power supply (I recommend using one with at least 7 amps rating, in case you want to run all of the led’s at full power, for example to use the clock as a moody RGB light). You should also get the appropriate female connector for your power supply. Sadly, neither of those products was available on Amazon, so you should look up where you can buy those in your area.

Once I had all the parts I needed, it was time to mount the led strip:

Raw electronics of the clock

This is where some wire (pay attention to the diameter of the wire, it should be capable of at least 5-6 amps!) is needed to jump the power and signal from each strip to the next. Here I paid attention to the distance between the LEDs, which is about 3,3 cm, so they formed a nice square. Also, the overall matrix should be centered nicely in the frame, so you should measure this out thoroughly.

In the next step, I drilled a hole for the female connector for my 5v power supply and connected the led strip to it. I also added a 7amp fuse, just to be safe.
I then mounted the raspberry pi to the wood and connected it to power using a micro USB cable that I soldered to the 5v power. The signal wire of the LED strip is connected to the raspberry pi’s pin GPIO 18. Since both the strip and the pi are connected to the same power supply, we don’t need to connect ground to the pi directly.

After installing the hardware, I built a grid out of some cardboard, so each LED was shielded from the next. This way an active LED will not light up letters other than the one it’s supposed to. I then went ahead and cut out every letter out of the adhesive foil by hand. If you plan to build this clock, it’s probably much easier and better to just find a copyshop with a plotter that will cut out the letters for you, or find a CNC shop that will make you a beautiful front plate.

Once I had cut out all of the letters, I glued the foil onto the glass of my frame. I also added a thin white plastic film on the inside of the clock, so all of the leds would look diffuse and you can’t see inside the clock through the letters. A cheap thin white plastic bag from the grocery store will be perfect for this.

I then followed this tutorial to test out the LEDs:
The tutorial is written in German but you should be able to follow the command-line instructions anyways. All it does is install python-dev and disable the audio output of the pi, otherwise, the pi won’t be able to drive the strip (you can use strips that require less precise timing and thus can leave the Pi’s audio enabled, but those strips will be more expensive).

I then went ahead and modified the code of the article linked above to create a clock. This code is a complete hack, but by this time it was late in the evening and I just wanted to be finished with the clock. I could have built a much neater logic to decide which pixels to enable and thus minimized the lines of code, but who doesn’t like a ton of if-statements?! Maybe I’ll clean it up in the future and update this post, but since this code works just fine, I’ll probably leave it like it is. If you decide to build this clock and write some neater code, please send me your’s and I’ll link it here.

#!/usr/bin/env python3

import time
import datetime
from neopixel import *
import argparse

# LED strip configuration:
LED_COUNT      = 110      # Number of LED pixels.
LED_PIN        = 18      # GPIO pin connected to the pixels (18 uses PWM!).
#LED_PIN        = 10      # GPIO pin connected to the pixels (10 uses SPI /dev/spidev0.0).
LED_FREQ_HZ    = 800000  # LED signal frequency in hertz (usually 800khz)
LED_DMA        = 10      # DMA channel to use for generating signal (try 10)
LED_BRIGHTNESS = 144     # Set to 0 for darkest and 255 for brightest
LED_INVERT     = False   # True to invert the signal (when using NPN transistor level shift)
LED_CHANNEL    = 0       # set to '1' for GPIOs 13, 19, 41, 45 or 53


#All words on the clock
ES = [10, 11]
IST = [31, 50, 51]
FUENF = [71, 90, 91, 110]
ZEHN = [9, 12, 29, 32]
VIERTEL = [48, 53, 68, 73, 88, 93, 108]
ZWANZIG = [49, 52, 69, 72, 89, 92, 109]
DREIVIERTEL = [8, 13, 28, 33, 48, 53, 68, 73, 88, 93, 108]
VOR = [7, 14, 27]
NACH = [74, 87, 94, 107]
HALB = [6, 15,  26, 35]
H_ELF = [55, 66, 75]
H_FUENF = [75, 86, 95, 106]
H_EINS = [5, 16, 25, 36]
H_EIN = [5, 16, 25]
H_ZWEI = [76, 85, 96, 105]
H_DREI = [4, 17, 24, 37]
H_VIER = [77, 84, 97, 104]
H_SECHS = [3, 18, 23, 38, 43]
H_ACHT = [78, 83, 98, 103]
H_SIEBEN = [2, 19, 22, 39, 42, 59]
H_ZWOELF = [62, 79, 82, 99, 102]
H_ZEHN = [ 1, 20, 21, 40]
H_NEUN = [40, 41, 60, 61]
UHR = [81, 100, 101]

def convertTimeToArray(strip):
    now =
    hour = now.hour
    array = []
    if hour == 4 and min == 20 or hour == 16 and min == 20: #embrace 420
   # if hour < 8: #turn clock off from 0 to 8 in the morning
     #   return array;
    if min == 0: #new hour, let's have fun
        theaterChase(strip, Color(127,127,127), 50, 30)
    array = array + ES + IST + getMinuteArray()
    if min == 0:
        array = array + getHourArray(hour, True) + UHR
    elif min  12:
                hour = hour - 12
        if hour == 0:
                return H_ZWOELF
        if hour == 1:
                if fullTime:
                        return H_EIN
                return H_EINS
        if hour == 2:
                return H_ZWEI
        if hour == 3:
                return H_DREI
        if hour == 4:
                return H_VIER
        if hour == 5:
                return H_FUENF
        if hour == 6:
                return H_SECHS
        if hour == 7:
                return H_SIEBEN
        if hour == 8:
                return H_ACHT
        if hour == 9:
                return H_NEUN
        if hour == 10:
                return H_ZEHN
        if hour == 11:
                return H_ELF
        if hour == 12:
                return H_ZWOELF
        return []

#get minutes
def getMinuteArray():
        min = LAST_MINUTE_ENTRY
        if min == 0:
                return []
        if min == 5:
                return FUENF + NACH
        if min == 10:
                return ZEHN + NACH
        if min == 15:
                return VIERTEL + NACH
        if min == 20:
                return ZWANZIG + NACH
        if min == 25:
                return FUENF + VOR + HALB
        if min == 30:
                return HALB
        if min == 35:
                return FUENF + NACH + HALB
        if min == 40:
                return ZEHN + NACH + HALB
        if min == 45:
                return DREIVIERTEL
        if min == 50:
                return ZEHN + VOR
        if min == 55:
                return FUENF + VOR
        return []

#Show array
def showArray(strip, color, array):
        for i in range(strip.numPixels()):
                if array.__contains__(i + 1):
                        strip.setPixelColor(i, color)
                        strip.setPixelColor(i, Color(0,0,0))

# Define functions which animate LEDs in various ways.
def colorWipe(strip, color, wait_ms=50):
    """Wipe color across display a pixel at a time."""
    for i in range(strip.numPixels()):
        strip.setPixelColor(i, color)

# Returns true every time a new 5 minutes have started on the clock
# i.E it's now 00:35 or 00:10
def timeHasChanged(isStartup):
    now =
    if isStartup == True:
        LAST_MINUTE_ENTRY = round(now.minute / 5) * 5 #round to nearest five
        return True
    if LAST_MINUTE_ENTRY != now.minute:
        if now.minute % 5 == 0:
            LAST_MINUTE_ENTRY = now.minute
            return True
    return False

def theaterChase(strip, color, wait_ms=50, iterations=10):
    """Movie theater light style chaser animation."""
    for j in range(iterations):
        for q in range(3):
            for i in range(0, strip.numPixels(), 3):
                strip.setPixelColor(i+q, color)
            for i in range(0, strip.numPixels(), 3):
                strip.setPixelColor(i+q, 0)

def rainbow(strip, wait_ms=20, iterations=1):
    """Draw rainbow that fades across all pixels at once."""
    for j in range(256*iterations):
        for i in range(strip.numPixels()):
            strip.setPixelColor(i, wheel((i+j) & 255))

def wheel(pos):
    """Generate rainbow colors across 0-255 positions."""
    if pos < 85:
        return Color(pos * 3, 255 - pos * 3, 0)
    elif pos < 170:
        pos -= 85
        return Color(255 - pos * 3, 0, pos * 3)
        pos -= 170
        return Color(0, pos * 3, 255 - pos * 3)

# Main program logic follows:
if __name__ == '__main__':
    # Process arguments
    parser = argparse.ArgumentParser()
    parser.add_argument('-c', '--clear', action='store_true', help='clear the display on exit')
    args = parser.parse_args()

    # Create NeoPixel object with appropriate configuration.
    # Intialize the library (must be called once before other functions).

    print ('Press Ctrl-C to quit.')
    if not args.clear:
        print('Use "-c" argument to clear LEDs on exit')

    array = convertTimeToArray(strip)

    showArray(strip, Color(50,50,50), array)

       while True:
            if timeHasChanged(False) == True:
                 array = convertTimeToArray(strip)
                 showArray(strip, Color(50,50,50), array)

    except KeyboardInterrupt:
        if args.clear:
             colorWipe(strip, Color(0,0,0), 10)

This code checks every thirty seconds wether new five minutes have started, since the clock can only show time down to five minutes. (I.e ten minutes past after nine, fifteen minutes past nine and so on). If it finds out that the time has changed, it'll create an array of words. The possible words are defined at the top of the code and are arrays of led-indexes. For example, if led 0,1,2 of our strip make up the word "one", then the array for the word one will be [0,1,2]. Depending on the time, these words are combined to a complete array that contains all the indexes of the LEDs that are needed to show the current time. This array is then passed to showArray(), which turns on the individual LEDs in the color we specified.

I also included a little animation that plays when a new hour starts and one that plays whenever the time is 4:20, just because I could ;-).

I then created a service with this script and "systemctl enable"d it, so it will automatically start when the Pi boots up. I also added another service that can run the original script from this site, which I found to be quite beautiful. This one I didn't enable since I only want the LEDClock service to run on boot.

Since the pi is connected to my wifi (which I recommend so it can just connect to a time server for accurate time), I built a really basic PHP site that I can always access in my network by going to http://ledclock.home (you can change this address to whatever you desire by using avahi-daemon). This website has two buttons, one to start the clock service and another to start the RGB lighting service:

<!doctype html>
<meta charset="utf-8">
<title>Unbenanntes Dokument</title>
	$startLEDClock = escapeshellcmd('sudo systemctl start ledclock.service');
	$stopLEDClock = escapeshellcmd('sudo systemctl stop ledclock.service');
	$startBlinkenlights = escapeshellcmd('sudo systemctl start blinkenlights.service');
	$stopBlinkenlights = escapeshellcmd('sudo systemctl stop blinkenlights.service');
	if(isset($_POST["blinkenlights"])) {
    		echo 'Blinkelights has started!';
	if(isset($_POST["LEDClock"])) {

    		echo 'LEDClock has started!';

<form action="" method="post">
<input type="submit" name="LEDClock" value="Start LEDClock"/>
<form action="" method="post">
<input type="submit" name="blinkenlights" value="Start Blinkenlights"/>

For this to work, I had to install apache and php7 on the pi. Then I had to give permissions to PHP to execute the systemctl commands as sudo. This way I can easily switch between moody RGB lighting and the clock mode by simply going to the website of the pi.

Overall I’m extremely happy with the clock I built. If I was to do this again and had a bigger budget, I’d definitely use a better front plate. I suppose one could order a plate where the letters are CNC cut, which would look a lot better (plus you don’t have to worry about bubbles in the foil).

whatsapp image 2019-01-25 at 21.44.42

8 thoughts on “Building a Word-Clock (QLOCKTWO Clone)

    1. Ja ich hatte leider etwas Probleme, die LEDs von einander abzuschirmen. Hatte das Gitter aus Pappe einfach nicht perfekt genug gebaut. Hat sich aber bei den meisten Buchstaben beheben lassen in dem ich einfach etwas Pappe hinzugefügt habe. Deine Methode mit Karton sieht da schon um einiges praktikabler aus!


  1. line 61
    elif min 12:
    SyntaxError: invalid syntax

    Hallo Ben,

    ich habs spontan mal ausprobiert, gibts evtl eine Korrektur oder mache ich etwas falsch?

    Vielen Dank!


    1. Hallo 🙂

      Nein, du machst nichts falsch. Der code wird hier leider seit geraumer Zeit nicht richtig dargestellt, scheint an wordpress zu legen.
      Hier findest du den richtigen code samt libraries (wenn du das komplette repository lädst):

      Liebe Grüße!

      For all english speaking readers: Unfortunately the code shown above is being mangled by wordpress and incorrectly displayed.
      To get the correct code, head on over to github:


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s