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:
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: https://tutorials-raspberrypi.de/raspberry-pi-ws2812-ws2811b-rgb-led-streifen-steuern/
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 LAST_MINUTE_ENTRY = 0; #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 = datetime.datetime.now() min = LAST_MINUTE_ENTRY hour = now.hour array =  if hour == 4 and min == 20 or hour == 16 and min == 20: #embrace 420 rainbow(strip) # 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) else: strip.setPixelColor(i, Color(0,0,0)) strip.show() # 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) strip.show() time.sleep(wait_ms/1000.0) # 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): global LAST_MINUTE_ENTRY now = datetime.datetime.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) strip.show() time.sleep(wait_ms/1000.0) 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)) strip.show() time.sleep(wait_ms/1000.0) 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) else: 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. strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL) # Intialize the library (must be called once before other functions). strip.begin() print ('Press Ctrl-C to quit.') if not args.clear: print('Use "-c" argument to clear LEDs on exit') timeHasChanged(True) array = convertTimeToArray(strip) showArray(strip, Color(50,50,50), array) try: while True: if timeHasChanged(False) == True: array = convertTimeToArray(strip) showArray(strip, Color(50,50,50), array) else: time.sleep(30) 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 https://tutorials-raspberrypi.de/raspberry-pi-ws2812-ws2811b-rgb-led-streifen-steuern/, 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:
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).