Word Clock

Posted on Updated on

This is a project I started a while ago and not yet completed. Just posting part of the design

  1. First Part of the Project: The front

The spaces between letters are 31.25mm (pitch of my led stripes). Font: Varsity Regular

word_clock_4

Word Clock Front

Here is a photo of the real things made from plastic sheets black and white. It is made by a local supplier (advertising display shop). Not bad.

916434330

2. Second Part of the Project: The back

Drawing of the laser cut back (layer 1)

g4168

Since I am limited by the cutting area of my laser cutter, I had to divide the area in 4 pieces.

Laser Cutting…

l_cut_2 l_cut_3 l_cut_1

Actually I need a thicker layer, the led are going to be too close to the front.

I am going to change to thick foam board. I will do the same design but CNC cut.

Foam board and CNC:

Result…

The result is OK, I still have to CNC three more pieces to complete the back.

Will finish that during the WE.

Soldering the stripes now.

Today is December 4th, I haven’t touch the thing for a while.

Here, the latest progress. All stripes are done and connected. Just need to think about the final look…

clock

Don’t know how I am going to control them yet?

Today, December 15th.

I have decided to make the clock running with an Arduino Nano + RTC DS3231.

I started with the code from Javelin Word Clock (google Javelin Word Clock) but changed it a bit!

Have a look and play with it. The code only runs the clock, there are no fanzy things yet…

#include <Adafruit_NeoPixel.h>
#include <Time.h>
#include <Wire.h>  
#include <DS1307RTC.h>        // a basic DS1307 library that returns time as a time_t
#include <avr/pgmspace.h>

#define N_LEDS 110
#define BRIGHTNESSDAY 200 // full on
#define BRIGHTNESSNIGHT 55 // half on
#define RGBLEDPIN 6

#define TIME_HEADER "T"

typedef const unsigned char prog_uchar;

Adafruit_NeoPixel grid = Adafruit_NeoPixel(N_LEDS, RGBLEDPIN, NEO_GRB + NEO_KHZ800);

int intBrightness = BRIGHTNESSDAY; // the brightness of the clock (0 = off and 255 = 100%)
 int intTestMode; // set when both buttons are held down
 String strTime = ""; // used to detect if word time has changed
 int intTimeUpdated = 0; // used to tell if we need to turn on the brightness again when words change

unsigned long previousMillis = 0;
 const long interval = 10000;

uint32_t colorWhite = grid.Color(255, 255, 255);
 uint32_t colorBlack = grid.Color(0, 0, 0);
 uint32_t colorRed = grid.Color(255, 0, 0);
 uint32_t colorGreen = grid.Color(0, 255, 0);
 uint32_t colorBlue = grid.Color(0, 0, 255);
 uint32_t colorJGreen = grid.Color(50, 179, 30);

#define arrIT {2,0,19}
#define arrIS {2,39,40}
#define arrAQUARTER {8,80,18,21,38,41,58,61,78}
#define arrHALF {4,3,16,23,36}
#define arrPAST {4,4,15,24,35}
#define arrOCLOCK {6,49,69,70,89,90,109}
#define arrTO {2,96,103}
#define arrONE {3,5,14,25}
#define arrTWO {3,34,45,54}
#define arrTHREE {5,65,74,85,94,105}
#define arrFOUR {4,6,13,26,33}
#define arrFIVE {4,46,53,66,73}
#define arrSIX {3,86,93,106}
#define arrSEVEN {5,55,64,75,84,95}
#define arrEIGHT {5,8,11,28,31,48}
#define arrNINE {4,7,12,27,32}
#define arrTEN {3,9,10,29}
#define arrELEVEN {6,51,68,71,88,91,108}
#define arrTWELVE {6,52,67,72,87,92,107}
#define arrTWENTY {6,2,17,22,37,42,57}
#define arrTWENTYFIVE {10,2,17,22,37,42,57,77,82,97,102}
#define arrMFIVE {4,77,82,97,102}
#define arrMTEN {3,56,63,76}

prog_uchar words[23][11] PROGMEM ={arrIT, arrIS, arrAQUARTER, arrMFIVE, arrMTEN, arrTWENTY, arrTWENTYFIVE, arrHALF,
 arrTO, arrPAST, arrONE, arrTWO, arrTHREE,
 arrFOUR, arrFIVE, arrSIX, arrSEVEN, arrEIGHT,
 arrNINE, arrTEN, arrELEVEN, arrTWELVE,
 arrOCLOCK};

prog_uchar m[12][2] PROGMEM ={{0,22},{5,3},{10,4},{15,2},{20,5},{25,6},{30,7},{35,6},{40,5},
 {45,2},{50,4},{55,3}};

prog_uchar h_a[13] PROGMEM ={0,10,11,12,13,14,15,16,17,18,19,20,21};
 prog_uchar h_b[13] PROGMEM ={0,11,12,13,14,15,16,17,18,19,20,21,10};

void setup() {

Serial.begin(9600);
 setSyncProvider(RTC.get); // the function to get the time from the RTC
 setSyncInterval(60); // sync the time every 60 seconds (1 minutes)
 if(timeStatus() != timeSet){
 Serial.println("Unable to sync");
 RTC.set(1481781666); // set the RTC to Thu, 15 Dec 2016 06:01:06 +00:00
 setTime(1481781666);
 }

// setup the LED strip
 grid.begin();
 grid.show();

// set the brightness of the strip
 grid.setBrightness(intBrightness);

}

void loop(){

unsigned long currentMillis = millis();

// if there is a serial connection lets see if we need to set the time
 if (Serial.available()) {
 time_t t = processSyncMessage();
 if (t != 0) {
 Serial.print("Time set via connection to: ");
 Serial.print(t);
 Serial.println();
 RTC.set(t); // set the RTC and the system time to the received value
 setTime(t);
 }
 }
 // check to see if the time has been set
 if (timeStatus() == timeSet){
 // time is set lets show the time
 if((hour() < 7) | (hour() >= 19)){
 intBrightness = BRIGHTNESSNIGHT;
 }else{
 intBrightness = BRIGHTNESSDAY;
 }
 grid.setBrightness(intBrightness);

}

if (currentMillis - previousMillis >= interval) {

previousMillis = currentMillis;

for (byte z =0; z<110; z++){
 grid.setPixelColor(z,colorBlack);
 }

pushtime();
 grid.show();

}
 }

unsigned long processSyncMessage() {
 unsigned long pctime = 0L;
 const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013

if(Serial.find(TIME_HEADER)) {
 pctime = Serial.parseInt();
 pctime = pctime + 28800; // + 8 hours - winter asia time
 return pctime;
 if( pctime < DEFAULT_TIME) { // check the value is a valid time (greater than Jan 1 2013) pctime = 0L; // return 0 to indicate that the time is not valid } Serial.println("Set"); } return pctime; } void pushtime(){ int Minute = minute(); int Hour = hourFormat12(); boolean past = true; Minute = Minute - (Minute % 5); if((minute()>34) && (minute()<= 59)) {
 past = false;
 }

Serial.print (hourFormat12());
 Serial.print (" : ");
 Serial.println (Minute);

// push IT
 for(int i = 1; i <= pgm_read_byte ( & words [0][0]); i++){
 Serial.println( pgm_read_byte ( & words [0][i]) );
 grid.setPixelColor(pgm_read_byte ( & words [0][i]),colorWhite);
 }
 // push IS
 for(int i = 1; i <= pgm_read_byte ( & words [1][0]); i++){
 Serial.println( pgm_read_byte ( & words [1][i]) );
 grid.setPixelColor(pgm_read_byte ( & words [1][i]),colorWhite);
 }
 // push minute
 byte a;
 for (byte i=0; i<12; i++) {
 if (pgm_read_byte ( & m [i][0]) == Minute) {
 a = pgm_read_byte ( & m [i][1]);
 break;
 }
 }
 for(int i = 1; i <= pgm_read_byte ( & words [a][0]); i++){  Serial.println( pgm_read_byte ( & words [a][i]));  grid.setPixelColor(pgm_read_byte ( & words [a][i]),colorWhite);  } //push to or past if (Minute > 0){
 if (past == true) {
 for(int i = 1; i <= pgm_read_byte ( & words [9][0]); i++){
 Serial.println( pgm_read_byte ( & words [9][i]) );
 grid.setPixelColor(pgm_read_byte ( & words [9][i]),colorWhite);
 }
 }
 if (past == false) {
 for(int i = 1; i <= pgm_read_byte ( & words [8][0]); i++){
 Serial.println( pgm_read_byte ( & words [8][i]) );
 grid.setPixelColor(pgm_read_byte ( & words [8][i]),colorWhite);
 }
 }
}
//push hour
 if (past == true){
 byte a = pgm_read_byte ( & h_a [Hour]);

for(int i = 1; i <= pgm_read_byte ( & words [a][0]); i++){
 Serial.println( pgm_read_byte ( & words [a][i]));
 grid.setPixelColor(pgm_read_byte ( & words [a][i]),colorWhite);
 }
 }

if (past == false){
 byte a = pgm_read_byte ( & h_b [Hour]);

for(int i = 1; i <= pgm_read_byte ( & words [a][0]); i++){
 Serial.println( pgm_read_byte ( & words [a][i]));
 grid.setPixelColor(pgm_read_byte ( & words [a][i]),colorWhite);
 }
 }
 }

Back and finally the clock is ticking…

Here is the last code. I used FastLed library and other nice stuff like PaletteKnife for FastLED. Really cool.

#include "FastLED.h"
#include <Time.h>
#include <Wire.h>  
#include <DS1307RTC.h>
#include <avr/pgmspace.h>
#include <IRremote.h>

#define N_LEDS 110
#define DATA_PIN 6
#define LED_TYPE    WS2812
#define BRIGHTNESS  255
#define UPDATES_PER_SECOND 50

#define TIME_HEADER  "T"

typedef const unsigned char prog_uchar;

CRGB leds[N_LEDS];

unsigned long previousMillis = 0;        
const long interval = 5000;
         
int RECV_PIN = 11;
boolean IR = false;
byte bb = 100;

#define arrIT         {2,0,19}
#define arrIS         {2,39,40}
#define arrAQUARTER   {8,80,18,21,38,41,58,61,78}
#define arrHALF       {4,3,16,23,36}
#define arrPAST       {4,4,15,24,35}
#define arrOCLOCK     {6,49,69,70,89,90,109}
#define arrTO         {2,96,103}
#define arrONE        {3,5,14,25}
#define arrTWO        {3,34,45,54}
#define arrTHREE      {5,65,74,85,94,105}
#define arrFOUR       {4,6,13,26,33}
#define arrFIVE       {4,46,53,66,73}
#define arrSIX        {3,86,93,106}
#define arrSEVEN      {5,55,64,75,84,95}
#define arrEIGHT      {5,8,11,28,31,48}
#define arrNINE       {4,7,12,27,32}
#define arrTEN        {3,9,10,29}
#define arrELEVEN     {6,51,68,71,88,91,108}
#define arrTWELVE     {6,52,67,72,87,92,107}
#define arrTWENTY     {6,2,17,22,37,42,57}
#define arrTWENTYFIVE {10,2,17,22,37,42,57,77,82,97,102}
#define arrMFIVE      {4,77,82,97,102}
#define arrMTEN       {3,56,63,76}

#define IR_ON           0xFFA25D
#define IR_AUTO         0xFF629D
#define IR_OFF          0xFFE21D
#define IR_SUP          0xFF22DD
#define IR_MUP          0xFF02FD
#define IR_BUP          0xFFC23D
#define IR_SDN          0xFFE0AF
#define IR_MDN          0xFFA857
#define IR_BDN          0xFF906F
#define IR_RED          0xFF6897
#define IR_GREEN        0xFF9867
#define IR_BLUE         0xFFB04F
#define IR_LIGHTYELLOW  0xFF30CF
#define IR_YELLOW       0xFF18E7
#define IR_LIGHTORANGE  0xFF7A85
#define IR_ORANGE       0xFF10EF
#define IR_GREY         0xFF38C7
#define IR_LIGHTBLUE    0xFF5AA5
#define IR_PINK         0xFF42BD
#define IR_LIGHTGREEN   0xFF4AB5
#define IR_WHITE        0xFF52AD



prog_uchar words[23][11] PROGMEM ={arrIT, arrIS, arrAQUARTER, arrMFIVE, arrMTEN, arrTWENTY, arrTWENTYFIVE, arrHALF, 
                                  arrTO, arrPAST, arrONE, arrTWO, arrTHREE,
                                  arrFOUR, arrFIVE, arrSIX, arrSEVEN, arrEIGHT,
                                  arrNINE, arrTEN, arrELEVEN, arrTWELVE,
                                  arrOCLOCK};
                                  
prog_uchar m[12][2] PROGMEM ={{0,22},{5,3},{10,4},{15,2},{20,5},{25,6},{30,7},{35,6},{40,5},
                                  {45,2},{50,4},{55,3}};

prog_uchar h_a[13] PROGMEM ={0,10,11,12,13,14,15,16,17,18,19,20,21};
prog_uchar h_b[13] PROGMEM ={0,11,12,13,14,15,16,17,18,19,20,21,10};

DEFINE_GRADIENT_PALETTE( p_orange ) {
    0,   9,  5,  1,
   48,  25,  9,  1,
   76, 137, 27,  1,
   96,  98, 42,  1,
  124, 144, 79,  1,
  153,  98, 42,  1,
  178, 137, 27,  1,
  211,  23,  9,  1,
  255,   9,  5,  1};


DEFINE_GRADIENT_PALETTE( p_green ) {
    0,   1, 22,  1,
  130,   1,168,  2,
  255,   1, 22,  1};



DEFINE_GRADIENT_PALETTE( p_red ) {
    0,  14,  2,  5,
   63,  40,  1,  7,
  130, 182,  1,  1,
  188,  40,  1,  7,
  255,  14,  2,  5};



DEFINE_GRADIENT_PALETTE( p_blue ) {
    0,   0,  0,  8,
   45,   0,  0, 45,
   79,   7, 12,255,
  119,  42, 55,255,
  158,   7, 12,255,
  209,   0,  0, 45,
  255,   0,  0,  8};


DEFINE_GRADIENT_PALETTE( p_yellow ) {
    0, 208,186, 19,
   53, 232,215, 42,
   84, 255,255, 45,
  127, 255,255,125,
  165, 255,255, 45,
  196, 232,215, 42,
  247, 208,186, 19,
  255, 208,186, 19};


DEFINE_GRADIENT_PALETTE( p_lightyellow ) {
    0, 159,142, 27,
   51, 192,180, 82,
  127, 255,255,255,
  201, 192,180, 82,
  255, 159,142, 27};


DEFINE_GRADIENT_PALETTE( p_lightorange ) {
    0,  47, 28,  2,
   76, 229, 73,  1,
  163, 255,255,  0,
  255, 229, 73,  1};


DEFINE_GRADIENT_PALETTE( p_grey ) {
    0,   1,  2,  1,
   51,  42, 55, 45,
   89, 144,178,170,
  130, 255,255,255,
  146, 194,215,210,
  175, 144,178,170,
  255,   1,  2,  1};


DEFINE_GRADIENT_PALETTE( p_lightblue ) {
    0,   1, 10, 19,
  132, 126,201,255,
  255,   1, 10, 19};


DEFINE_GRADIENT_PALETTE( p_pink ) {
    0,   2,  1,  8,
   94,  79,  2,212,
  140, 110, 11,197,
  255,   2,  1,  8};


DEFINE_GRADIENT_PALETTE( p_lightgreen ) {
    0,  30,186, 72,
   81,  77,255,105,
  127, 184,250,186,
  175,  77,255,105,
  255,  30,186, 72};


DEFINE_GRADIENT_PALETTE( p_white ) {
    0, 121,136,125,
   38, 173,180,172,
  107, 255,255,255,
  140, 255,255,255,
  219, 173,180,172,
  255, 121,136,125};


CRGBPalette16 currentPalette;
TBlendType    currentBlending;

extern CRGBPalette16 myRedWhiteBluePalette;
extern const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM;

IRrecv irrecv(RECV_PIN);
decode_results results;

void setup() {

  FastLED.addLeds<LED_TYPE, DATA_PIN, GRB>(leds, N_LEDS);
  FastLED.setBrightness(  BRIGHTNESS );
    
    currentPalette = p_orange;
    currentBlending = LINEARBLEND;
    
  Serial.begin(9600);
  setSyncProvider(RTC.get);       // the function to get the time from the RTC
  setSyncInterval(60);            // sync the time every 60 seconds (1 minutes)

  irrecv.enableIRIn(); // Start the receiver
  
  if(timeStatus() != timeSet){
     Serial.println("Unable to sync");
     RTC.set(1481781666);         // set the RTC to Thu, 15 Dec 2016 06:01:06 +00:00
     setTime(1481781666);
  }
  
}

void loop(){
  
  
 if (irrecv.decode(&results)) {

    IR = true;
    previousMillis = millis();
  
   // Serial.println(results.value, HEX);

    //if results.value == 0xFFE21D display time

    switch (results.value) {
      case IR_OFF:          //exit IR Loop
        IR = false;
        break;
        case IR_GREEN:
        currentPalette = p_green;
        IR = false;
        break;
        case IR_RED:
        currentPalette = p_red;
        IR = false;
        break;
        case IR_BLUE:
        currentPalette = p_blue;
        IR = false;
        break;
        case IR_LIGHTYELLOW:
        currentPalette = p_lightyellow;
        IR = false;
        break;
        case IR_YELLOW:
        currentPalette = p_yellow;
        IR = false;
        break;
        case IR_LIGHTORANGE:
        currentPalette = p_lightorange;
        IR = false;
        break;
        case IR_ORANGE:
        currentPalette = p_orange;
        IR = false;
        break;
        case IR_GREY:
        currentPalette = p_grey;
        IR = false;
        break;
        case IR_LIGHTBLUE:
        currentPalette = p_lightblue;
        IR = false;
        break;
        case IR_PINK:
        currentPalette = p_pink;
        IR = false;
        break;
        case IR_LIGHTGREEN:
        currentPalette = p_lightgreen;
        IR = false;
        break;
        case IR_WHITE:
        currentPalette = p_white;
        IR = false;
        break;
        case IR_MUP:
        RTC.set(now() + 300);                       // set the RTC and the system time to the received value
        setTime(now() + 300);
        IR = false;
        break;
        case IR_MDN:
        RTC.set(now() - 300);                       // set the RTC and the system time to the received value
        setTime(now() - 300);
        IR = false;
        break;
        case IR_BUP:
        bb = bb + 10;
        IR = false;
        break;
        case IR_BDN:
        bb = bb - 10;
        IR = false;
        break;
    }
    
    irrecv.resume(); // Receive the next value
 }
 
 if (!IR) {
      unsigned long currentMillis = millis();
  // if there is a serial connection lets see if we need to set the time
  if (Serial.available()) {
    time_t t = processSyncMessage();
    if (t != 0) {
      Serial.print("Time set via connection to: ");
      Serial.print(t);
      Serial.println();
      RTC.set(t);                       // set the RTC and the system time to the received value
      setTime(t);          
    }
  }

   static uint8_t startIndex = 0;
    startIndex = startIndex + 1; /* motion speed */
    
    FillLEDsFromPaletteColors( startIndex);
    pushtime();
  
    FastLED.show();
    FastLED.delay(1000 / UPDATES_PER_SECOND);
    }

if (IR) {
  //unsigned long currentMillis = millis();
  
if ( millis() - previousMillis >= interval) {
  IR = false; 
}
}
}
unsigned long processSyncMessage() {
  unsigned long pctime = 0L;
  const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013 

  if(Serial.find(TIME_HEADER)) {
     pctime = Serial.parseInt();
     pctime = pctime + 28800;       // + 8 hours - winter asia time
     return pctime;
     if( pctime < DEFAULT_TIME) {   
       pctime = 0L;                 // return 0 to indicate that the time is not valid
     }
     Serial.println("Set");
  }
  return pctime;
}

void pushtime(){

int Minute = minute();
int Hour = hourFormat12();
boolean past = true;

Minute = Minute - (Minute % 5);

if((minute()>34) && (minute()<= 59)) {
    past = false;
  }


// push IT
   for(int i = 1; i <= pgm_read_byte ( & words [0][0]); i++){
   leds[pgm_read_byte ( & words [0][i])] = CRGB::White;
  }
// push IS
   for(int i = 1; i <= pgm_read_byte ( & words [1][0]); i++){
   leds[pgm_read_byte ( & words [1][i])] = CRGB::White;
  }
// push Minute
  byte a;
  for (byte i=0; i<12; i++) {
   if (pgm_read_byte ( & m [i][0])  == Minute) {
     a = pgm_read_byte ( & m [i][1]);
     break;
   }
  }
   for(int i = 1; i <= pgm_read_byte ( & words [a][0]); i++){
   leds[pgm_read_byte ( & words [a][i])] = CRGB::White;
  }
  
//push to or past excepted 0
if ( Minute > 0){
if (past == true) {
  for(int i = 1; i <= pgm_read_byte ( & words [9][0]); i++){
   leds[pgm_read_byte ( & words [9][i])] = CRGB::White;
  }
}
if (past == false) {
  for(int i = 1; i <= pgm_read_byte ( & words [8][0]); i++){
   leds[pgm_read_byte ( & words [8][i])] = CRGB::White;
  }
}
}

//push hour
if (past == true){
   byte a = pgm_read_byte ( & h_a [Hour]);
  
   for(int i = 1; i <= pgm_read_byte ( & words [a][0]); i++){
   leds[pgm_read_byte ( & words [a][i])] = CRGB::White;
  }
}


if (past == false){
    byte a = pgm_read_byte ( & h_b [Hour]);
  
   for(int i = 1; i <= pgm_read_byte ( & words [a][0]); i++){
   leds[pgm_read_byte ( & words [a][i])] = CRGB::White;
  } 
}
}


void FillLEDsFromPaletteColors( uint8_t colorIndex)
{
    for( int i = 0; i < N_LEDS; i++) {
        leds[i] = ColorFromPalette( currentPalette, colorIndex, bb, currentBlending);
        colorIndex += 3;
    }
}

I added a IR remote to set the time, change the effect and brightness.

If you look at the program, I stopped the led refresh to be able to use the IR receiving. I  check the millis once in the IR receiving mode and after 5 sec hte refresh starts again. Not as nice as I want it to be but it does the job.

Some photos…

remote
Remote Control used
Advertisements

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

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

Connecting to %s