Color Fading Mode Switching

I’m working on a sort of DMX Lighting Controller, with several different outputs. One of which is a RGB fading program. I’ve got the fading part figured out, thanks to some code I found on here. However, I want to be able to turn it on and off. I discovered this other framework for mode switching. The problem I’m having, is that while I can get the fade to start with the mode (1) button press, after pressing the button for a mode (2) that turns the fade off, the fade doesn’t stop until the fading sequence/function is complete. How can I interrupt the crossfade at any point, and switch modes? I’d also like to be able to process other button inputs that control things unrelated to the fading while the crossfade is running, if possible. Thanks for all your help!

(Sorry, had to cut some in-code documentation to fit the limit, I’ve attached the full file. )

#include <Conceptinetics.h>

boolean button[]= {3,4,5,6,7}; //pin assignment for buttons

boolean buttonstate = 0;
int mode = 0;

// Color arrays
int black[3]  = { 0, 0, 0 };
int white[3]  = { 100, 100, 100 };
int red[3]    = { 100, 0, 0 };
int green[3]  = { 0, 100, 0 };
int blue[3]   = { 0, 0, 100 };
int yellow[3] = { 100, 50, 0 };
int orange[3] = { 100, 25, 0 };
int purple[3] = { 80, 0, 100};
int pink [3]  = { 100, 0, 45};
int teal [3]  = { 0, 70, 100};
//int dimWhite[3] = { 30, 30, 30 };
// etc.

// Set initial color
int redVal = black[0];
int grnVal = black[1]; 
int bluVal = black[2];

int wait = 5;      // 10ms internal crossFade delay; increase for slower fades
int hold = 200;       // Optional hold when a color is complete, before the next crossFade
int DEBUG = 1;      // DEBUG counter; if set to 1, will write values back via serial
int loopCount = 60; // How often should DEBUG report?
int repeat = 0;     // How many times should we loop before stopping? (0 for no stop)
int j = 0;          // Loop counter for repeat

// Initialize color variables
int prevR = redVal;
int prevG = grnVal;
int prevB = bluVal;

DMX_Master dmx_master (512,2);  //DMX Initialize

  void setup()
  {
  for(int x=0; x<5; x++) //starts a loop to set the pin modes for all button pins
  {
    pinMode(button[x], INPUT);
  }
  for(int x=0; x<5; x++) //starts a loop to set the buttons to a default of HIGH
  {
    pinMode(button[x], LOW);
  }
   pinMode (2,OUTPUT);     //This is for the DMX Shield
   dmx_master.enable ();
  }
 
  void loop(){
  
    buttoncheck(); //calls to the void buttoncheck() section for checking the buttons
    if(mode == 1)
    {
      mode1(); //directs teh program to teh section with the flashes you selected
    }
    if(mode == 2)
    {
      mode2(); //directs teh program to teh section with the flashes you selected
    }
    if(mode == 3)
    {
      mode3(); //directs teh program to teh section with the flashes you selected
    }
    if(mode == 4)
    {
      mode4(); //directs teh program to teh section with the flashes you selected
    }
    if(mode == 5)
    {
      mode5(); //directs teh program to teh section with the flashes you selected
    }   
  }
 
  void buttoncheck()
  {
    for(int x=0; x<5; x++) //loop for checking all the buttons
    {
      buttonstate = digitalRead(button[x]);
      if(buttonstate == HIGH && button[x] == 3) //number equals the buttoon the pin is connected too
      {
        mode = 1; // number equals the mode you want to run
      }
      if(buttonstate == HIGH && button[x] == 4)
      {
        mode = 2;
      }
      if(buttonstate == HIGH && button[x] == 5)
      {
        mode = 3;
      }
      if(buttonstate == HIGH && button[x] == 6)
      {
        mode = 4;
      }
      if(buttonstate == HIGH && button[x] == 7)
      {
        mode = 5;
      }
    }
  }
 
  void mode1() //RGB FADE
  {

    buttoncheck();
    
    crossFade(red);
    buttoncheck();
    crossFade(orange);
    buttoncheck();
    crossFade(yellow);
    buttoncheck();
    crossFade(green);
    buttoncheck();
    crossFade(teal);
    buttoncheck();
    crossFade(blue);
    buttoncheck();
    crossFade(purple);
    buttoncheck();
    crossFade(pink);
    
    buttoncheck();
  
    if (repeat) { // Do we loop a finite number of times?
      j += 1;
      if (j >= repeat) { // Are we there yet?
        exit(j);         // If so, stop.
      }
    }

  }
 
  void mode2() //BLACKOUT
  { 
  
    buttoncheck();
    dmx_master.setChannelValue (1,0);
    dmx_master.setChannelValue (2,0);
    dmx_master.setChannelValue (3,0);
    buttoncheck();
        
  }
 
  void mode3() //ALL@FULL
  {
 
    buttoncheck();
    dmx_master.setChannelValue (1,255);
    dmx_master.setChannelValue (2,255);
    dmx_master.setChannelValue (3,255);
    buttoncheck();
  } 
  void mode4()
  {
          
  }
 
  void mode5()
  {
            
  }
  
//LED FADE PROGRAM ...................................................................................
//...
//April 2007, Clay Shirky <clay.shirky@nyu.edu> 

  

  int calculateStep(int prevValue, int endValue) {
  int step = endValue - prevValue; // What's the overall gap?
  if (step) {                      // If its non-zero, 
  step = 1020/step;              //   divide by 1020
  } 
  return step;
  }

  /* The next function is calculateVal. When the loop value, i,
  *  reaches the step size appropriate for one of the
  *  colors, it increases or decreases the value of that color by 1. 
  *  (R, G, and B are each calculated separately.)
  */

  int calculateVal(int step, int val, int i) 
  {

  if ((step) && i % step == 0) { // If step is non-zero and its time to change a value,
    if (step > 0) {              //   increment the value if step is positive...
      val += 1;           
    } 
    else if (step < 0) {         //   ...or decrement it if step is negative
      val -= 1;
    } 
  }
 
  // Defensive driving: make sure val stays in the range 0-255
  if (val > 255) 
  {
    val = 255;
  } 
  else if (val < 0) {
    val = 0;
  }
  return val;
  }

  /* crossFade() converts the percentage colors to a 
  *  0-255 range, then loops 1020 times, checking to see if  
  *  the value needs to be updated each time, then writing  
  *  the color values to the correct pins.
  */

  void crossFade(int color[3])
  {
  // Convert to 0-255
  int R = (color[0] * 255) / 100;
  int G = (color[1] * 255) / 100;
  int B = (color[2] * 255) / 100;
  
  int stepR = calculateStep(prevR, R);
  int stepG = calculateStep(prevG, G); 
  int stepB = calculateStep(prevB, B);
  
  
    

  for (int i = 0; i <= 1020; i++) {
    redVal = calculateVal(stepR, redVal, i);
    grnVal = calculateVal(stepG, grnVal, i);
    bluVal = calculateVal(stepB, bluVal, i);
    buttoncheck();
    dmx_master.setChannelValue (1, redVal);   // Write current values to LED pins (DMX Channels)
    dmx_master.setChannelValue (2, grnVal);      
    dmx_master.setChannelValue (3, bluVal); 
    
   
    

    delay(wait); // Pause for 'wait' milliseconds before resuming the loop

    
  }

  // Update current values for next loop
  prevR = redVal; 
  prevG = grnVal; 
  prevB = bluVal;
  delay(hold); // Pause for optional 'wait' milliseconds before resuming the loop
  }

console_modes.ino (9.98 KB)

the fade doesn't stop until the fading sequence/function is complete.

Do you, while the fading is happening, read the switch state?

I see that you do. But, then, you don't give a shit whether a switch was pressed, or not, so the calls to buttoncheck() are useless.

By the way, have you noticed that the functions are called pinMode, digitalRead, etc. Not, pinmode, digitalread, etc. Ever wonder why?

So, you're saying I should add this mode change code

    if(mode == 1)
    {
      mode1(); //directs teh program to teh section with the flashes you selected
    }
    if(mode == 2)
    {
      mode2(); //directs teh program to teh section with the flashes you selected
    }
    if(mode == 3)
    {
      mode3(); //directs teh program to teh section with the flashes you selected
    }
    if(mode == 4)
    {
      mode4(); //directs teh program to teh section with the flashes you selected
    }
    if(mode == 5)
    {
      mode5(); //directs teh program to teh section with the flashes you selected
    }

after every call to buttoncheck()? And what are you getting at with the function names? Are you suggesting I call it "buttonCheck"? Does that affect the operation at all or is it just for convention's sake?

So, you're saying I should add this mode change code

NO!

In each mode function, you call buttoncheck(), but you don't end the mode when a switch is pressed. Why not?

Your buttoncheck() needs some work. It should return a value - the switch that was pressed. It should NOT diddle a global variable. Every time you call it, you should take action on what it returns. In most cases, that simply means returning from the function in which you call it.

First off, thanks for all your help, I'm pretty new at this. I understand what you're saying, but I don't know how to implement it. Could you please go into a bit more detail on how to rework buttonCheck()? Also, how would I end the modes?

Could you please go into a bit more detail on how to rework buttonCheck()?

Sure.

  void buttoncheck()
  {
    for(int x=0; x<5; x++) //loop for checking all the buttons
    {
      buttonstate = digitalRead(button[x]);
      if(buttonstate == HIGH && button[x] == 3) //number equals the buttoon the pin is connected too
      {
        mode = 1; // number equals the mode you want to run
      }
      if(buttonstate == HIGH && button[x] == 4)
      {
        mode = 2;
      }
      if(buttonstate == HIGH && button[x] == 5)
      {
        mode = 3;
      }
      if(buttonstate == HIGH && button[x] == 6)
      {
        mode = 4;
      }
      if(buttonstate == HIGH && button[x] == 7)
      {
        mode = 5;
      }
    }
  }

should be, in my opinion:

 int buttonCheck()
{
   int whichWasPressed = -1;
   for(int x=0; x<5; x++) //loop for checking all the buttons
   {
      int buttonstate = digitalRead(button[x]);
      if(buttonstate == HIGH)
      {
          whichWasPressed = x;
      }
   }
   return whichWasPressed;
}

The only global variable needed is the array of pin numbers. What is returned is the index into the array that corresponds to the switch that was pressed, or -1 if no switch was pressed.

Which switch was pressed affects mode, but NOT in buttonCheck() (notice that I changed its name to camelCase).

In loop(), then, use:

int mode = buttonCheck();

Again. mode does not need to be global. It is used only in loop().

Then, in mode1(), for instance, you can use:

  void mode1() //RGB FADE
  {
    crossFade(red);
    if(buttonCheck() > 0) return;

    crossFade(orange);
    if(buttonCheck() > 0) return;

    // etc.

The time between calling buttonCheck() in the modeN() functions and calling it again in loop() will be pretty short, so it should return the same value from both calls.

If not, you’d need to make modeN() return the value that buttonCheck() returns, and rework how loop() works. But, test it with these changes (in all modeN() functions, first.

Here’s what I got:

#include <Conceptinetics.h>

boolean button[]= {3,4,5,6,7}; //pin assignment for buttons

boolean buttonstate = 0;


// Color arrays
int black[3]  = { 0, 0, 0 };
int white[3]  = { 100, 100, 100 };
int red[3]    = { 100, 0, 0 };
int green[3]  = { 0, 100, 0 };
int blue[3]   = { 0, 0, 100 };
int yellow[3] = { 100, 50, 0 };
int orange[3] = { 100, 25, 0 };
int purple[3] = { 80, 0, 100};
int pink [3]  = { 100, 0, 45};
int teal [3]  = { 0, 70, 100};

// Set initial color
int redVal = black[0];
int grnVal = black[1]; 
int bluVal = black[2];

int wait = 5;      // 10ms internal crossFade delay; increase for slower fades
int hold = 200;       // Optional hold when a color is complete, before the next crossFade
int DEBUG = 1;      // DEBUG counter; if set to 1, will write values back via serial
int loopCount = 60; // How often should DEBUG report?
int repeat = 0;     // How many times should we loop before stopping? (0 for no stop)
int j = 0;          // Loop counter for repeat

// Initialize color variables
int prevR = redVal;
int prevG = grnVal;
int prevB = bluVal;

DMX_Master dmx_master (512,2);  //DMX Initialize

  void setup()
  {
  for(int x=0; x<5; x++) //starts a loop to set the pin modes for all button pins
  {
    pinMode(button[x], INPUT);
  }
  for(int x=0; x<5; x++) //starts a loop to set the buttons to a default of HIGH
  {
    pinMode(button[x], LOW);
  }
   pinMode (2,OUTPUT);     //This is for the DMX Shield
   dmx_master.enable ();
  }
 
  void loop()
  {
    
    buttonCheck(); //calls to the void buttonCheck() section for checking the buttons
    
    int mode = buttonCheck();
    
    if(mode == 1)
    {
      mode1(); //directs the program to the mode
    }
    if(mode == 2)
    {
      mode2(); //directs the program to the mode
    }
    if(mode == 3)
    {
      mode3(); //directs the program to the mode
    }
    if(mode == 4)
    {
      mode4(); //directs the program to the mode
    }
    if(mode == 5)
    {
      mode5(); //directs the program to the mode
    }   
  }
 
 int buttonCheck()
 {
   int whichWasPressed = -1;
   for(int x=0; x<5; x++) //loop for checking all the buttons
   {
      int buttonstate = digitalRead(button[x]);
      if(buttonstate == HIGH)
      {
          whichWasPressed = x;
      }
   }
   return whichWasPressed;
 }
 
  void mode1() //RGB FADE
  {
    //flash your LEDs how you want in here for this mode
    //a call to buttonCheck can be added in anywhere you want
    //so that the program can continue to check for a button
    //press even while it's in the middle of the mode.
    //When a button is pressed it stores what mode it needs to
    //go to next and will move to that mode once the sequence is over.
    if(buttonCheck()>0) return;
    
    crossFade(red);
    if(buttonCheck()>0) return;
    crossFade(orange);
    if(buttonCheck()>0) return;
    crossFade(yellow);
    if(buttonCheck()>0) return;
    crossFade(green);
    if(buttonCheck()>0) return;
    crossFade(teal);
    if(buttonCheck()>0) return;
    crossFade(blue);
    if(buttonCheck()>0) return;
    crossFade(purple);
    if(buttonCheck()>0) return;
    crossFade(pink);
    
    if(buttonCheck()>0) return;
  
    if (repeat) { // Do we loop a finite number of times?
      j += 1;
      if (j >= repeat) { // Are we there yet?
        exit(j);         // If so, stop.
      }
    }

  }
 
  void mode2() //BLACKOUT
  { 
    //flash your LEDs how you want in here for this mode
    //a call to buttonCheck can be added in anywhere you want
    //so that the program can continue to check for a button
    //press even while it's in the middle of the mode.
    //When a button is pressed it stores what mode it needs to
    //go to next and will move to that mode once the sequence is over.
    if(buttonCheck()>0) return;
    dmx_master.setChannelValue (1,0);
    dmx_master.setChannelValue (2,0);
    dmx_master.setChannelValue (3,0);
    if(buttonCheck()>0) return;
        
  }
 
  void mode3() //ALL@FULL
  {
    //flash your LEDs how you want in here for this mode
    //a call to buttonCheck can be added in anywhere you want
    //so that the program can continue to check for a button
    //press even while it's in the middle of the mode.
    //When a button is pressed it stores what mode it needs to
    //go to next and will move to that mode once the sequence is over.
    if(buttonCheck()>0) return;
    dmx_master.setChannelValue (1,255);
    dmx_master.setChannelValue (2,255);
    dmx_master.setChannelValue (3,255);
    if(buttonCheck()>0) return;
  } 
  void mode4()
  {
    if(buttonCheck()>0) return;
  }
 
  void mode5()
  {
     if(buttonCheck()>0) return;  
  }
  
//LED FADE PROGRAM ...................................................................................
//...

// April 2007, Clay Shirky <clay.shirky@nyu.edu> 
 
  

  int calculateStep(int prevValue, int endValue) {
  int step = endValue - prevValue; // What's the overall gap?
  if (step) {                      // If its non-zero, 
  step = 1020/step;              //   divide by 1020
  } 
  return step;
  }

  /* The next function is calculateVal. When the loop value, i,
  *  reaches the step size appropriate for one of the
  *  colors, it increases or decreases the value of that color by 1. 
  *  (R, G, and B are each calculated separately.)
  */

  int calculateVal(int step, int val, int i) 
  {

  if ((step) && i % step == 0) { // If step is non-zero and its time to change a value,
    if (step > 0) {              //   increment the value if step is positive...
      val += 1;           
    } 
    else if (step < 0) {         //   ...or decrement it if step is negative
      val -= 1;
    } 
  }
 
  // Defensive driving: make sure val stays in the range 0-255
  if (val > 255) 
  {
    val = 255;
  } 
  else if (val < 0) {
    val = 0;
  }
  return val;
  }

  /* crossFade() converts the percentage colors to a 
  *  0-255 range, then loops 1020 times, checking to see if  
  *  the value needs to be updated each time, then writing  
  *  the color values to the correct pins.
  */

  void crossFade(int color[3])
  {
  // Convert to 0-255
  int R = (color[0] * 255) / 100;
  int G = (color[1] * 255) / 100;
  int B = (color[2] * 255) / 100;
  
  int stepR = calculateStep(prevR, R);
  int stepG = calculateStep(prevG, G); 
  int stepB = calculateStep(prevB, B);
  
  
    

  for (int i = 0; i <= 1020; i++) {
    redVal = calculateVal(stepR, redVal, i);
    grnVal = calculateVal(stepG, grnVal, i);
    bluVal = calculateVal(stepB, bluVal, i);
    buttonCheck();
    dmx_master.setChannelValue (1, redVal);   // Write current values to LED pins (DMX Channels)
    dmx_master.setChannelValue (2, grnVal);      
    dmx_master.setChannelValue (3, bluVal); 
    
   
    

    delay(wait); // Pause for 'wait' milliseconds before resuming the loop

    
  }

  // Update current values for next loop
  prevR = redVal; 
  prevG = grnVal; 
  prevB = bluVal;
  delay(hold); // Pause for optional 'wait' milliseconds before resuming the loop
  }

How’s this look? It doesn’t seem to interrupt the fading, and the wrong button now starts the fade. I played with the -1, but I never got it to work.

boolean button[]= {3,4,5,6,7}; //pin assignment for buttons

boolean variables should be assigned values of true or false.

  for(int x=0; x<5; x++) //starts a loop to set the pin modes for all button pins
  {
    pinMode(button[x], INPUT);
  }
  for(int x=0; x<5; x++) //starts a loop to set the buttons to a default of HIGH
  {
    pinMode(button[x], LOW);
  }

Only one for loop is needed. The code in the second one does not agree with the comment.

    buttonCheck(); //calls to the void buttonCheck() section for checking the buttons

If you don’t care what the function returns, why call it?

It doesn’t seem to interrupt the fading

Of course it doesn’t, because crossFade() doesn’t bother to return if a switch is pressed.

and the wrong button now starts the fade.

You’ve got far too much code for now. Get rid of most of it. Make sure that buttonCheck() actually does what it is supposed to. Then, start putting code back in.

You’ve got far too much code for now. Get rid of most of it.

This is good advice, and I’ll definitely try it, but first, how does the return function work? I’ve looked up the documentation, but it didn’t really explain. Where does it return to? or maybe, where should I tell it to return to?

Where does it return to?

It returns to the calling function. When you return from mode1(), the next line of code executed is the one following the call to mode1().

or maybe, where should I tell it to return to?

You can't, so don't worry about it.

Got it. Can’t thank you enough. Went through and started from the ground up. This code works flawlessly, even blacks out nice and slow. I made a choice to have slower response and a nice fade, though if I put the “if ( mode !=1)” -then break code in the crossFade function it was an instant blackout.

#include <Conceptinetics.h>

// Color arrays
int black[3]  = { 0, 0, 0 };
int white[3]  = { 100, 100, 100 };
int red[3]    = { 100, 0, 0 };
int green[3]  = { 0, 100, 0 };
int blue[3]   = { 0, 0, 100 };
int yellow[3] = { 100, 50, 0 };
int orange[3] = { 100, 25, 0 };
int purple[3] = { 80, 0, 100};
int pink [3]  = { 100, 0, 45};
int teal [3]  = { 0, 70, 100};

// Set initial color
int redVal = black[0];
int grnVal = black[1]; 
int bluVal = black[2];

int wait = 5;      // 10ms internal crossFade delay; increase for slower fades
int hold = 0;       // Optional hold when a color is complete, before the next crossFade
int repeat = 0;     // How many times should we loop before stopping? (0 for no stop)
int j = 0;          // Loop counter for repeat

// Initialize color variables
int prevR = redVal;
int prevG = grnVal;
int prevB = bluVal;

////////////////////////////////////

int bpin1 = (3);
int bpin2 = (4);
int bpin3 = (5);
int bpin4 = (6);

int lpin = (13);

int mode = (0);

DMX_Master dmx_master (512,2);  //DMX Initialize

/////////////////////////////////////

void setup(){
  
  pinMode (bpin1, INPUT);
  pinMode (bpin2, INPUT);
  
  pinMode (lpin, OUTPUT);
  digitalWrite (lpin, LOW);
  
  pinMode (2,OUTPUT);     //This is for the DMX Shield
   dmx_master.enable ();
  
}
void loop(){
  
  buttonCheck();
  
  
  while (mode == 1){
    
    crossFade(red);
  
    if (mode != 1){   //jump out
      break; }
      
    crossFade(orange);
    
    if (mode != 1){   //jump out
      break; }
      
    crossFade(yellow);
    
    if (mode != 1){   //jump out
      break; }
      
    crossFade(green);
    
    if (mode != 1){   //jump out
      break; }
      
    crossFade(teal);
    
    if (mode != 1){   //jump out
      break; }
      
    crossFade(blue);
    
    if (mode != 1){   //jump out
      break; }
      
    crossFade(purple);
    
    if (mode != 1){   //jump out
      break; }
      
    crossFade(pink);
    
    if (mode != 1){   //jump out
      break; }
    
     if (repeat) { // Do we loop a finite number of times?
      j += 1;
      if (j >= repeat) { // Are we there yet?
        exit(j);         // If so, stop.
      }
    }
    
    digitalWrite (13,LOW);
   
  }
  
  while (mode == 2){
    crossFade(black); 
    digitalWrite (13,HIGH);
     buttonCheck();
    if (mode != 2){
      redVal = black[0];
      grnVal = black[1]; 
      bluVal = black[2];
      break; }
  }
  
  while (mode == 3){
    dmx_master.setChannelValue (1,255);
    dmx_master.setChannelValue (2,255);
    dmx_master.setChannelValue (3,255); 
    digitalWrite (13,HIGH);
     buttonCheck();
    if (mode != 3){
      break; }
  }
  
  while (mode == 4){
    crossFade(blue); 
    digitalWrite (13,HIGH);
     buttonCheck();
    if (mode != 4){
      break; }
  }
  
  
    
}

///////////////

void buttonCheck(){
   if (digitalRead(bpin1) == HIGH){
    mode = (1);
  }
   if (digitalRead(bpin2) == HIGH){
    mode = (2);
  } 
   if (digitalRead(bpin3) == HIGH){
    mode = (3);
  } 
   if (digitalRead(bpin4) == HIGH){
    mode = (4);
  }    
}





//LED FADE PROGRAM ...................................................................................
//...
/*
* Code for cross-fading 3 LEDs, red, green and blue (RGB) 
* To create fades, you need to do two things: 
*  1. Describe the colors you want to be displayed
*  2. List the order you want them to fade in
*
* DESCRIBING A COLOR:
* A color is just an array of three percentages, 0-100, 
*  controlling the red, green and blue LEDs
*
* Red is the red LED at full, blue and green off
*   int red = { 100, 0, 0 }
* Dim white is all three LEDs at 30%
*   int dimWhite = {30, 30, 30}
* etc.
*
* Some common colors are provided below, or make your own
* 
* LISTING THE ORDER:
* In the main part of the program, you need to list the order 
*  you want to colors to appear in, e.g.
*  crossFade(red);
*  crossFade(green);
*  crossFade(blue);
*
* Those colors will appear in that order, fading out of 
*    one color and into the next  
*
* In addition, there are 5 optional settings you can adjust:
* 1. The initial color is set to black (so the first color fades in), but 
*    you can set the initial color to be any other color
* 2. The internal loop runs for 1020 interations; the 'wait' variable
*    sets the approximate duration of a single crossfade. In theory, 
*    a 'wait' of 10 ms should make a crossFade of ~10 seconds. In 
*    practice, the other functions the code is performing slow this 
*    down to ~11 seconds on my board. YMMV.
* 3. If 'repeat' is set to 0, the program will loop indefinitely.
*    if it is set to a number, it will loop that number of times,
*    then stop on the last color in the sequence. (Set 'return' to 1, 
*    and make the last color black if you want it to fade out at the end.)
* 4. There is an optional 'hold' variable, which pasues the 
*    program for 'hold' milliseconds when a color is complete, 
*    but before the next color starts.
* 5. Set the DEBUG flag to 1 if you want debugging output to be
*    sent to the serial monitor.
*
*    The internals of the program aren't complicated, but they
*    are a little fussy -- the inner workings are explained 
*    below the main loop.
*
* April 2007, Clay Shirky <clay.shirky@nyu.edu> 
*/ 
  

  int calculateStep(int prevValue, int endValue) {
  int step = endValue - prevValue; // What's the overall gap?
  if (step) {                      // If its non-zero, 
  step = 1020/step;              //   divide by 1020
  } 
  return step;
  }

  /* The next function is calculateVal. When the loop value, i,
  *  reaches the step size appropriate for one of the
  *  colors, it increases or decreases the value of that color by 1. 
  *  (R, G, and B are each calculated separately.)
  */

  int calculateVal(int step, int val, int i) 
  {

  if ((step) && i % step == 0) { // If step is non-zero and its time to change a value,
    if (step > 0) {              //   increment the value if step is positive...
      val += 1;           
    } 
    else if (step < 0) {         //   ...or decrement it if step is negative
      val -= 1;
    } 
  }
 
  // Defensive driving: make sure val stays in the range 0-255
  if (val > 255) 
  {
    val = 255;
  } 
  else if (val < 0) {
    val = 0;
  }
  return val;
  }

  /* crossFade() converts the percentage colors to a 
  *  0-255 range, then loops 1020 times, checking to see if  
  *  the value needs to be updated each time, then writing  
  *  the color values to the correct pins.
  */

  void crossFade(int color[3])
  {
  // Convert to 0-255
  int R = (color[0] * 255) / 100;
  int G = (color[1] * 255) / 100;
  int B = (color[2] * 255) / 100;
  
  buttonCheck();

  
  int stepR = calculateStep(prevR, R);
  int stepG = calculateStep(prevG, G); 
  int stepB = calculateStep(prevB, B);
  
  
    

  for (int i = 0; i <= 1020; i++) {
    redVal = calculateVal(stepR, redVal, i);
    grnVal = calculateVal(stepG, grnVal, i);
    bluVal = calculateVal(stepB, bluVal, i);
    
    buttonCheck();

    dmx_master.setChannelValue (1, redVal);   // Write current values to LED pins (DMX Channels)
    dmx_master.setChannelValue (2, grnVal);      
    dmx_master.setChannelValue (3, bluVal); 
    
   
    buttonCheck();
 

    delay(wait); // Pause for 'wait' milliseconds before resuming the loop

    
  }

  // Update current values for next loop
  prevR = redVal; 
  prevG = grnVal; 
  prevB = bluVal;
  delay(hold); // Pause for optional 'wait' milliseconds before resuming the loop
 
  }