Fading RGB Clock

I am trying to create a clock using 12 rgb LEDs. Time will be represented in colors, Red for seconds, green for minutes, and blue for hours.

Currently I have code that works but it does not look exactly how I want it. I am using tlc5940's to control all 12 LED's.

This is my current code(hours is omitted)

int TimeBrightness[] = {0, 1023, 2047, 4095, 2047, 1023, 0};
int oldsec = 0;
int oldmin = 0;

void NewTime(){
  int seconds = second();
  int minutes = minute();
  Serial.println(seconds);
  
  if(seconds < 7){
    redtime[0]=TimeBrightness[seconds];
  }
  if(seconds >= 5 && seconds < 12){
    redtime[1]=TimeBrightness[seconds-5];
  }
  if(seconds >= 10 && seconds < 17){
    redtime[2]=TimeBrightness[seconds-10];
  }
  if(seconds >= 15 && seconds < 22){
    redtime[3]=TimeBrightness[seconds-15];
  }
  if(seconds >= 20 && seconds < 27){
    redtime[4]=TimeBrightness[seconds-20];
  }
  if(seconds >= 25 && seconds < 32){
    redtime[5]=TimeBrightness[seconds-25];
  }
  if(seconds >= 30 && seconds < 37){
    redtime[6]=TimeBrightness[seconds-30];
  }
  if(seconds >= 35 && seconds < 42){
    redtime[7]=TimeBrightness[seconds-35];
  }
  if(seconds >= 40 && seconds < 47){
    redtime[8]=TimeBrightness[seconds-40];
  }
  if(seconds >= 45 && seconds < 52){
    redtime[9]=TimeBrightness[seconds-45];
  }
  if(seconds >= 50 && seconds < 57){
    redtime[10]=TimeBrightness[seconds-50];
  }
  if(seconds >= 55){
    redtime[11]=TimeBrightness[seconds-55];
  }
  if(oldsec == 59){
    redtime[11]=TimeBrightness[5];
  }
  if(seconds == 1){
    redtime[11]=TimeBrightness[6];
  }
  oldsec = seconds;
  
  if(minutes < 7){
    grntime[0]=TimeBrightness[minutes];
  }
  if(minutes >= 5 && minutes < 12){
    grntime[1]=TimeBrightness[minutes-5];
  }
  if(minutes >= 10 && minutes < 17){
    grntime[2]=TimeBrightness[minutes-10];
  }
  if(minutes >= 15 && minutes < 22){
    grntime[3]=TimeBrightness[minutes-15];
  }
  if(minutes >= 20 && minutes < 27){
    grntime[4]=TimeBrightness[minutes-20];
  }
  if(minutes >= 25 && minutes < 32){
    grntime[5]=TimeBrightness[minutes-25];
  }
  if(minutes >= 30 && minutes < 37){
    grntime[6]=TimeBrightness[minutes-30];
  }
  if(minutes >= 35 && minutes < 42){
    grntime[7]=TimeBrightness[minutes-35];
  }
  if(minutes >= 40 && minutes < 47){
    grntime[8]=TimeBrightness[minutes-40];
  }
  if(minutes >= 45 && minutes < 52){
    grntime[9]=TimeBrightness[minutes-45];
  }
  if(minutes >= 50 && minutes < 57){
    grntime[10]=TimeBrightness[minutes-50];
  }
  if(minutes >= 55){
    grntime[11]=TimeBrightness[minutes-55];
  }
  if(oldmin == 59){
    grntime[11]=TimeBrightness[5];
  }
  if(minutes == 1){
    grntime[11]=TimeBrightness[6];
  }
  oldmin = minutes;
  
  for(int lights = 0; lights < 12; lights++){
    setLight(redtime[lights],grntime[lights],blutime[lights],lights);
  }
}

void setLight(int red, int green, int blue, int lightnum){
  Tlc.set(0+(3*lightnum), red);
  Tlc.set(1+(3*lightnum), green);
  Tlc.set(2+(3*lightnum), blue);
  Tlc.update();
}

My complaint with my code is, I cant for the life of me figure out a way to gradually fade in and out colors around the LED's. Instead all I can do is get a "ticking" look.
I need a way to retrieve time from time.h in less than second increments. I have thought about millis() but I cant figure out a way to use this with time.h.

Any ideas?

I didn't quite figured out how the clock will work.

Is it something like:

00:00:00 -> All lights off
12:00:00 -> Blue PWM at 50%
00:15:00 -> green PWM at 25%
00:00:45 -> red PWM at 75%

?

What you may want to do, is to map the clock straight to a PWM value. That way you won't feel the ticking like you do now with your interval coded lights. This, of course, has problems regarding scaling and precision... but read on:

TLC5940 has 12 bits, so 4096 steps.

so for the hours, you'll have 4096/24 = 170,7 increase/hour. So within each hour, you'll have to increase 170 (in odd hours add 170, in even hours add 171). So, in each minute that goes by, you add 170/60 = 2,83.
To minimize the errors, we'll calculate it all together. 4096/(24*60) = 2,84.

For the minutes, 4096/(60*60) = 1,14. So in every second that goes by, you increase the counter by 1. Try to work out intervals and add two to make up for the loss of bits. Or, you may notice that the brightness is too dim in the lower end of the scale, and use kind of a deadband to get rid of it and to make the calculations more integer friendly.

For the seconds, you can do the same. However, you won't be able to increase the rate every milisecond since that may be too demanding, so you'll define 10 milisecond intervals and work out how much you need to add based on that interval.

So, 4096/(60*10) = 6,82. Every 10 ms you add 6,82 or 7. To the seconds counter.

Again, even 10 ms may be too tight for what we want since you have an RTC (I guess) and the TLC to communicate to, but you can make you code in a way that you only update your TLC whenever you need to. If you only changed the seconds pwm value, you don't need to write to the hours and minutes PWM. See what I mean?

Hope to have helped. I don't have a lot of time to write code, or sample of code, but it's a different way of solving the problem that you may want to look at.

No it works like this,

Say its 01:32:52 then it would be lit like this
light 0 1 2 3 4 5 6 7 8 9 10 11
Brightness/color 4095/Blue 2047/Grn 2047/Red

I can post a video if it is not clear.

However this is not what I want, I would prefer is it did this but with a much higher resolution. So as the seconds from 0-5 light 0 gradually gets brighter then darker then moves on to light 1 for 6-10.

Its kind of hard to explain. I want a smooth increase and decrease in brightness while the seconds, minutes, and hours pass. I can not do this with just the time.h library because its most actuate time interval is only seconds. I would think I would need at least milliseconds to accomplish my task.

If I understand your question correctly, I think you could bypass your TimeBrightness array and use some creative mod'ing.

secBrightness = millis() % 5000; //some value between 0 and 5000 that restarts every 5 seconds
if(secBrightness > 2500) secBrightness = 5000 - secBrightness; //after 2.5 seconds start counting down instead of up
secBrightness *= peakFactor;

This would give brightness from 0 to peakFactor*2500 to 0 every 5 seconds and will be very smooth assuming the function gets called at least a few times a second. A similar routine for minutes and hours would also work. I assume the seconds() from time.h is aligned with millis()... that is seconds() changes value exactly when millis() % 1000 = 0.

Also, in my example you get 0 intensity on the time marks and full intensity halfway between time marks. I'll let you figure how to fudge things to have full intensity on the time marks.

int old_sec;
long microtime;
  
  int seconds = second();
  int minutes = minute();
  Serial.println(seconds);

if ( seconds != old_sec ) microtime = millis(); // seconds change "tick"
  
  if(seconds < 7){
    redtime[0]= 4 * ( millis() - microtime ); // adjust to 0 - 4000 range;
  }
  if(seconds >= 5 && seconds < 12){
    redtime[1]= 4 * ( millis() - microtime ); // adjust to 0 - 4000 range;
...............................................
old_sec = seconds;

So, basically create a local (inside loop) counter of milliseconds by subtracting reference created when seconds "tick" from real millis() (which is increasing every time code make new cycle-loop) and wright brightness w/o using index-array.
Not sure how good resolution can get, all depends on length of loop itself in milliseconds, the faster - the better. And it's only for seconds probably, minutes and hours not blinking much.

OP: do you intend for the lights to "hand off" to one another, or just one on at a time? By "hand off" I mean that as soon as light #2 starts to dim light #3 starts to brighten. One at a time would be light #3 does not brighten until #2 is off.

Thank You, Thank You, Thank You!!!!!!

I can't believe I never thought of that before.

Had to change it a little for my uses but here is the final code for seconds.

void NewTime(){
  int seconds = second();
  int minutes = minute();
  long curmicrotime = millis();

  if(seconds > oldsec+4 || (seconds == 0 && seconds != oldsec)){ 
    microtime = millis();
    oldsec = seconds;
  }
  
  if(seconds < 5){
    redtime[0]=(curmicrotime - microtime)/1.2505;
  }
  if(seconds >= 5 && seconds < 10){
    redtime[0]=4000 - (curmicrotime - microtime)/1.25;
    redtime[1]=(curmicrotime - microtime)/1.2505;
  }
  if(seconds >= 10 && seconds < 15){
    redtime[1]=4000 - (curmicrotime - microtime)/1.25;
    redtime[2]=(curmicrotime - microtime)/1.2505;
  }
  if(seconds >= 15 && seconds < 20){
    redtime[2]=4000 - (curmicrotime - microtime)/1.25;
    redtime[3]=(curmicrotime - microtime)/1.2505;
  }
  if(seconds >= 20 && seconds < 25){
    redtime[3]=4000 - (curmicrotime - microtime)/1.25;
    redtime[4]=(curmicrotime - microtime)/1.2505;
  }
  if(seconds >= 25 && seconds < 30){
    redtime[4]=4000 - (curmicrotime - microtime)/1.25;
    redtime[5]=(curmicrotime - microtime)/1.2505;
  }
  if(seconds >= 30 && seconds < 35){
    redtime[5]=4000 - (curmicrotime - microtime)/1.25;
    redtime[6]=(curmicrotime - microtime)/1.2505;
  }
  if(seconds >= 35 && seconds < 40){
    redtime[6]=4000 - (curmicrotime - microtime)/1.25;
    redtime[7]=(curmicrotime - microtime)/1.2505;
  }
  if(seconds >= 40 && seconds < 45){
    redtime[7]=4000 - (curmicrotime - microtime)/1.25;
    redtime[8]=(curmicrotime - microtime)/1.2505;
  }
  if(seconds >= 45 && seconds < 50){
    redtime[8]=4000 - (curmicrotime - microtime)/1.25;
    redtime[9]=(curmicrotime - microtime)/1.2505;
  }
  if(seconds >= 50 && seconds < 55){
    redtime[9]=4000 - (curmicrotime - microtime)/1.25;
    redtime[10]=(curmicrotime - microtime)/1.2505;
  }
  if(seconds >= 55){
    redtime[10]=4000 - (curmicrotime - microtime)/1.25;
    redtime[11]=(curmicrotime - microtime)/1.2505;
  }
  if(oldsec == 0){
    redtime[11]=4000 - (curmicrotime - microtime)/1.25;
  }

Again thanks for the help

Still, can you post a video. I didn't understood how it worked.

Here you go!