User avatar
bleep42
Posts: 134
Joined: Wed Mar 07, 2012 12:43 pm
Location: Sussex
Contact: Website

Simple Word Clock

Mon Jul 16, 2018 5:23 pm

IMG_1713.jpg
Finished Word Clock.
IMG_1713.jpg (59.27 KiB) Viewed 117 times


I recently found I had an old dusty PiB+ (1CPU 500MB) laying around not doing anything useful, so I decided to build a Word Clock, there seem to be loads of different ones around, but I wanted it to be reasonably small, neat and good looking.
Unfortunately I don't have access to nice laser cutter and such like, so I decided to buy a kit, with the intention of then customising it.
The one I found which I liked the look of and wasn't ridiculously expensive, was the Cyntech Word Clock https://shop.cyntech.co.uk/collections/wordclock based on the WS2812 panel 8x8 RGB led matrix.
Their instructions worked very well, I built the case very quickly, installed a standard Stretch image on a flash card, then followed their set up procedure, everything worked very nicely.
Video of original. https://www.dropbox.com/s/vul5frhanabvl ... l.mp4?dl=0
Once fired up the clock worked very well, but I didn't really like the lettering layout, so after doing a lot of searches and finding that this layout was basically universal for an 8x8 matrix, I set about relaying it to something I preferred, this took quite a while! I'm not much good at crosswords.
Doc file of new layout. https://www.dropbox.com/s/4rpv4xat7qgfe ... k.doc?dl=0
Luckily the Cyntech kit also comes supplied with two straight 8x8 grid front plates, one with round holes the other with square, as well as the word clock front, so I word processed up my layout, so that it exactly lined up with the grid, this worked nicely. I was then able to re-write the Python code for my new layout, I included a ticking cursor, the potential to have a PM indication, and some Easter eggs, which appear about every few hours for about 10 seconds, along with a test flag to step through all combinations reasonably fast.
Video of new layout. https://www.dropbox.com/s/dtn16p6w239z8 ... t.mp4?dl=0
I then though it would be even better if I could get a proper laser cut front plate, so I asked Jason at Cyntech if he would be willing to make one up for me to buy, which he very kindly did, so here it is the finished item, using an old PiB+ under clocked to 500MHz to save (a very small amount of) heat/power, a WiFi dongle to get the time and an old phone charger. I'll probably try adding some extra Easter Eggs over time, just because I can. :-) I suppose anyone not having an old Pi, could easily use a Pi Zero, or newer.
Video of finished Word Clock, with redesigned, laser cut, face. https://www.dropbox.com/s/3f6uvkc3yc7nn ... d.mp4?dl=0

Code: Select all

from neopixel import *
from time import sleep
from datetime import datetime
import math
import struct
from random import randint

panel = Adafruit_NeoPixel(64, 18, 800000, 5, False, 50)

panel.begin()

test = 0

r = 0
g = 60
b = 200
rinc = 1
ginc = 1
binc = 1
min = 0
hour = 0
tick = 1


def mfive(colour):
  panel.setPixelColor(12,colour)
  panel.setPixelColor(13,colour)
  panel.setPixelColor(14,colour)
  panel.setPixelColor(15,colour)

def mten(colour):
  panel.setPixelColor(7,colour)
  panel.setPixelColor(15,colour)
  panel.setPixelColor(23,colour)

def quarter(colour):
  panel.setPixelColor(8,colour)
  panel.setPixelColor(16,colour)
  panel.setPixelColor(17,colour)
  panel.setPixelColor(18,colour)
  panel.setPixelColor(19,colour)
  panel.setPixelColor(20,colour)
  panel.setPixelColor(21,colour)
  panel.setPixelColor(22,colour)

def twenty(colour):
  panel.setPixelColor(1,colour)
  panel.setPixelColor(2,colour)
  panel.setPixelColor(3,colour)
  panel.setPixelColor(4,colour)
  panel.setPixelColor(5,colour)
  panel.setPixelColor(6,colour)

def half(colour):
  panel.setPixelColor(9,colour)
  panel.setPixelColor(10,colour)
  panel.setPixelColor(11,colour)
  panel.setPixelColor(12,colour)

def past(colour):
  panel.setPixelColor(25,colour)
  panel.setPixelColor(26,colour)
  panel.setPixelColor(27,colour)
  panel.setPixelColor(28,colour)

def to(colour):
  panel.setPixelColor(28,colour)
  panel.setPixelColor(29,colour)



def one(colour):
  panel.setPixelColor(61,colour)
  panel.setPixelColor(62,colour)
  panel.setPixelColor(63,colour)

def two(colour):
  panel.setPixelColor(32,colour)
  panel.setPixelColor(40,colour)
  panel.setPixelColor(48,colour)

def three(colour):
  panel.setPixelColor(56,colour)
  panel.setPixelColor(57,colour)
  panel.setPixelColor(58,colour)
  panel.setPixelColor(59,colour)
  panel.setPixelColor(60,colour)

def four(colour):
  panel.setPixelColor(35,colour)
  panel.setPixelColor(36,colour)
  panel.setPixelColor(37,colour)
  panel.setPixelColor(38,colour)

def five(colour):
  panel.setPixelColor(35,colour)
  panel.setPixelColor(43,colour)
  panel.setPixelColor(51,colour)
  panel.setPixelColor(59,colour)

def six(colour):
  panel.setPixelColor(39,colour)
  panel.setPixelColor(47,colour)
  panel.setPixelColor(55,colour)

def seven(colour):
  panel.setPixelColor(41,colour)
  panel.setPixelColor(42,colour)
  panel.setPixelColor(51,colour)
  panel.setPixelColor(52,colour)
  panel.setPixelColor(53,colour)

def eight(colour):
  panel.setPixelColor(42,colour)
  panel.setPixelColor(43,colour)
  panel.setPixelColor(44,colour)
  panel.setPixelColor(45,colour)
  panel.setPixelColor(46,colour)

def nine(colour):
  panel.setPixelColor(53,colour)
  panel.setPixelColor(54,colour)
  panel.setPixelColor(62,colour)
  panel.setPixelColor(63,colour)

def ten(colour):
  panel.setPixelColor(32,colour)
  panel.setPixelColor(33,colour)
  panel.setPixelColor(34,colour)

def eleven(colour):
  panel.setPixelColor(49,colour)
  panel.setPixelColor(50,colour)
  panel.setPixelColor(59,colour)
  panel.setPixelColor(51,colour)
  panel.setPixelColor(52,colour)
  panel.setPixelColor(53,colour)

def twelve(colour):
  panel.setPixelColor(32,colour)
  panel.setPixelColor(40,colour)
  panel.setPixelColor(49,colour)
  panel.setPixelColor(50,colour)
  panel.setPixelColor(51,colour)
  panel.setPixelColor(52,colour)

def update():
  panel.show()

def clear():
  for i in range(0,64):
    panel.setPixelColor(i,Color(0,0,0))


def display_time(rate):

  global tick, hour, min, r, g, b, rinc, ginc, binc, test

  if test:
# Cycle hours and five minute intervals
    if tick == 0:
      hour += 1
      min += 5

    if hour > 12:
      hour = 1

    if min >= 60:
      min = 5
  else:
# Display real time
    time = datetime.now().time()
    hour,min,sec = str(time).split(":")
    hour = int(hour)
    min = int(min)


  if r >= 250:
    rinc = 0
  if r <= 5:
    rinc = 1
  if rinc == 1:
    r += randint(0,5)
  else:
    r -= randint(0,5)

  if g >= 250:
    ginc = 0
  if g <= 5:
    ginc = 1
  if ginc == 1:
    g += randint(0,5)
  else:
    g -= randint(0,5)

  if b >= 250:
    binc = 0
  if b <= 5:
    binc = 1
  if binc == 1:
    b += randint(0,5)
  else:
    b -= randint(0,5)

  clear()

  col = Color(g,r,b)

  if 3 <= min <= 7:
    mfive(col)
    past(col)

  if 8 <= min <= 12:
    mten(col)
    past(col)

  if 13 <= min <= 17:
    quarter(col)
    past(col)

  if 18 <= min <= 22:
    twenty(col)
    past(col)

  if 23 <= min <= 27:
    twenty(col)
    mfive(col)
    past(col)

  if 28 <= min <= 32:
    half(col)
    past(col)

  if 33 <= min <= 37:
    twenty(col)
    mfive(col)
    to(col)

  if 38 <= min <= 42:
    twenty(col)
    to(col)

  if 43 <= min <= 47:
    quarter(col)
    to(col)

  if 48 <= min <= 52:
    mten(col)
    to(col)

  if 53 <= min <= 57:
    mfive(col)
    to(col)

  if test == 0:
    if min > 32:
      hour = hour + 1



  if hour == 1 or hour == 13:
    one(col)
  if hour == 2 or hour == 14:
    two(col)
  if hour == 3 or hour == 15:
    three(col)
  if hour == 4 or hour == 16:
    four(col)
  if hour == 5 or hour == 17:
    five(col)
  if hour == 6 or hour == 18:
    six(col)
  if hour == 7 or hour == 19:
    seven(col)
  if hour == 8 or hour == 20:
    eight(col)
  if hour == 9 or hour == 21:
    nine(col)
  if hour == 10 or hour == 22:
    ten(col)
  if hour == 11 or hour == 23:
    eleven(col)
  if hour == 12 or hour == 0:
    twelve(col)

  tick += 1

  if tick == 1:
    panel.setPixelColor(0,col)
  elif tick == 2:
    panel.setPixelColor(24,col)
  elif tick == 3:
    panel.setPixelColor(31,col)
  else:
    panel.setPixelColor(24,col)
    tick = 0

# AM/PM indication
# if hour >= 12:
#   panel.setPixelColor(30,Color(0,200,0))
# else:
#   panel.setPixelColor(30,Color(0,0,0))

  update()
  sleep(rate)


def next_colour(pix):
  r = pix[0]
  g = pix[1]
  b = pix[2]

# New rainbow colour calculation, in steps of 10
  if r > 245:
    if (g <= 245 and b < 10):
      g += 10
    if (b >= 10 and g < 10):
      b -= 10

  if g > 245:
    if (r >= 10 and b < 10):
      r -= 10
    if (b <= 245 and r < 10):
      b += 10

  if b > 245:
    if (g >= 10 and r < 10):
      g -= 10
    if (r <= 245 and g < 10):
      r += 10

  pix[0] = r
  pix[1] = g
  pix[2] = b


def rainbow_diagonal():
# Initialise the rainbow colours borrowed from Sense HAT examples
  pixels = [
      [255, 0, 0], [255, 0, 0], [255, 87, 0], [255, 196, 0], [205, 255, 0], [95, 255, 0], [0, 255, 13], [0, 255, 122],
      [255, 0, 0], [255, 96, 0], [255, 205, 0], [196, 255, 0], [87, 255, 0], [0, 255, 22], [0, 255, 131], [0, 255, 240],
      [255, 105, 0], [255, 214, 0], [187, 255, 0], [78, 255, 0], [0, 255, 30], [0, 255, 140], [0, 255, 248], [0, 152, 255],
      [255, 223, 0], [178, 255, 0], [70, 255, 0], [0, 255, 40], [0, 255, 148], [0, 253, 255], [0, 144, 255], [0, 34, 255],
      [170, 255, 0], [61, 255, 0], [0, 255, 48], [0, 255, 157], [0, 243, 255], [0, 134, 255], [0, 26, 255], [83, 0, 255],
      [52, 255, 0], [0, 255, 57], [0, 255, 166], [0, 235, 255], [0, 126, 255], [0, 17, 255], [92, 0, 255], [201, 0, 255],
      [0, 255, 66], [0, 255, 174], [0, 226, 255], [0, 117, 255], [0, 8, 255], [100, 0, 255], [210, 0, 255], [255, 0, 192],
      [0, 255, 183], [0, 217, 255], [0, 109, 255], [0, 0, 255], [110, 0, 255], [218, 0, 255], [255, 0, 183], [255, 0, 74]
  ]
# Repeat 1200 times, approx 20sec on Pi1 adjust as necessary
  for i in range(1200):
# Output the array to the LEDs
    x = 0
    for pix in pixels:
      panel.setPixelColor(x,Color(pix[1],pix[0],pix[2]))
      x += 1
# Calculate the new LED colour
      next_colour(pix)

    update()
    sleep(0.008)


def rainbow_effect(pos):
# Generate rainbow colors across 0-255 colour range.
    if pos >= 170:
        pos -= 170
        return Color(0, pos * 3, 255 - pos * 3)
    if pos >= 85:
        pos -= 85
        return Color(255 - pos * 3, 0, pos * 3)
    else:
        return Color(pos * 3, 255 - pos * 3, 0)


def rainbow_up(iter=5):
#  Draw upward moving rainbow spread across all pixels, scaled to 0-255.
    for j in range(256*iter):
        for i in range(64):
            panel.setPixelColor(i, rainbow_effect((int(i * 4) + j) & 255))
        panel.show()
        sleep(0.005)


def matrix_code():
# Draw 'Matrix' falling code display
# repeat 90 times. approx 20 sec.
  for i in range(90):
# Insert a new random 'code'
    column = randint(0,7)
    panel.setPixelColor(column,Color(255,50,40))
    update()

# Move the 'code' down one line
    for x in range(55,-1,-1):
      panel.setPixelColor(x+8,panel.getPixelColor(x))

# Calculate the new colour for top row.
    for column in range(8):
# extract the green colour only and fade it as 'code' drops
      green_pix = (panel.getPixelColor(column) >> 16) & 0xff
      if green_pix > 200:
         green_pix -= 157
      elif green_pix >= 13:
         green_pix -= 13

      panel.setPixelColor(column,Color(green_pix,0,0))

    sleep(0.25)


def fast_time():

  for i in range(1000):
    display_time(0.01)


def zoom():

  for y in range(80):

    for x in range(8):
      panel.setPixelColor(x,Color(255,0,0))
      panel.setPixelColor(x+56,Color(255,0,0))

    for x in range(0,56,8):
      panel.setPixelColor(x,Color(255,0,0))
      panel.setPixelColor(x+7,Color(255,0,0))

    update()
    sleep(0.05)
    clear()

    for x in range(9,14):
      panel.setPixelColor(x,Color(100,0,0))
      panel.setPixelColor(x+40,Color(100,0,0))

    for x in range(9,50,8):
      panel.setPixelColor(x,Color(100,0,0))
      panel.setPixelColor(x+5,Color(100,0,0))

    update()
    sleep(0.05)
    clear()

    for x in range(18,21):
      panel.setPixelColor(x,Color(50,0,0))
      panel.setPixelColor(x+24,Color(50,0,0))

    for x in range(18,43,8):
      panel.setPixelColor(x,Color(50,0,0))
      panel.setPixelColor(x+3,Color(50,0,0))

    update()
    sleep(0.05)
    clear()

    panel.setPixelColor(27,Color(25,0,0))
    panel.setPixelColor(28,Color(25,0,0))

    panel.setPixelColor(35,Color(25,0,0))
    panel.setPixelColor(36,Color(25,0,0))

    update()
    sleep(0.05)
    clear()


def rainbow_theater_chase():
#  Rainbow theater light style chaser animation.
    for j in range(100):
      for q in range(4):
        for i in range(0, 64, 4):
          panel.setPixelColor(i+q, rainbow_effect(((i*4)+(j*4)) % 255))
        panel.show()
        sleep(0.05)
        for i in range(0, 64, 4):
          panel.setPixelColor(i+q, 0)


def colour_run(colour):
#  Run a color across display and downwards a pixel at a time.
    for i in range(64):
      panel.setPixelColor(i, colour)
      panel.show()
      sleep(0.02)


def colour_swipe(colour):
#  Swipe colour accross the screen
    for i in range(8):
      for x in range(i,(i+57),8):
        panel.setPixelColor(x,colour)
      panel.show()
      sleep(0.2)


easter = 0
if test:
  mult = 1
else:
  mult = 1000

while True:
  easter += 1

  if easter == (9*mult):
    matrix_code()
  elif easter == (18*mult):
    rainbow_diagonal()
  elif easter == (27*mult):
    fast_time()
  elif easter == (36*mult):
    colour_run(Color(20, 20, randint(20,150)))
    colour_run(Color(randint(20,255), 20, 20))
    colour_run(Color(20, randint(20,255), 20))
    colour_run(Color(randint(20,255), randint(20,255), randint(20,150)))
    colour_run(Color(20, 20, randint(20,150)))
    colour_run(Color(randint(20,255), 20, 20))
    colour_run(Color(20, randint(20,255), 20))
    colour_run(Color(randint(20,255), randint(20,255), randint(20,150)))
    colour_run(Color(20, 20, randint(20,150)))
    colour_run(Color(randint(20,255), 20, 20))
    colour_run(Color(20, randint(20,255), 20))
    colour_run(Color(randint(20,255), randint(20,255), randint(20,150)))
    colour_run(Color(20, 20, randint(20,150)))
    colour_run(Color(randint(20,255), 20, 20))
    colour_run(Color(20, randint(20,255), 20))
    colour_run(Color(randint(20,255), randint(20,255), randint(20,150)))
  elif easter == (45*mult):
    rainbow_theater_chase()
  elif easter == (54*mult):
    colour_swipe(Color(255, 0, 0))  # Green wipe
    colour_swipe(Color(0, 255, 0))  # Red wipe
    colour_swipe(Color(255, 0, 200))  # Cyan wipe
    colour_swipe(Color(0, 255, 225))  # Magenta wipe
    colour_swipe(Color(255, 255, 0))  # Yellow wipe
    colour_swipe(Color(0, 0, 255))  # Blue wipe
    colour_swipe(Color(255, 0, 0))  # Green wipe
    colour_swipe(Color(0, 255, 0))  # Red wipe
    colour_swipe(Color(255, 0, 200))  # Cyan wipe
    colour_swipe(Color(0, 255, 225))  # Magenta wipe
    colour_swipe(Color(255, 255, 0))  # Yellow wipe
    colour_swipe(Color(0, 0, 255))  # Blue wipe
  elif easter == (63*mult):
    rainbow_up()
  elif easter >= (72*mult):
    zoom()
    easter = 0

  display_time(0.5)
Link to Python file to run clock.https://www.dropbox.com/s/ru61ilsu563pu ... ck.py?dl=0
I'm not a Python coder, so this is far from optimal. ;-)

I'm much happy ;-) and I for one, think it looks lovely.
Presumably, anyone else liking this layout could also get one by asking Cyntech nicely. ;-)

And when you get bored with it being a word clock, you can always re-purpose it using the square or round hole matrix to do something else. :-)

PS. All the video files seem to be on their side from DropBox, on my machine they are fine. I'll try to sort it, Videos now corrected.
Code updated with extra Easter eggs.

Return to “Other projects”

Who is online

Users browsing this forum: No registered users and 10 guests