Word clock on steroids

Nowaday it seems that everyone is building a word clock. The idea is nothing new anymore so I shouldn’t build one too, right? WRONG! Instead of going the easy route and rebuild a proven design I decided to create my own and beef it up a little. It’s definitely more expensive, more work, uses more power, produces more heat and has a lot of other drawbacks but I just need to have it. So far so good.

Planning

In order to do it right the first time some planning had to be done. (Thanks…captain obvious) The layout of other word clocks didn’t really please me so I did my own – in Swiss German! I didn’t want to show only the time but also the day, month and year. To display all that information at once I had to do a 15×15 raster. In the picture below a first test of the layout is shown. It was made using the laser I built in a previous project.

image101

When the date and year is displayed there will be some gaps but I guess it won’t be too much of a problem to read it.

The clock has to be readable from at least 10m so the height of each letter has to be 30mm or more. I made each letter around 40mm just to be sure. Lets calculate the whole size: 15x 40mm + Space + Frame… that’s around 900x900mm. Because the LED spacings of standard WS2812b LED strips is either too big or to small I decided to build PCBs for my word clock. I didn’t want to make the clock too deep so I had to take more LEDs than just one to get an even light distribution over the letter.

Winkel

Because the beam angle of the LED is around 120° a minimum distance of around 12mm to the front had to be kept. I used 2 8mm MDF sheets so the distance to the front will be around 15mm. I used 4 LEDs per letter that makes a total of 900 LEDs for the whole word clock! I used the LED panels that are described in an earlier blog entry. Each panel has 36 LEDs that makes 25 panels in total.

cad1

cad2

cad3

cad4

I used a stencil font because I didn’t want to deal with letters that would fall apart when lasering the front stencil.

cad5

Build time

The project took me quite long because it was a filler project with low priority. Here are some pictures of the building process…

PCBs!!!!

image219

image231

I used low melting point solder paste because I don’t have a proper reflow oven, just a normal industrial oven that can’t do temperature profiles. It melts at around 138° so the soldering puts way less stress on the LEDs than with normal solder paste. Only one LED on a panel didn’t work and that was because I placed it the wrong way. I also couldn’t make out a difference in the intensities of the LEDs.

image236

image235

image239

image237

I didn’t have access to an SMT assembly machine and was too stingy to let them produce professionally so I placed every LED and capacitor by hand! Gosh that was a lot of monotonous work.

image240

I put everything in the oven and after 5 minutes @ 160° the PCBs were soldered.

image244

Now to the frame.. In the end I made it 860x860mm because the Y-Axis of the laser supports sheet just a little below 900mm.

image251

image257

image247

image249

image245

image261

image263

image265

image266

I began to paint the frame but I noticed it doesn’t really make a difference in light intensity when the stencil is on. The frame won’t be seen afterwards and because I am lazy I didn’t paint the whole frame.

image264

Light it up!

I used the FastLed3.1 Library from FastLed in combination with an Arduino Due to control the LEDs. Setting the library up and let the demo code run wasn’t a problem at all.

The code is basically a huge look up table. feld

There are 225 field of 4 LEDs. There are only 3 types of fields (cases). Field 1 consisting of LED 0,1,10,11 field 2 consisting of LED 2,3,8,9 and field 3 consisting of LED 4,5,6,7. All other fields are the base field plus a multiplication of 3 (-> 9 fields per panel).

For example the letter A in field 189 is case 0. 189* 4 LEDs per  letter gives us LEDs 757, 758, 759, 760.

This can be easily programmed using modulo and multiplication operations. Then we have 68 different field sequences (= arrays with different lengths). For example “halbi” is sequence 8 using fields 24,25,26,33,34. I wrote a function with an array as input that displays each field of the sequence. Now when we want to display 13:45 = “Es isch viertel vor zwei” the sequences 1,4,6 and 10 have to light up. So in another truth table I hard-coded all the possibilities.

There are 60 / 5 = 12 time steps, 12 hour steps, 31 day steps, 12 month steps and some year steps. Now i programmed a function void displayTime(int year, int month, int day, int hour, int minute, int R, int G, int B){} that gets the value from the DS3231 time module and rounds it to 5 minute time steps and displays it. Maybe it sounds complicated but it’s really not a big deal. So here is the code. At the moment the colors are fading slowly. Note I used the DS1307 Library for the DS3231 because the native library for the DS3231 doesn’t work on the arduino due.

#include "FastLED.h"
#include <Wire.h>
#include "RTClib.h"

#define NUM_LEDS 900
#define DATA_PIN 6


const unsigned char seq1[]={6,0,1,9,10,11,18};                //es isch
const unsigned char seq2[]={4,20, 27, 28, 29};                //foif
const unsigned char seq3[]={3,36,37,38};                      //zäh
const unsigned char seq4[]={7,3,4,5,12,13,14,21};             //viertel
const unsigned char seq5[]={6,22,23,30,31,32,39};             //zwänzg
const unsigned char seq6[]={3,6,7,8};                         //vor
const unsigned char seq7[]={2,15,16};                         //ab
const unsigned char seq8[]={5,24,25,26,33,34};                //halbi
const unsigned char seq9[]={3,42,43,44};                      //eis
const unsigned char seq10[]={4,89,88,87,80};                  //zwei
const unsigned char seq11[]={3,79,78,71};                     //drü
const unsigned char seq12[]={5,70,69,62,61,60};               //vieri
const unsigned char seq13[]={5,86, 85, 84, 77, 76};           //foifi
const unsigned char seq14[]={6,75, 68, 67, 66, 59, 58};       //sächsi
const unsigned char seq15[]={4,57,50, 49, 48};                //SCMD
const unsigned char seq16[]={5,83,82,81,74,73};               //sibni
const unsigned char seq17[]={5,72, 65, 64, 63, 56};           //achti
const unsigned char seq18[]={4,55,54,47,46};                  //nüni
const unsigned char seq19[]={4,90, 91, 92, 99};               //zäni
const unsigned char seq20[]={4,100,101,108,109};              //elfi
const unsigned char seq21[]={6,110,117,118,119,126,127};      //zwölfi
const unsigned char seq22[]={6,93,94,95,102,103,104};                       //mentig
const unsigned char seq23[]={8,111,112,113,120,121,122,129,130};            //zischtig
const unsigned char seq24[]={8,96,97,98,105,106,107,114,115};               //mittwuch
const unsigned char seq25[]={9,179,178,177,170,169,168,161,160,159};        //dunschtig
const unsigned char seq26[]={6,152,151,150,143,142,141};                    //fritig
const unsigned char seq27[]={9,176,175,174,167,166,165,158,157,156};        //samschtig
const unsigned char seq28[]={7,116,123,124,125,132,133,134};                //Sunntig
const unsigned char seq29[]={1,149};                //1
const unsigned char seq30[]={1,148};                //2
const unsigned char seq31[]={1,147};                //3
const unsigned char seq32[]={1,140};                //1
const unsigned char seq33[]={1,139};                //2
const unsigned char seq34[]={1,138};                //3
const unsigned char seq35[]={1,173};                //4
const unsigned char seq36[]={1,172};                //5
const unsigned char seq37[]={1,171};                //6
const unsigned char seq38[]={1,164};                //7
const unsigned char seq39[]={1,163};                //8
const unsigned char seq40[]={1,162};                //9
const unsigned char seq41[]={1,155};                //0
const unsigned char seq42[]={3,154,153,146};        //JAN
const unsigned char seq43[]={3,145,144,137};        //FEB
const unsigned char seq44[]={3,180,181,182};        //MAR
const unsigned char seq45[]={3,189,190,191};        //APR
const unsigned char seq46[]={3,198,199,200};        //MAI
const unsigned char seq47[]={3,207,208,209};        //JUN
const unsigned char seq48[]={3,216,217,218};        //JUL
const unsigned char seq49[]={3,183,184,185};        //AUG
const unsigned char seq50[]={3,192,193,194};        //SEP
const unsigned char seq51[]={3,201,202,203};        //OKT
const unsigned char seq52[]={3,210,211,212};        //NOV
const unsigned char seq53[]={3,219,220,221};        //DEZ
const unsigned char seq54[]={1,186};                //2
const unsigned char seq55[]={1,187};                //0
const unsigned char seq56[]={1,188};                //1
const unsigned char seq57[]={1,195};                //2
const unsigned char seq58[]={1,196};                //1
const unsigned char seq59[]={1,197};                //2
const unsigned char seq61[]={1,204};                //3
const unsigned char seq62[]={1,205};                //4
const unsigned char seq63[]={1,206};                //5
const unsigned char seq64[]={1,213};                //6
const unsigned char seq65[]={1,214};                //7
const unsigned char seq66[]={1,215};                //8
const unsigned char seq67[]={1,222};                //9
const unsigned char seq68[]={1,223};                //0

CRGB leds[NUM_LEDS];

RTC_DS1307 rtc;
int minu,hr,dy,mon,yr,wkd;




void field(const unsigned char NR[], int R, int G, int B){
  
  int arraySize=NR[0];
  for(int i=1;i<=arraySize; i++){
  int case_nr;
  int multiplier;
  
  case_nr=NR[i]%3;                  
  multiplier=(NR[i]-case_nr)/3;     
  multiplier*=12;
  
  switch (case_nr){
  
    case 0:
                leds[1+multiplier-1].setRGB( R, G, B);
                leds[2+multiplier-1].setRGB( R, G, B);
                leds[11+multiplier-1].setRGB( R, G, B);
                leds[12+multiplier-1].setRGB( R, G, B);
                break;
    case 1:
                leds[3+multiplier-1].setRGB( R, G, B);
                leds[4+multiplier-1].setRGB( R, G, B);
                leds[9+multiplier-1].setRGB( R, G, B);
                leds[10+multiplier-1].setRGB( R, G, B);
                break;
    case 2:
                leds[5+multiplier-1].setRGB( R, G, B);
                leds[6+multiplier-1].setRGB( R, G, B);
                leds[7+multiplier-1].setRGB( R, G, B);
                leds[8+multiplier-1].setRGB( R, G, B);
                break;
  }
  
}
}


void displayTime(int year, int month, int day, int twhor, int minute, int wkd, int R, int G, int B){

  int rndHr;
  int hor=twhor;

  if(hor>12){hor-=12;}

  
for(int i=0;i<=899;i++){      //set all leds to Black, so the old values dont light up;
  leds[i].setRGB(0,0,0);
  }

  
  field(seq1,R,G,B);     //Es isch
switch (minute){
  
  case 0:
  rndHr=hor;
  break;
  
  case 1:case 2:case 3:case 4: case 5:                    //foif ab
  field(seq2,R,G,B);
  field(seq7,R,G,B);
  rndHr=hor;
  break;
  
  case 6:case 7:case 8:case 9: case 10:                    //zäh ab    
  field(seq3,R,G,B);
  field(seq7,R,G,B);
  rndHr=hor;
  break;
  
  case 11:case 12:case 13:case 14: case 15:                    //viertel ab
  field(seq4,R,G,B);
  field(seq7,R,G,B);
  rndHr=hor;
  break;
  
  case 16:case 17:case 18:case 19: case 20:                    //zwänzg ab
  field(seq5,R,G,B);
  field(seq7,R,G,B);
  rndHr=hor;
  break;
  
  case 21:case 22:case 23:case 24: case 25:                    //foif vor halbi
  field(seq2,R,G,B);
  field(seq6,R,G,B);
  field(seq8,R,G,B);
  rndHr=(hor+1)%12;
  break;
  
  case 26:case 27:case 28:case 29: case 30:                    //halbi
  field(seq8,R,G,B);
  rndHr=(hor+1)%12;
  break;
  
  case 31:case 32:case 33:case 34: case 35:                    //foif ab halbi
  field(seq2,R,G,B);
  field(seq7,R,G,B);
  field(seq8,R,G,B);
  rndHr=(hor+1)%12;
  break;
  
  case 36:case 37:case 38:case 39: case 40:                    //zwänzg vor
  field(seq5,R,G,B);
  field(seq6,R,G,B);
  rndHr=(hor+1)%12;
  break;
  
  case 41:case 42:case 43:case 44: case 45:                    //viertel vor
  field(seq4,R,G,B);
  field(seq6,R,G,B);
  rndHr=(hor+1)%12;
  break;
  
  case 46:case 47:case 48:case 49: case 50:                    //zäh vor
  field(seq3,R,G,B);
  field(seq6,R,G,B);
  rndHr=(hor+1)%12;
  break;

  case 51:case 52:case 53:case 54: case 55:                    //foif vor
  field(seq2,R,G,B);
  field(seq6,R,G,B);
  rndHr=(hor+1)%12;
  break;

  case 56:case 57:case 58:case 59:                             //stunde +1 
  rndHr=hor;
  rndHr=(hor+1)%12;
  break;
  
  }

switch (rndHr){
  case 1:
  field(seq9,R,G,B);    //eis
  break;

  case 2:
  field(seq10,R,G,B);  //zwei
  break;
  
  case 3:
  field(seq11,R,G,B);   //drü
  break;
  
  case 4:
  field(seq12,R,G,B);   //vieri
  break;
  
  case 5:
  field(seq13,R,G,B);   //foifi
  break;
  
  case 6:
  field(seq14,R,G,B);   //sächsi
  break;
  
  case 7:
  field(seq16,R,G,B);   //sibni
  break;
  
  case 8:
  field(seq17,R,G,B);   //achti
  break;
  
  case 9:
  field(seq18,R,G,B);   //nüni
  break;
  
  case 10:
  field(seq19,R,G,B);   //zäni
  break;
  
  case 11:
  field(seq20,R,G,B);   //elfi
  break;
  
  case 0: case 12:
  field(seq21,R,G,B);   //zwölfi
  break;
}
switch (day){
  case 1:
  field(seq32,R,G,B);
  break;
  
  case 2:
  field(seq33,R,G,B);
  break;
  
  case 3:
  field(seq34,R,G,B);
  break;
  
  case 4:
  field(seq35,R,G,B);
  break;
  
  case 5:
  field(seq36,R,G,B);
  break;
  
  case 6:
  field(seq37,R,G,B);
  break;
  
  case 7:
  field(seq38,R,G,B);
  break;
  
  case 8:
  field(seq39,R,G,B);
  break;
  
  case 9:
  field(seq40,R,G,B);
  break;
  
  case 10:
  field(seq29,R,G,B);
  field(seq41,R,G,B);
  break;
  
  case 11:
  field(seq29,R,G,B);
  field(seq32,R,G,B);
  break;
  
  case 12:
  field(seq29,R,G,B);
  field(seq33,R,G,B);
  break;
  
  case 13:
  field(seq29,R,G,B);
  field(seq34,R,G,B);
  break;
  
  case 14:
  field(seq29,R,G,B);
  field(seq35,R,G,B);
  break;
  
  case 15:
  field(seq29,R,G,B);
  field(seq36,R,G,B);
  break;
  
  case 16:
  field(seq29,R,G,B);
  field(seq37,R,G,B);
  break;
  
  case 17:
  field(seq29,R,G,B);
  field(seq38,R,G,B);
  break;
  
  case 18:
  field(seq29,R,G,B);
  field(seq39,R,G,B);
  break;
  
  case 19:
  field(seq29,R,G,B);
  field(seq40,R,G,B);
  break;
  
  case 20:
  field(seq30,R,G,B);
  field(seq41,R,G,B);
  break;
  
  case 21:
  field(seq30,R,G,B);
  field(seq32,R,G,B);
  break;
  
  case 22:
  field(seq30,R,G,B);
  field(seq33,R,G,B);
  break;
  
  case 23:
  field(seq30,R,G,B);
  field(seq34,R,G,B);
  break;
  
  case 24:
  field(seq30,R,G,B);
  field(seq35,R,G,B);
  break;
  
  case 25:
  field(seq30,R,G,B);
  field(seq36,R,G,B);
  break;
  
  case 26:
  field(seq30,R,G,B);
  field(seq37,R,G,B);
  break;
  
  case 27:
  field(seq30,R,G,B);
  field(seq38,R,G,B);
  break;
  
  case 28:
  field(seq30,R,G,B);
  field(seq39,R,G,B);
  break;
  
  case 29:
  field(seq30,R,G,B);
  field(seq40,R,G,B);
  break;
  
  case 30:
  field(seq31,R,G,B);
  field(seq41,R,G,B);
  break;
  
  case 31:
  field(seq31,R,G,B);
  field(seq32,R,G,B);
  break;
}
switch (month){
  case 1:
  field(seq42,R,G,B);
  break;
  case 2:
  field(seq43,R,G,B);
  break;
  case 3:
  field(seq44,R,G,B);
  break;
  case 4:
  field(seq45,R,G,B);
  break;
  case 5:
  field(seq46,R,G,B);
  break;
  case 6:
  field(seq47,R,G,B);
  break;
  case 7:
  field(seq48,R,G,B);
  break;
  case 8:
  field(seq49,R,G,B);
  break;
  case 9:
  field(seq50,R,G,B);
  break;
  case 10:
  field(seq51,R,G,B);
  break;
  case 11:
  field(seq52,R,G,B);
  break;
  case 12:
  field(seq53,R,G,B);
  break;  
}
switch (year){
  case 2015:
  field(seq54,R,G,B);
  field(seq55,R,G,B);
  field(seq56,R,G,B);
  field(seq63,R,G,B);
  break;
  
  case 2016:
  field(seq54,R,G,B);
  field(seq55,R,G,B);
  field(seq56,R,G,B);
  field(seq64,R,G,B);
  break;
  
  case 2017:
  field(seq54,R,G,B);
  field(seq55,R,G,B);
  field(seq56,R,G,B);
  field(seq65,R,G,B);
  break;
  
  case 2018:
  field(seq54,R,G,B);
  field(seq55,R,G,B);
  field(seq56,R,G,B);
  field(seq66,R,G,B);
  break;
  
  case 2019:
  field(seq54,R,G,B);
  field(seq55,R,G,B);
  field(seq56,R,G,B);
  field(seq67,R,G,B);
  break;
  
  case 2020:
  field(seq54,R,G,B);
  field(seq55,R,G,B);
  field(seq57,R,G,B);
  field(seq68,R,G,B);
  break;
}
switch (wkd){
  case 1:
  field(seq22,R,G,B);   //mentig
  break;
  case 2:
  field(seq23,R,G,B);   //zischtig
  break;
  case 3:
  field(seq24,R,G,B);   //mittwuch
  break;
  case 4:
  field(seq25,R,G,B);   //dunnschtig
  break;
  case 5:
  field(seq26,R,G,B);   //fritig
  break;
  case 6:
  field(seq27,R,G,B);   //samstig
  break;
  case 0:
  field(seq28,R,G,B);   //Sunntig
  break;
}
FastLED.show();
}


void getTime()
{
  DateTime now = rtc.now();
  
  minu=now.minute();
  hr=now.hour();
  dy=now.day();
  mon=now.month();
  yr=now.year();
  wkd=now.dayOfTheWeek();

}

void setup() { 
        // Start the I2C interface
        rtc.begin();
        //rtc.adjust(DateTime(2015, 12, 25, 20, 51, 30)); //year, month, day, hour, minute, second
       // Serial.begin(57600);
  Wire.begin();
  FastLED.addLeds<WS2812,DATA_PIN, GRB>(leds, NUM_LEDS);
  
}

void loop() { 
  
	getTime();

	float redVal = (exp(sin(millis()/7000.0*PI)) - 0.36787944)*108.0;
	float grnVal = (exp(cos(millis()/11000.0*PI)) - 0.36787944)*108.0;
	float bluVal = (exp(sin(millis()/13000.0*PI)) - 0.36787944)*108.0;

	displayTime(yr, mon, dy, hr, minu, wkd, int(redVal), int(grnVal), int(bluVal));

  delay(10);


}

DSC04488-1 DSC04493-2 DSC04494-3

In the meantime I moved to a new flat, the word clock has been installed and obviously something had to break. So while moving the solder joint of two of the 900 LEDs broke, which resulted in some weird display patterns. I then took the opportunity to update the code and move from the Arduino DUE platform to an ESP8266 so the microcontroller can be connected to my guest WIFI and get the time from a NTP server. I rewrote parts of the code and included another feature: From 9pm to 8am the LEDs will be in a dim red color. The reason for that feature is the fact that blue light can have a bad influence on the sleep quality. Moreover looking into a bright blue light during a nightly bathroom break is not really pleasant. So here’s the new code:

define FASTLED_ESP8266_RAW_PIN_ORDER
 include "FastLED.h"
 define NUM_LEDS 900
 define DATA_PIN 4 //= PinD2 ufem Board
 include 
 include 
 include 
 include 
 float redVal = 0; float grnVal = 0; float bluVal = 0;
 const char ssid[] = "*********";  //  your network SSID (name)
 const char pass[] = "*********";       // your network password
 // NTP Servers:
 static const char ntpServerName[] = "0.ch.pool.ntp.org";
 //static const char ntpServerName[] = "time.nist.gov";
 //static const char ntpServerName[] = "time-a.timefreq.bldrdoc.gov";
 //static const char ntpServerName[] = "time-b.timefreq.bldrdoc.gov";
 //static const char ntpServerName[] = "time-c.timefreq.bldrdoc.gov";
 const int timeZone = 0;     // Central European Time 2,                              leave 0 if auto changing time zone is activated
 //const int timeZone = -5;  // Eastern Standard Time (USA)
 //const int timeZone = -4;  // Eastern Daylight Time (USA)
 //const int timeZone = -8;  // Pacific Standard Time (USA)
 //const int timeZone = -7;  // Pacific Daylight Time (USA)
 long previousMillis = 0;        // will store last time LED was updated
 long interval = 1000;
 WiFiUDP Udp;
 unsigned int localPort = 8888;  // local port to listen for UDP packets
 time_t getNtpTime();
 void digitalClockDisplay();
 void printDigits(int digits);
 void sendNTPpacket(IPAddress &address);
 //Timezone
 //Central European Time (Frankfurt, Paris)
 TimeChangeRule CEST = { "CEST", Last, Sun, Mar, 2, 120 };     //Central European Summer Time
 TimeChangeRule CET = { "CET ", Last, Sun, Oct, 3, 60 };       //Central European Standard Time
 Timezone CE(CEST, CET);
 TimeChangeRule *tcr;        //pointer to the time change rule, use to get the TZ abbrev
 time_t utc, local;
 CRGB leds[NUM_LEDS];
 const unsigned char seq1[]={6,0,1,9,10,11,18};                //es isch
 const unsigned char seq2[]={4,20, 27, 28, 29};                //foif
 const unsigned char seq3[]={3,36,37,38};                      //zäh
 const unsigned char seq4[]={7,3,4,5,12,13,14,21};             //viertel
 const unsigned char seq5[]={6,22,23,30,31,32,39};             //zwänzg
 const unsigned char seq6[]={3,6,7,8};                         //vor
 const unsigned char seq7[]={2,15,16};                         //ab
 const unsigned char seq8[]={5,24,25,26,33,34};                //halbi
 const unsigned char seq9[]={3,42,43,44};                      //eis
 const unsigned char seq10[]={4,89,88,87,80};                  //zwei
 const unsigned char seq11[]={3,79,78,71};                     //drü
 const unsigned char seq12[]={5,70,69,62,61,60};               //vieri
 const unsigned char seq13[]={5,86, 85, 84, 77, 76};           //foifi
 const unsigned char seq14[]={6,75, 68, 67, 66, 59, 58};       //sächsi
 const unsigned char seq15[]={4,57,50, 49, 48};                //SCMD
 const unsigned char seq16[]={5,83,82,81,74,73};               //sibni
 const unsigned char seq17[]={5,72, 65, 64, 63, 56};           //achti
 const unsigned char seq18[]={4,55,54,47,46};                  //nüni
 const unsigned char seq19[]={4,90, 91, 92, 99};               //zäni
 const unsigned char seq20[]={4,100,101,108,109};              //elfi
 const unsigned char seq21[]={6,110,117,118,119,126,127};      //zwölfi
 const unsigned char seq22[]={6,93,94,95,102,103,104};                       //mentig
 const unsigned char seq23[]={8,111,112,113,120,121,122,129,130};            //zischtig
 const unsigned char seq24[]={8,96,97,98,105,106,107,114,115};               //mittwuch
 const unsigned char seq25[]={9,179,178,177,170,169,168,161,160,159};        //dunschtig
 const unsigned char seq26[]={6,152,151,150,143,142,141};                    //fritig
 const unsigned char seq27[]={9,176,175,174,167,166,165,158,157,156};        //samschtig
 const unsigned char seq28[]={7,116,123,124,125,132,133,134};                //Sunntig
 const unsigned char seq29[]={1,149};                //1
 const unsigned char seq30[]={1,148};                //2
 const unsigned char seq31[]={1,147};                //3
 const unsigned char seq32[]={1,140};                //1
 const unsigned char seq33[]={1,139};                //2
 const unsigned char seq34[]={1,138};                //3
 const unsigned char seq35[]={1,173};                //4
 const unsigned char seq36[]={1,172};                //5
 const unsigned char seq37[]={1,171};                //6
 const unsigned char seq38[]={1,164};                //7
 const unsigned char seq39[]={1,163};                //8
 const unsigned char seq40[]={1,162};                //9
 const unsigned char seq41[]={1,155};                //0
 const unsigned char seq42[]={3,154,153,146};        //JAN
 const unsigned char seq43[]={3,145,144,137};        //FEB
 const unsigned char seq44[]={3,180,181,182};        //MAR
 const unsigned char seq45[]={3,189,190,191};        //APR
 const unsigned char seq46[]={3,198,199,200};        //MAI
 const unsigned char seq47[]={3,207,208,209};        //JUN
 const unsigned char seq48[]={3,216,217,218};        //JUL
 const unsigned char seq49[]={3,183,184,185};        //AUG
 const unsigned char seq50[]={3,192,193,194};        //SEP
 const unsigned char seq51[]={3,201,202,203};        //OKT
 const unsigned char seq52[]={3,210,211,212};        //NOV
 const unsigned char seq53[]={3,219,220,221};        //DEZ
 const unsigned char seq54[]={1,186};                //2
 const unsigned char seq55[]={1,187};                //0
 const unsigned char seq56[]={1,188};                //1
 const unsigned char seq57[]={1,195};                //2
 const unsigned char seq58[]={1,196};                //1
 const unsigned char seq59[]={1,197};                //2
 const unsigned char seq61[]={1,204};                //3
 const unsigned char seq62[]={1,205};                //4
 const unsigned char seq63[]={1,206};                //5
 const unsigned char seq64[]={1,213};                //6
 const unsigned char seq65[]={1,214};                //7
 const unsigned char seq66[]={1,215};                //8
 const unsigned char seq67[]={1,222};                //9
 const unsigned char seq68[]={1,223};                //0 falsch auf schablone, sollte 0 sein ist aber N
 //RTC_DS1307 rtc;
 int minu,hr,dy,mon,yr,wkd;
 void field(const unsigned char NR[], int R, int G, int B){
 int arraySize=NR[0];
   for(int i=1;i<=arraySize; i++){
   int case_nr;
   int multiplier;
 case_nr=NR[i]%3;                  
   multiplier=(NR[i]-case_nr)/3;     
   multiplier*=12;
 switch (case_nr){
 case 0:             leds[1+multiplier-1].setRGB( R, G, B);             leds[2+multiplier-1].setRGB( R, G, B);             leds[11+multiplier-1].setRGB( R, G, B);             leds[12+multiplier-1].setRGB( R, G, B);             break; case 1:             leds[3+multiplier-1].setRGB( R, G, B);             leds[4+multiplier-1].setRGB( R, G, B);             leds[9+multiplier-1].setRGB( R, G, B);             leds[10+multiplier-1].setRGB( R, G, B);             break; case 2:             leds[5+multiplier-1].setRGB( R, G, B);             leds[6+multiplier-1].setRGB( R, G, B);             leds[7+multiplier-1].setRGB( R, G, B);             leds[8+multiplier-1].setRGB( R, G, B);             break;
 }
 }
 }
 void displayTime(int year, int month, int day, int twhor, int minute, int wkd, int R, int G, int B){
 int rndHr;
   int hor=twhor;
 if(hor>12){hor-=12;}
 for(int i=0;i<=899;i++){      //set all leds to Black, so the old values dont light up;
   leds[i].setRGB(0,0,0);
   }
 field(seq1,R,G,B);     //Es isch
 switch (minute){
 case 0:
   rndHr=hor;
   break;
 case 1:case 2:case 3:case 4: case 5:                    //foif ab
   field(seq2,R,G,B);
   field(seq7,R,G,B);
   rndHr=hor;
   break;
 case 6:case 7:case 8:case 9: case 10:                    //zäh ab    
   field(seq3,R,G,B);
   field(seq7,R,G,B);
   rndHr=hor;
   break;
 case 11:case 12:case 13:case 14: case 15:                    //viertel ab
   field(seq4,R,G,B);
   field(seq7,R,G,B);
   rndHr=hor;
   break;
 case 16:case 17:case 18:case 19: case 20:                    //zwänzg ab
   field(seq5,R,G,B);
   field(seq7,R,G,B);
   rndHr=hor;
   break;
 case 21:case 22:case 23:case 24: case 25:                    //foif vor halbi
   field(seq2,R,G,B);
   field(seq6,R,G,B);
   field(seq8,R,G,B);
   rndHr=(hor+1)%12;
   break;
 case 26:case 27:case 28:case 29: case 30:                    //halbi
   field(seq8,R,G,B);
   rndHr=(hor+1)%12;
   break;
 case 31:case 32:case 33:case 34: case 35:                    //foif ab halbi
   field(seq2,R,G,B);
   field(seq7,R,G,B);
   field(seq8,R,G,B);
   rndHr=(hor+1)%12;
   break;
 case 36:case 37:case 38:case 39: case 40:                    //zwänzg vor
   field(seq5,R,G,B);
   field(seq6,R,G,B);
   rndHr=(hor+1)%12;
   break;
 case 41:case 42:case 43:case 44: case 45:                    //viertel vor
   field(seq4,R,G,B);
   field(seq6,R,G,B);
   rndHr=(hor+1)%12;
   break;
 case 46:case 47:case 48:case 49: case 50:                    //zäh vor
   field(seq3,R,G,B);
   field(seq6,R,G,B);
   rndHr=(hor+1)%12;
   break;
 case 51:case 52:case 53:case 54: case 55:                    //foif vor
   field(seq2,R,G,B);
   field(seq6,R,G,B);
   rndHr=(hor+1)%12;
   break;
 case 56:case 57:case 58:case 59:                             //stunde +1 
   rndHr=hor;
   rndHr=(hor+1)%12;
   break;
 }
 switch (rndHr){
   case 1:
   field(seq9,R,G,B);    //eis
   break;
 case 2:
   field(seq10,R,G,B);  //zwei
   break;
 case 3:
   field(seq11,R,G,B);   //drü
   break;
 case 4:
   field(seq12,R,G,B);   //vieri
   break;
 case 5:
   field(seq13,R,G,B);   //foifi
   break;
 case 6:
   field(seq14,R,G,B);   //sächsi
   break;
 case 7:
   field(seq16,R,G,B);   //sibni
   break;
 case 8:
   field(seq17,R,G,B);   //achti
   break;
 case 9:
   field(seq18,R,G,B);   //nüni
   break;
 case 10:
   field(seq19,R,G,B);   //zäni
   break;
 case 11:
   field(seq20,R,G,B);   //elfi
   break;
 case 0: case 12:
   field(seq21,R,G,B);   //zwölfi
   break;
 }
 switch (day){
   case 1:
   field(seq32,R,G,B);
   break;
 case 2:
   field(seq33,R,G,B);
   break;
 case 3:
   field(seq34,R,G,B);
   break;
 case 4:
   field(seq35,R,G,B);
   break;
 case 5:
   field(seq36,R,G,B);
   break;
 case 6:
   field(seq37,R,G,B);
   break;
 case 7:
   field(seq38,R,G,B);
   break;
 case 8:
   field(seq39,R,G,B);
   break;
 case 9:
   field(seq40,R,G,B);
   break;
 case 10:
   field(seq29,R,G,B);
   field(seq41,R,G,B);
   break;
 case 11:
   field(seq29,R,G,B);
   field(seq32,R,G,B);
   break;
 case 12:
   field(seq29,R,G,B);
   field(seq33,R,G,B);
   break;
 case 13:
   field(seq29,R,G,B);
   field(seq34,R,G,B);
   break;
 case 14:
   field(seq29,R,G,B);
   field(seq35,R,G,B);
   break;
 case 15:
   field(seq29,R,G,B);
   field(seq36,R,G,B);
   break;
 case 16:
   field(seq29,R,G,B);
   field(seq37,R,G,B);
   break;
 case 17:
   field(seq29,R,G,B);
   field(seq38,R,G,B);
   break;
 case 18:
   field(seq29,R,G,B);
   field(seq39,R,G,B);
   break;
 case 19:
   field(seq29,R,G,B);
   field(seq40,R,G,B);
   break;
 case 20:
   field(seq30,R,G,B);
   field(seq41,R,G,B);
   break;
 case 21:
   field(seq30,R,G,B);
   field(seq32,R,G,B);
   break;
 case 22:
   field(seq30,R,G,B);
   field(seq33,R,G,B);
   break;
 case 23:
   field(seq30,R,G,B);
   field(seq34,R,G,B);
   break;
 case 24:
   field(seq30,R,G,B);
   field(seq35,R,G,B);
   break;
 case 25:
   field(seq30,R,G,B);
   field(seq36,R,G,B);
   break;
 case 26:
   field(seq30,R,G,B);
   field(seq37,R,G,B);
   break;
 case 27:
   field(seq30,R,G,B);
   field(seq38,R,G,B);
   break;
 case 28:
   field(seq30,R,G,B);
   field(seq39,R,G,B);
   break;
 case 29:
   field(seq30,R,G,B);
   field(seq40,R,G,B);
   break;
 case 30:
   field(seq31,R,G,B);
   field(seq41,R,G,B);
   break;
 case 31:
   field(seq31,R,G,B);
   field(seq32,R,G,B);
   break;
 }
 switch (month){
   case 1:
   field(seq42,R,G,B);
   break;
   case 2:
   field(seq43,R,G,B);
   break;
   case 3:
   field(seq44,R,G,B);
   break;
   case 4:
   field(seq45,R,G,B);
   break;
   case 5:
   field(seq46,R,G,B);
   break;
   case 6:
   field(seq47,R,G,B);
   break;
   case 7:
   field(seq48,R,G,B);
   break;
   case 8:
   field(seq49,R,G,B);
   break;
   case 9:
   field(seq50,R,G,B);
   break;
   case 10:
   field(seq51,R,G,B);
   break;
   case 11:
   field(seq52,R,G,B);
   break;
   case 12:
   field(seq53,R,G,B);
   break;  
 }
 switch (year){
   case 2015:
   field(seq54,R,G,B);
   field(seq55,R,G,B);
   field(seq56,R,G,B);
   field(seq63,R,G,B);
   break;
 case 2016:
   field(seq54,R,G,B);
   field(seq55,R,G,B);
   field(seq56,R,G,B);
   field(seq64,R,G,B);
   break;
 case 2017:
   field(seq54,R,G,B);
   field(seq55,R,G,B);
   field(seq56,R,G,B);
   field(seq65,R,G,B);
   break;
 case 2018:
   field(seq54,R,G,B);
   field(seq55,R,G,B);
   field(seq56,R,G,B);
   field(seq66,R,G,B);
   break;
 case 2019:
   field(seq54,R,G,B);
   field(seq55,R,G,B);
   field(seq56,R,G,B);
   field(seq67,R,G,B);
   break;
 case 2020:
   field(seq54,R,G,B);
   field(seq55,R,G,B);
   //field(seq57,R,G,B);
   //field(seq68,R,G,B);
   break;
 case 2021:
 field(seq54,R,G,B);
   field(seq55,R,G,B);
   field(seq57,R,G,B);
   field(seq58,R,G,B);
   break;
 case 2022:
 field(seq54,R,G,B);
   field(seq55,R,G,B);
   field(seq57,R,G,B);
   field(seq59,R,G,B);
   break;
 case 2023:
 field(seq54,R,G,B);
   field(seq55,R,G,B);
   field(seq57,R,G,B);
   field(seq61,R,G,B);
   break;
 case 2024:
 field(seq54,R,G,B);
   field(seq55,R,G,B);
   field(seq57,R,G,B);
   field(seq62,R,G,B);
   break;
 case 2025:
 field(seq54,R,G,B);
   field(seq55,R,G,B);
   field(seq57,R,G,B);
   field(seq63,R,G,B);
   break;
 case 2026:
 field(seq54,R,G,B);
   field(seq55,R,G,B);
   field(seq57,R,G,B);
   field(seq64,R,G,B);
   break;
 case 2027:
 field(seq54,R,G,B);
   field(seq55,R,G,B);
   field(seq57,R,G,B);
   field(seq65,R,G,B);
   break;
   case 2028:
 field(seq54,R,G,B);
   field(seq55,R,G,B);
   field(seq57,R,G,B);
   field(seq66,R,G,B);
   break;
   case 2029:
 field(seq54,R,G,B);
   field(seq55,R,G,B);
   field(seq57,R,G,B);
   field(seq67,R,G,B);
   break;
 }
 switch (wkd){
   case 2:
   field(seq22,R,G,B);   //mentig
   break;
   case 3:
   field(seq23,R,G,B);   //zischtig
   break;
   case 4:
   field(seq24,R,G,B);   //mittwuch
   break;
   case 5:
   field(seq25,R,G,B);   //dunnschtig
   break;
   case 6:
   field(seq26,R,G,B);   //fritig
   break;
   case 7:
   field(seq27,R,G,B);   //samstig
   break;
   case 1:
   field(seq28,R,G,B);   //Sunntig
   break;
 }
 FastLED.show();
 }
 void getTime(time_t t)
 {
 minu=minute(t);
   hr=hour(t);
   dy=day(t);
   mon=month(t);
   yr=year(t);
   wkd=weekday(t);
 Serial.println(minu, DEC);
 Serial.println(hr, DEC);
 Serial.println(dy, DEC);
 Serial.println(mon, DEC);
 Serial.println(yr, DEC);
 Serial.println(wkd, DEC);
 }
 void getTime()
 {
 minu=minute();
   delay(5);
   hr=hour();
   delay(5);
   dy=day();
   delay(5);
   mon=month();
   delay(5);
   yr=year();
   delay(5);
   wkd=weekday();
   delay(5);
 //Serial.println(minu, DEC);
 //Serial.println(hr, DEC);
 //Serial.println(dy, DEC);
 //Serial.println(mon, DEC);
 //Serial.println(yr, DEC);
 //Serial.println(wkd, DEC);
 }
 void setup()
 {
   Serial.begin(115200);
   FastLED.addLeds(leds, NUM_LEDS);
   leds[0].setRGB(0,255,0);
   FastLED.show();
   //while (!Serial) ; // Needed for Leonardo only
   delay(250);
   Serial.println("TimeNTP Example");
   Serial.print("Connecting to ");
   Serial.println(ssid);
   WiFi.begin(ssid, pass);
 while (WiFi.status() != WL_CONNECTED) {
     delay(500);
     Serial.print(".");
   }
 Serial.print("IP number assigned by DHCP is ");
   Serial.println(WiFi.localIP());
   Serial.println("Starting UDP");
   Udp.begin(localPort);
   Serial.print("Local port: ");
   Serial.println(Udp.localPort());
   Serial.println("waiting for sync");
   setSyncProvider(getNtpTime);
   setSyncInterval(300);
 }
 time_t prevDisplay = 0; // when the digital clock was displayed
 void loop()
 {
   if (timeStatus() != timeNotSet) {
     if (now() != prevDisplay) { //update the display only if time has changed
       prevDisplay = now();
      // digitalClockDisplay();
     }
   }
 unsigned long currentMillis = millis();
 if(currentMillis - previousMillis > interval) {
     previousMillis = currentMillis;
    // getTime(); //not auto changing time zone
     Serial.println("Still alive");
     Serial.println(hr);
   getTime(local);                   //auto changing time zone   local = CE.toLocal(now(), &tcr);  //auto changing time zone
 //    printTime(local);
 }
 if(hr>=8 && hr<=21){ //color fade only during the day
     redVal = (exp(sin(millis()/7000.0PI)) - 0.36787944)108.0;
     grnVal = (exp(cos(millis()/11000.0PI)) - 0.36787944)108.0;
     bluVal = (exp(sin(millis()/13000.0PI)) - 0.36787944)108.0;
   }
   else{
     redVal=64;
     grnVal=0;
     bluVal=0;
   }
 displayTime(yr, mon, dy, hr, minu, wkd, int(redVal), int(grnVal), int(bluVal));
 delay(10);
 if (WiFi.status() != WL_CONNECTED) {   //achtung nur zum testen, falls wifi connection verloren
 WiFi.begin(ssid, pass); Udp.begin(localPort); setSyncProvider(getNtpTime); setSyncInterval(300);
 }
 }
 void digitalClockDisplay()
 {
   // digital clock display of the time
   Serial.print(hour());
   printDigits(minute());
   printDigits(second());
   Serial.print(" ");
   Serial.print(day());
   Serial.print(".");
   Serial.print(month());
   Serial.print(".");
   Serial.print(year());
   Serial.println();
 }
 void printDigits(int digits)
 {
   // utility for digital clock display: prints preceding colon and leading 0
   Serial.print(":");
   if (digits < 10)
     Serial.print('0');
   Serial.print(digits);
 }
 /-------- NTP code ----------/
 const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
 byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
 time_t getNtpTime()
 {
   IPAddress ntpServerIP; // NTP server's ip address
 while (Udp.parsePacket() > 0) ; // discard any previously received packets
   Serial.println("Transmit NTP Request");
   // get a random server from the pool
   WiFi.hostByName(ntpServerName, ntpServerIP);
   Serial.print(ntpServerName);
   Serial.print(": ");
   Serial.println(ntpServerIP);
   sendNTPpacket(ntpServerIP);
   uint32_t beginWait = millis();
   while (millis() - beginWait < 1500) {     int size = Udp.parsePacket();     if (size >= NTP_PACKET_SIZE) {
       Serial.println("Receive NTP Response");
       Udp.read(packetBuffer, NTP_PACKET_SIZE);  // read packet into the buffer
       unsigned long secsSince1900;
       // convert four bytes starting at location 40 to a long integer
       secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
       secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
       secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
       secsSince1900 |= (unsigned long)packetBuffer[43];
       return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
     }
   }
   Serial.println("No NTP Response :-(");
   return 0; // return 0 if unable to get the time
 }
 // send an NTP request to the time server at the given address
 void sendNTPpacket(IPAddress &address)
 {
   // set all bytes in the buffer to 0
   memset(packetBuffer, 0, NTP_PACKET_SIZE);
   // Initialize values needed to form NTP request
   // (see URL above for details on the packets)
   packetBuffer[0] = 0b11100011;   // LI, Version, Mode
   packetBuffer[1] = 0;     // Stratum, or type of clock
   packetBuffer[2] = 6;     // Polling Interval
   packetBuffer[3] = 0xEC;  // Peer Clock Precision
   // 8 bytes of zero for Root Delay & Root Dispersion
   packetBuffer[12] = 49;
   packetBuffer[13] = 0x4E;
   packetBuffer[14] = 49;
   packetBuffer[15] = 52;
   // all NTP fields have been given values, now
   // you can send a packet requesting a timestamp:
   Udp.beginPacket(address, 123); //NTP requests are to port 123
   Udp.write(packetBuffer, NTP_PACKET_SIZE);
   Udp.endPacket();
 }