Mini Stage Show x 20

I am a print maker working on a set of 20 light boxes in which I will show a set of prints. Each box also has a figurine like an actor on a stage with the print as a backdrop. The box has a manual timer on front that the viewer activates to initiate the lights. After 60 seconds, times up, shows over.

I have set up 12 lighting circuits that will throw LED light around the box much like a miniature stage setting. I have 12 circuits that I would like to control. I would like to be able to use PWM fading and independently turn lights on and off, like a tiny piece of theatre.

I have built the boxes and I have the first one wired up and working with a little lighting desk I have made using a bank of switches and potentiometers to model the light show. Many of the lights pull more than 40ma, so I will be using transistors to assist with current. I will be using 5V to keep the box cool.

Now I need to write code for arduino control. I have never written any code before. I have been reading the Arduino Cookbook and I have an arduino uno starter pack. I have managed to make two leds fade on and off. I have a long way to go...

I would like to set up a general code template that has a 60 second time line in which I can easily insert on/off commands and fade rates for each circuit. From my reading so far, it seems like this is a pretty tricky piece of programming.

Any suggestions will be greatly appreciated.

Usually LED's are considered fairly basic, but when you need to fade various LEDs simultaneously at various rates it can get quite complicated. The first thing I will note is dimming the non-PWM lines will take a bit more work. I'm sure you have found the analogWrite lets you dim an LED, but it only works on the PWM pins. To get around this use a software PWM library. Just searching that should give you some options. I haven’t had to use these, so I cannot endorse a specific implementation.

As for the program itself, there are a lot of ways to could approach this. I will lay out the simplest way for a beginner. There are more advanced techniques that could simplify things, but I want to keep it so that it is very understandable.

First, I would not use any delays. These work fine for one or two LEDs, but once you have multiple dimmers things will go bad fast. Instead make a long variable called startTime. Use the millis() function to store the current number of milliseconds since the start of the program into startTime. Now you can calculate how far you are into your program by calculating (millis()-startTime). You may want to store this value in a variable called runTime.

Next for each LED make a large series of if statements. Here is where you do your light scheduling. For instance:
if(runTime > 1000 && runTime < 5000){
//Code to make light turn on
}
if(runTime > 5000 && runTime < 10000){
//Code to make light turn off
}

Just keep adding if statements to get the full show. To dim the lights just use some math and the map() function. Say if you want a light to turn on over seconds 1-5 you might write the code below in the if statement.

level = map(runTime, 1000,5000,0,255)
analogWrite(pinNum, level)

Feel free to ask for clarification of anything here, but I hope this gets you started.

You're going to kill yourself (or want to kill yourself) if you try to set up a mass of if/then structures to pull this off. It can assuredly be done but it's not very flexible.

#define LED1 3 // assign the first LED to pin 3
#define LED2 5 // assign the second LED to pin 5, etc...
#define LED3 6

// The scene; turn LED1 at 0 milliseconds at 255 (full) brightness, LED2 at 100 milliseconds at full brightness, 
// LED3 at 2000 milliseconds at half brightness, ad nauseum.
// PROGMEM keyword causes the array to be stored in flash (~30KB, shared by the program) vs. SRAM (2KB)
unsigned int scene[] PROGMEM = {LED1, 0, 255, LED2, 1000, 255, LED3, 2000, 128};

void setup(){
}

void loop(){
  // "ctr" holds our position in the scene. ctr + 0 is the LED pin, ctr + 1 is the time, and ctr + 2 is the brightness.
  unsigned int ctr = 0; 
  
  // wait for a button press here to kick off the scene if desired
  unsigned long kickoffTime = millis();

  while (ctr < sizeof(scene) / sizeof(unsigned int)) {
     if (scene[ctr + 1] > millis() - kickoffTime) {
      analogWrite(scene[ctr], scene[ctr + 2]);
      ctr += 3;  // each command is 3 numbers; we jump to the next command by adding 3 to ctr.
    }
  }

  // might want to turn all the LEDs off here at the end of the scene.
}

The scene[] can be as long as your Arduino's memory allows, but that's still a couple thousand changes. 1000 LED brightness changes equates to (1000 * 2 bytes in unsigned int) 2KB of memory; ATMega328 has 32KB memory, this program subtracts ~1KB from that.

How do you do a fade? You'd need to break it down into multiple stages, for example fading LED3 from full on to off over 1 second by adjusting it every 100 milliseconds:

{LED3, 0, 255, LED3, 100, 230, LED3, 200, 205, LED3, 300, 180.....

It's not a perfect fade by the effect should still be fine. You can always break up scene-critical fades into more, finer steps.

Understandably, creating a large scene is going to be a painstaking act without custom scene-creating software. I'd suggest laying it out in a spreadsheet and then exporting that spreadsheet to text. If you're good with spreadsheets you could probably form a graph with the data to show the status of each LED? Also, remember that the scene timings must be in sequential order.

Beyond the code, if you want to be able to fade 20 LEDs then you're going to need more PWM outputs than the Arduino has. There are two popular options: shiftPWM or the TLC5940. The TLC5940 would be more expensive but it can also handle more current (120ma per pin). Lots of tutorials and examples on both if you google a bit.

Thanks Chagrin for putting another option on the table. No doubt a more sophisticated approach which creates a dilemma, I'm not even getting my head around the "basic" approach outlined by Jroorda. But I wont let that stop me weighing up the options. I should restate here that I have 12 circuits that need control, not all of these will need fading, but I take your point that I might need an upgraded board to get more PWM outlets. I was hoping to use transistors to handle the increase current needs of a range of leds. This project is a little different to most in that I am using a variety of leds with different specs including a bit of strip lighting, so each circuit has a different loading. For this reason I am steering clear of multiplexing. I am also using dedicated led colours rather than colour mixing with RGB. So my circuits include 2 meters of strip light on one circuit, 3 x 100ma bright white on another, 3 x 40ma of blue on another.

A spreadsheet is a great idea as you suggest with changes set out sequentially, I understand that the coding needs to track the millis since kickoffTime - which is triggered by power supplied by a manual timer. I will try to get my head around the ctr set-up, which does seem very efficient. I think I should be able to code for 12 circuits each with a different start time, end time and fade values over a 60 second period. Lets say each circuit is only fired up once over the minute to keep things simple. Gotta be doable.

I also take your point about wanting to kill myself somewhere inside the coding, not yet though... I think I need to get some basic control over a couple of leds over a 60 second time line so I can at least claim to have tried to do some coding before I beg for mercy.

OK, I'm wrestling with the code provided by Chagrin - see earlier post. In a nutshell the control over an array of leds is achieved by coding for "scenes" using the "ctr" command

// "ctr" holds our position in the scene. ctr + 0 is the LED pin, ctr + 1 is the time, and ctr + 2 is the brightness.

My basic question is how do I set out my scene commands? If each change must be listed sequentially then I would need to rank commands by increasing values of ctr + 1.

The first command is effectively the unsigned int scene which sets out the time delay for each led before it is turned on, does this mean I cannot program a change till the int scene has fully played out?

To be honest i have no idea where or how to plug my change values into the code set-up. A small piece of example code for a couple of changes would probably help me out.

Tom-Kristensen:
My basic question is how do I set out my scene commands? If each change must be listed sequentially then I would need to rank commands by increasing values of ctr + 1.

Correct. I did mention that "timings must be in sequential order". Going back to my spreadsheet idea for laying out the scene it shouldn't be too hard to handle, and you can always sort the rows of commands based on the time. Practically, if you tried laying out your scene in a non-sequential order I think you'd go batty trying to arrange everything.

Tom-Kristensen:
The first command is effectively the unsigned int scene which sets out the time delay for each led before it is turned on, does this mean I cannot program a change till the int scene has fully played out?

Just to get the nomenclature correct, scene[] is an array (a list) of unsigned ints. As an aside, an unsigned int is 0 to 65535 -- this has the repercussion that you cannot have actions occurring past the 65.536 second mark without changing to a different data type.

I'm not sure what you mean by "being unable to program a change". My understanding is that you would program this scene for the box and then ship it off so multiple visitors could play the 60 second script over and over. If you need to change the scene you'll need to pull it out and reprogram it with your computer.

If, on the other hand, you were confused by the "time delay" you should rather think of it as when the action occurs -- "when" being based from the start of the scene. It doesn't specify a duration, but rather just what happens at that moment in time. You effectively set the duration by later adding another command to change the state of the LED (turn it back off, dim it slightly, etc.). It's just like managing kids; you have to tell them they can watch TV at 8pm and later tell them to stop watching TV at 9pm. If you tried telling them at 8pm that they can watch TV for an hour... well that just never works. That would require more advanced kids (or a more advanced program).

Tom-Kristensen:
To be honest i have no idea where or how to plug my change values into the code set-up. A small piece of example code for a couple of changes would probably help me out.

Crack open a spreadsheet and label the spreadsheet columns A, B, and C "light circuit", "time", and "brightness":

Circuit	Time	Brightness
		
LED1	0	255
LED2	0	255
LED3	0	255
LED1	1000	0
LED2	2000	0
LED3	3000	230
LED3	3100	205
LED3	3200	180
LED3	3300	155
LED3	3400	130
LED3	3500	105
LED3	3600	80
LED3	3700	55
LED3	3800	30
LED3	3900	5
LED3	4000	0

Going over again how this works, just read the list from top to bottom. E. g. at time 0 LED1 is set to 255 brightness, also at time 0 LED2 is set to 255 brightness... etc.

Now take those numbers and turn them into one long line separated by commas (hint: export to CSV and modify with a text editor)

LED1,0,255,LED2,0,255,LED3,0,255,LED1,1000,0,LED2,2000,0,LED3,3000,230,LED3,3100,205,LED3,3200,180,LED3,3300,155,LED3,3400,130,LED3,3500,105,LED3,3600,80,LED3,3700,55,LED3,3800,30,LED3,3900,5,LED3,4000,0

Finally, wrap them with all the program stuff:

unsigned int scene[] PROGMEM {LED1,0,255,LED2,0,255,LED3,0,255,LED1,1000,0,LED2,2000,0,LED3,3000,230,LED3,3100,205,LED3,3200,180,LED3,3300,155,LED3,3400,130,LED3,3500,105,LED3,3600,80,LED3,3700,55,LED3,3800,30,LED3,3900,5,LED3,4000,0};

Now paste that into the Arduino sketch, replacing the existing scene[] line. Upload it to the Arduino... done. And always correct the first time of course :wink:

Amazing. I am really grateful for your work Chagrin. This has cleared up a lot of mysteries for me. But I have to say it is still mysterious. I shall keep studying...

Thank you for clarifying that a scene is actually the entire lightshow. I was indeed thinking that programming a change was just advancing one set of values along the scene string.
I am also kinda lucky that this programming approach using unsigned int values will work only for 65 seconds, given that I want a 60 second light show. I was not intending to "end" the light show but let it die when the manual timer expires. Most people will probably not dial up the full 60 seconds until they learn that more time dialed up extends the show. So, there will be a lot of dying light shows happening. Likewise there will not be a push button start to the show, just power on to the arduino.

I would never have guessed that the entire light show is set out as one long string of commands. I was under the misapprehension that each new command required some active recourse to this line of codectr += 3;  // each command is 3 numbers; we jump to the next command by adding 3 to ctr.But, I now see that the ctr counter clocks forward with each set of 3 values in the scene (I think I see).

Another thing that surprises me is that the entire scene is to be set out in the section of code that defines the constants for the program ie before the void setup. Is this really right?

Of course I have set up a bread board with LEDs connected to the uno board, tested with simple fade exercise - all in readiness to start beaming back the code, and of course I am going to be pretty overjoyed when that finally happens, but so far no joy. The compiler is perfectly happy with the initial code that you outlined, it verifies and uploads, but no lights. Interestingly When I tried to replace the early short scene string with your later longer string the compiler was not happy: attributes are not allowed on a function definition. Hmmm, I guess I better experiment with moving the string around. I see some knots coming up.

Thanks again, I'm having fun while learning. (agonising fun)

After actually wiring it up, it looks like the PROGMEM keyword is causing problems; I'm not sure why. For now, remove it. Hopefully someone else will chime in as to the issue.

Tom-Kristensen:
Another thing that surprises me is that the entire scene is to be set out in the section of code that defines the constants for the program ie before the void setup. Is this really right?

Yes that's correct. See scope - Arduino Reference for an explanation.

Tom-Kristensen:
Interestingly When I tried to replace the early short scene string with your later longer string the compiler was not happy: attributes are not allowed on a function definition. Hmmm, I guess I better experiment with moving the string around. I see some knots coming up.

I made a mistake and left out the "=" sign. That should be unsigned int scene[] = {LED1...

Reposting the code, and now guaranteeing that it will work :wink:

#define LED1 3 // assign the first LED to pin 3
#define LED2 5 // assign the second LED to pin 5, etc...
#define LED3 6

unsigned int scene[] = {LED1,0,255,LED2,0,255,LED3,0,255,LED1,1000,0,LED2,2000,0,LED3,3000,230,LED3,3100,205,LED3,3200,180,LED3,3300,155,LED3,3400,130,LED3,3500,105,LED3,3600,80,LED3,3700,55,LED3,3800,30,LED3,3900,5,LED3,4000,0};

void setup(){
}

void loop(){
  unsigned int ctr = 0;
  
  // wait for a button press here to kick off the scene if desired
  unsigned long kickoffTime = millis();

  while (ctr < sizeof(scene) / sizeof(unsigned int)) {
     if (scene[ctr + 1] <= millis() - kickoffTime) {
      analogWrite(scene[ctr], scene[ctr + 2]);
      ctr += 3;  // each command is 3 unsigned ints; we jump to the next command.
    }
  }
  
  while (millis() - kickoffTime < 60000)  {} // idle loop; wait for 60 seconds to expire if not already.
}

With this scene all of the LEDs should turn on, then two turn off a second later and a second apart, and the third fades out. The entire scene should replay, repeatedly, every 60 seconds.

Thank you Chagrin your latest code works like a dream - to a point. The story so far
I have 12 LED circuits to control independently with on/off and fade up/down over a scene running for 60 seconds. The code sets out the change commands as a string of triple values, where the first value is the LEDpin, then time in millis, then brightness level. This string of values is written up sequentially and declared as a local variable prior to set-up and loop functions. A ctr - counter is set running as a loop function to read the command string. The code is very neat for turning the LEDs on and off and executing a rough fade. Any work done on the fades quickly produces a fiendishly long set of commands. If two lights need to cross-fade, a common theatrical device, the commands become interwoven. Even if these commands are managed in a spreadsheet and uploaded using friendly software the string might bust a memory barrier, and reading it is beyond fun. But there is another consideration:

Problem I am using LED lighting to mimic stage lighting in miniature. In the olden days lights onstage were dimmed by pushing a fader that choked off the voltage running to the light producing dimming. I have modeled this effect using a bank of potentimeters hooked up to my 12 light circuits. Works a treat. When I started experimenting with PWM fading I could see that a linear fade of brightness level from 255 to 0 was not the same as a smooth fade made with the potentiometer. The PWM fade seems faster at the end and dies off with a flicker. One solution to this is to set up fades using a sine function as in this example:

int value, value2 ;
int ledpin = 9;                           // light connected to digital pin 9
int ledpin2 = 11;                           // light connected to digital pin 11
long time=0;

int periode = 10000;
int displace = 5000;

void setup()
{
  // nothing for setup
}

void loop()
{
  time = millis();
  value = 128+127*cos(2*PI/periode*time);
  value2 = 128+127*cos(2*PI/periode*(displace-time));
  analogWrite(ledpin, value);           // sets the value (range from 0 to 255)
  analogWrite(ledpin2, value2);           // sets the value (range from 0 to 255)
}

This makes the light fade more slowly in the dying stages and it looks smooth. I don't think I can achieve this style of fading with a command string, plugging in all those itsy-bitsy sine values would be a nightmare... unless...

Possible solution For my benefit Chagrin describes the command string like this:

It's just like managing kids; you have to tell them they can watch TV at 8pm and later tell them to stop watching TV at 9pm. If you tried telling them at 8pm that they can watch TV for an hour... well that just never works. That would require more advanced kids (or a more advanced program).

Would it not be possible to give each kid an alarm clock at 8pm set so that the youngest kid has to stop watching TV at 9pm while the older kid can stay up till 11? The clock being a sine function that works like the drooping eyelids of a sleepy kid. Can I insert sine functions into the command string? Better still, a little node that refers to a standard type of fade, ie 10 seconds, full on to off using a sine wave. Or 5 seconds full on to 50% brightness linear fade in increments of 5. Given that I have 20 lightboxes to program I need to find some cut and paste solutions that I can easily shove in and out of my string of commands. Greedy, I know.

If you can define a mathematical function for the fade then you can chanhe the lookup table to be just for the parameters to drive your function. This makes it more compact, but less flexible, as the work is done in the calculations in real time rather than precalculated in a table.

Thanks Marco. So you seem to be suggesting that I should persevere with the long string which we call a "lookup table", reason being that it will actually be more efficient than asking the microprocessor to chug through a sine function at every interval of the fade. I wonder if one sine calculation might slow down other actions, ie could one achieve the cross-fade - two independent fades working at the same time? This sends me back to an earlier suggestion by Chagrin :

Understandably, creating a large scene is going to be a painstaking act without custom scene-creating software. I'd suggest laying it out in a spreadsheet and then exporting that spreadsheet to text. If you're good with spreadsheets you could probably form a graph with the data to show the status of each LED? Also, remember that the scene timings must be in sequential order.

So, I now need to come to grips with writing spreadsheets and exporting them to text - constructing the lookup table. This I then paste into the sketch (regardless of the fact that the file will be absolutely enormous). I just need to check that this is in fact the best way forward.

If you are planning to use the same fading profile for all the LEDs (for example the sine function), then you can store the profile just once rather than repeating it every time for every LED. If the LEDs are out of phase (ie start at different times or places in the profile), all it means is that each LED is just at a different point in the profile, but the data is the same.

Clearly this is extensible to a few profiles, as long as it is not per individual LED, which is what you have now.

Easier to do than explain, so I hope this is clear.

Thanks Marco. I take what you mean by having data stored for a few different fade profiles lets say, up, down, long & short. The problem arises that these profiles will need to be woven into the time line with other commands for different circuits. Every command group of 3 variables, led number, time, & brightness must be ranked sequentially by time. My understanding is that each point in time will need to be physically entered into the lookup table. So if there are two fades happening at once - the cross fade, they will need to be stitched together. Or am I wrong here? If other commands are to happen during a fade I suppose they will just be inserted into the code like a needle in a haystack.

This is definitely doable. I suppose I better get good at spreadsheets. Any tips on becoming a spreadsheet manager? I can handle Excel, is that what we are talking about?

Excel it is :slight_smile:

To clarify my understanding of a crossfade - one light getting brighter while the other getting darker?

If so, you can also have similar effects like fading light chasers all off the same table. A sine profile is a continuous wave. We are quantising (or digitizing) the wave by representing the profile in a table using discrete values. The position along the wave (where you start) dictates the profile from then on. At the any time interval, you move into the next 'position' of the time axis by going to the next calculated value inthe table. Interestingly, you can have different spaced time intervals to cope with the variability that you get near the crest and the trough of the wave.

The key is not to think that you have to start all the LEDs at the start of the table. Where you start is where you want it to start. So for a crossfade the way I understand it, you might start one LED at the lowest point of the sine wave and the other at the highest and then move from there. For a LED chaser, you start each LED 'offset' in time in the lookup table. Then progress each one from there to get the 'bump' moving along the LEDs. All this still the same lookup table, just used differently in code.

Handling lots of lights is easy - don't get hung up on that. Really be clear about what you want and then code can be designed to do anything. Going into an exercise worried about the implementation, especially if you are not too confident with the technology, is the wrong approach. Be clear about the interaction of the lights, people, buttons, sensors, etc. The best designs are really optimised to the stated requirements and adding stuff later that was crucial often won't work as well as planning for it in the first place.

Yep, the cross fade is one light dimming while another fades up. In a theatrical use this allows you to make transitions without the viewer quite realising what is happening. One can merge from green to blue or one could merge from left to right, front to back etc. Lights in a theatre, or a rock show, are set up for this kind of thing, and yes, they now run on programmable desks. The lighting director sets up the light to taste for scene one, levels are set manually on the faders on the light desk and this scene is stored in memory. Scene two is set up and so on. When the show is in progress one entire scene can fade down while another fades up. It looks smoother than just switching everything off and on. While the scene, or song, is playing the lights can also be faded up or down to suit, usually this is a manual operation, but of course everything can be automated.

This is the kind of creative latitude I am trying to model - a bunch of lights that I can play with, like a mini theatre. I am pretty clear about what I want the lights to do now, but I will no doubt change my mind when I install a new print in my light box. I do have 20 boxes built, so each will need a different light show. I do need adaptable code. As for sensors these might be nice to include in the show at some time, but I think I better not go there for now.

Getting back to the code... I like the idea of the sign wave embedded throughout the duration of the lightshow, but I think the wave would have to be set to a specific periodicity, that is 10 seconds between peaks for a fade that is to take 10 seconds. For a faster fade you would need another wave. Or are we talking about higher order mathematics?

My real problem is where all this wave action takes place. My understanding is that we are now talking about generating values for a lookup table that is being constructed in Excel. So the programming challenge has moved from the Arduino board into Excel. Which might mean I have worked myself into a calculus forum. Gasp! Ideally there would be a program that one plugs with the required on and off times fade durations and the lookup table is generated automatically. I need me a magic box!

As long as you have more samples than you need in the table you can squeeze the time base.

A sine wave is very simple mathematically as y=h*sin(x). X varies from 0 to 360 degrees and then the wave repeats, so no need to work out any more. The h for the arduino is 255 as there is no need for a bigger number. The only real variable is the step in the degrees that you want to hold and with 5 degree difference between points we get a table of 72 data points. In 10 seconds that makes it 7.2 changes to the led in 1 second, easily done.

If you only want 5 seconds, then you just take every other value in the table or have more transitions per second.

This version allows basic dimming/brightnessing(?) of any LED and in combination with any other LEDs (i.e. it allows crossfading). I have the dimming defined by a "dimrate" or how much to change the brightness every MILLISPERDIMSTEP millis. So, a dimrate of "10" means it increases brightness by 10 every 10 milliseconds. Not the most flexible method but y'know.

I checked memory usage and it is has 1752 bytes free with this size of a scene. Each scene command uses 6 bytes. Plenty of space left for a larger scene.

#define LED1 3 // assign the first LED to pin 3
#define LED2 5 // assign the second LED to pin 5, etc...
#define LED3 6

#define MAXPINNUMBER 6 // LED3 is the highest pin number -- number 6

#define MILLISPERDIMSTEP 10 // LED dimming is recalculated every X millis

// Group LED commands into a structure (struct)
struct command {
  byte pin;          // 0 to 255
  unsigned int time; // 0 to 65535
  byte brightness;   // 0 to 255
  char dimrate;      // -127 to 128
};

// The stored state of each LED; holds state of dimming.
struct state {
  unsigned int time;
  byte brightness;
  byte dimrate;
};

// Create the scene which is an array of "command" structs
struct command scene[] = {
  {LED1,0,255,0},
  {LED2,0,255,0},
  {LED3,0,255,0},
  {LED1,1000,0,0},
  {LED2,2000,0,0},
  {LED3,3000,0,10},
  {LED3,6000,255,-10},
  {LED3,9000,0,1},
  {LED3,12000,255,-1}
};

// Initialize the LED states to off. Note 0 to MAXPINNUMBER is (MAXPINNUMBER + 1) states.
struct state ledState[MAXPINNUMBER + 1] = {{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}};

void setup(){
}

void loop(){
  static unsigned long kickoff = 0;
  static unsigned int sceneCtr = 0;
  byte ledCtr = 0;

  // wait for a button press here to kick off the scene if desired

  // Check the scene to see if we have a new command
  if (sceneCtr < sizeof(scene) / sizeof(command)) {
    if (scene[sceneCtr].time <= millis() - kickoff) {
      analogWrite(scene[sceneCtr].pin, scene[sceneCtr].brightness);
      if (scene[sceneCtr].dimrate != 0) {
        // It dims; we have to add this to our ledState array and keep track of it.
        ledState[scene[sceneCtr].pin].time = scene[sceneCtr].time + MILLISPERDIMSTEP;
        ledState[scene[sceneCtr].pin].brightness = scene[sceneCtr].brightness;
        ledState[scene[sceneCtr].pin].dimrate = scene[sceneCtr].dimrate;         
      }
      sceneCtr += 1;
    }
  }
  
  // Here we process through all the LEDs in a dimming state
  for (ledCtr = 0; ledCtr < MAXPINNUMBER + 1; ledCtr++) {
    if (ledState[ledCtr].dimrate != 0) { // only LEDs with a dimming are messed with.
      if (ledState[ledCtr].time <= millis() - kickoff) {
        if (ledState[ledCtr].brightness + (char) ledState[ledCtr].dimrate >= 0 &&
            ledState[ledCtr].brightness + (char) ledState[ledCtr].dimrate <= 255) {
          ledState[ledCtr].brightness += (char) ledState[ledCtr].dimrate;
          ledState[ledCtr].time += MILLISPERDIMSTEP; // mark the time of next LED adjustment
        } else if (ledState[ledCtr].brightness + (char) ledState[ledCtr].dimrate < 0) {
          // need to stop at zero and not a negative number.
          ledState[ledCtr].brightness = 0;
          ledState[ledCtr].dimrate = 0; // zero dimrate so we stop dimming it
        } else {
          // need to stop at 255.
          ledState[ledCtr].brightness = 255;
          ledState[ledCtr].dimrate = 0; // zero dimrate.
        }
        analogWrite(ledCtr, ledState[ledCtr].brightness);
      } 
    }
  }
  
  // Have 60 seconds elapsed? We need to restart if so.
  if (millis() - kickoff > 60000) {
    kickoff = millis();
    sceneCtr = 0;
  }
}

Marco, I opened an Excel spreadsheet and got stuck into applying the sine wave fomula to produce a set of values to generate a lookup table. I didn't get too far before I decided to go back to basics and just produce a full cross fade of two LEDs jumping by 5 brightness points every 100 milliseconds. One led fading from 255 - 0. the other brightening from 0 - 255. Testing out my rusty Excel skills I came to see that it is not such a big deal to make these tables. I produced a matrix of 101 commands with 3 values per line. I then saved this as a CVC file and pasted it into the sketch built by Chagrin. Worked fine. Happy to post the code except that it is just a boring list of numbers that would produce a box that runs down to the floor.

Watching my slow 10 second cross fade I came to realise that there is more to producing a smooth fade than trying to apply a sine function. The brightness scale of 0 - 255 seems like it should produce a linear increase in brightness with each increasing step, but the perception of brightness is non-linear. The eye is far more sensitive to incremental changes to a low level light source than to a bright light source. So, ideally we would be applying an exponent other than sine to get the classic exponential growth curve - and even I can see that this is not going to happen.

But it was fun thinking about!

BREAKING NEWS The most exciting LED code ever written has arrived overnight from Chagrin.

I used to hate LED lighting. Its too bright for a start and then when used as dynamic decorative lighting the programmer makes something that would induce epilepsy. I don't want to dump on Christmas, but when I was a kid when we had actual candles on the tree and they looked beautiful. Like LED christmas lights, most arduino LED sketches are not pleasant to watch IMO. But LED is very reliable and low cost and the colours are pretty cool, best of all it is programmable with PWM. Or so I thought until I tried to find some code that would allow me to gently dim and brighten a bank of 12 lights, any lights at any time. No such code. Until now!

Too busy playing with my lights now. More later. Thank you Chagrin - there is a Santa after all.

I've been playing with a bank of 3 LEDs all day, on and off. The Chagrin code (that's an official code name) is excellent. I am able to do everything I was hoping for. Slow fades, fast fades, cross fades and best of all its all extremely flexible and simple to control. I really recommend that people try this code, it has to be the best thing yet for running LEDs in a controlled fashion.

I am not sure about limitations to the program. There is a time limit imposed by the unsigned int time of 65535 milliseconds which gives a time line of 65 seconds, but perhaps this could be extended with another board. In any case there is a loop function so lights keep running. Likewise I am not yet sure of the PWM capabilities of the Uno board when using a larger bank of LEDs. I am using pretty hefty 10mm LEDs that draw at least 20mA - sorry no means to measure this - and I am seeing some fluttering on particular dimming sequences. Perhaps this is a function of the hardware rather than the code, but the nice thing is that a dodgy fade can be corrected by tweaking the fade parameters.

I would be keen to hear from other who might try the code. My next exercise is to add more lights and see what happens. Might order a bigger board too. Having fun now.