Clock
Word Clock
This is a project I started a while ago and not yet completed. Just posting part of the design
- First Part of the Project: The front
The spaces between letters are 31.25mm (pitch of my led stripes). Font: Varsity Regular
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.
2. Second Part of the Project: The back
Drawing of the laser cut back (layer 1)
Since I am limited by the cutting area of my laser cutter, I had to divide the area in 4 pieces.
Laser Cutting…
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…
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…
