Go Down

Topic: Mini Stage Show x 20 (Read 4823 times) previous topic - next topic

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.


jroorda

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.

Chagrin

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.

Code: [Select]
#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:
Code: [Select]
{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.

#3
Aug 13, 2012, 08:09 am Last Edit: Aug 13, 2012, 11:52 pm by Tom-Kristensen Reason: 1
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

Quote
// "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.

Chagrin


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.


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).


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":
Code: [Select]
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)
Code: [Select]
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:
Code: [Select]
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 ;)

#6
Aug 14, 2012, 05:33 am Last Edit: Aug 14, 2012, 05:35 am by Tom-Kristensen Reason: 1
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 code
Code: [Select]
ctr += 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)

Chagrin

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.

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 http://arduino.cc/en/Reference/Scope for an explanation.

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 ;)

Code: [Select]
#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:
Code: [Select]
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:
Quote
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.




marco_c

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.
Arduino libraries http://arduinocode.codeplex.com
Parola for Arduino http://parola.codeplex.com

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 :
Quote
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.

marco_c

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.
Arduino libraries http://arduinocode.codeplex.com
Parola for Arduino http://parola.codeplex.com

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?

marco_c

Excel it is :)

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.
Arduino libraries http://arduinocode.codeplex.com
Parola for Arduino http://parola.codeplex.com

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!

Go Up