RGB Control: Breaking Fading function

Hi all, I'm new to the Arduino world. I'm trying to replicate with it the functions of my RGB controller.
I've managed to map some functions (change colours) to the remote control.

I'm trying to replicate the Fade 7 function but I can't figure out how to exit from it while it is running (endless loop) when another button is pressed.

Can you help me out? Here is the code:

#include "IRremote.h"

int pinRic = 9; // Signal Pin of IR receiver to Arduino Digital Pin 11
IRrecv ricevitore(pinRic);     // create instance of 'irrecv'
decode_results results;      // create instance of 'decode_results'
#define BLUE 3
#define GREEN 5
#define RED 6
#define IR_BPlus  0xFF3AC5  // 
#define IR_BMinus 0xFFBA45  // 
#define IR_ON     0xFF827D  // 
#define IR_OFF    0xFF02FD  // 
#define IR_R    0xFF1AE5  // 
#define IR_G    0xFF9A65  // 
#define IR_B      0xFFA25D  // 
#define IR_FADE7  0xFFE01F  // 
#define incFact 30
int  brightFact = 0;
int lv[3];


void fade7()
{
    #define delayTime 10 // fading time between colors

    lv[0] = 255; // choose a value between 1 and 255 to change the color.
    lv[1] = 0;
    lv[2] = 0;

    while(true)
    {
        for(int i = 0; i < 255; i += 1) // fades out R bring G full when i=255
        {
            lv[0] -= 1;
            lv[1] += 1;

            analogWrite(RED, lv[0]);
            analogWrite(GREEN, lv[1]);
            delay(delayTime);
        }

        lv[0] = 0;
        lv[1] = 255;
        lv[2] = 0;

        for(int i = 0; i < 255; i += 1) // fades out G bring B full when i=255
        {
            lv[1] -= 1;
            lv[2] += 1;

            analogWrite(GREEN, lv[1]);
            analogWrite(BLUE, lv[2]);
            delay(delayTime);
        }

        lv[0] = 0;
        lv[1] = 0;
        lv[2] = 255;

        for(int i = 0; i < 255; i += 1) // fades out B bring R full when i=255
        {

            lv[2] -= 1;
            lv[0] += 1;

            analogWrite(BLUE, lv[2]);
            analogWrite(RED, lv[0]);
            delay(delayTime);
        }
    }
}
void setColor(int r, int g, int b)
{


  Serial.println("About to write:");
  Serial.println(r);
  Serial.println(g);
  Serial.println(b);
  analogWrite(RED,r);
  analogWrite(GREEN,g);
  analogWrite(BLUE,b);
}
void  translateIR() // takes action based on IR code received

// describing Remote IR codes 

{
  Serial.println(results.value, HEX);
  switch(results.value)
  {

    case IR_OFF: setColor(0,0,0);break;
    //COLORS    
    case IR_R: setColor(255,0,0);break;
    case IR_G: setColor(0,255,0);break;
    case IR_B: setColor(0,0,255);break;


    //END COLORS
    case IR_FADE7: fade7();

  }// End Case

  delay(100); // Do not get immediate repeat


} //END translateIR

void setup()   /*----( SETUP: RUNS ONCE )----*/
{

  pinMode(RED, OUTPUT);
  pinMode(GREEN, OUTPUT);
  pinMode(BLUE, OUTPUT);
  Serial.begin(9600);
  Serial.println("IR Receiver Button Decode"); 
  ricevitore.enableIRIn(); // Start the receiver*/

}/*--(end setup )---*/

void loop()   /*----( LOOP: RUNS CONSTANTLY )----*/
{

  if (ricevitore.decode(&results)) // have we received an IR signal?

  {
    translateIR(); 
    ricevitore.resume(); // receive the next value*/
  }  
} /*--(end main loop )-- */

I'm trying to replicate the Fade 7 function but I can't figure out how to exit from it while it is running (endless loop) when another button is pressed.

The function has an endless while loop in it as you have discovered. If you read the input in that loop and use the return statement when the button becomes pressed the function will end and the program will return to where it was called.

However, the function is riddled with delay()s so don't expect a quick response to the button press. The proper way to fix this would be to rewrite the function so that delay() is not used.

See Using millis() for timing. A beginners guide, Several things at the same time and look at the BlinkWithoutDelay example in the IDE.

You will have to get rid of the while loop for starters. And you will need to break up your fade7 into small chunks (I will call them steps).

Start with defining the steps

Initialisation step

  lv[0] = 255; // choose a value between 1 and 255 to change the color.
  lv[1] = 0;
  lv[2] = 0;

Fade from red to green

    for (int i = 0; i < 255; i += 1) // fades out R bring G full when i=255
    {
      lv[0] -= 1;
      lv[1] += 1;

      analogWrite(RED, lv[0]);
      analogWrite(GREEN, lv[1]);
      delay(delayTime);
    }

New initialisation step
4)
Fade from green to blue
5)
New initialisation step
6)
Fade from blue to red

Now you can write a basic framework for those steps.

#define INIT_R2G 0
#define FADE_R2G 1
#define INIT_G2B 2
#define FADE_G2B 3
#define INIT_B2R 4
#define FADE_B2R 5

void fade7()
{
  // keep track of the current step; initialize with INIT_R2G
  static byte currentStep = INIT_R2G;

  switch (currentStep)
  {
    case INIT_R2G:
      ...
      ...
      // done with this step, go to next step
      currentStep = FADE_R2G;
      break;
    case FADE_R2G:
      ...
      ...
      currentStep = INIT_G2B;
      break;
    case INIT_G2B:
      ...
      ...
      currentStep = FADE_G2B;
      break;
    case FADE_G2B:
      ...
      ...
      currentStep = INIT_B2R;
      break;
    case INIT_B2R:
      ...
      ...
      currentStep = FADE_B2R;
      break;
    case FADE_B2R:
      ...
      ...
      currentStep = INIT_R2B;
      break;
  }
}

Each time the code has completed a step, it will set currentStep to the another value and the next time you call fade7() it will execute that step. What is implemented is basically a simple statemachine.

Now you can fill what needs to happen in the steps. I'll leave that to you.

In loop() you can call fade7(); try this for testing purpose.

void loop()
{
  fade7();
}

Next, in your translateIR(), you can set a global flag indicating that you want to run fade7() so in loop() you know what to do.

bool fadeSeven = false;
void  translateIR() // takes action based on IR code received
// describing Remote IR codes
{
  Serial.println(results.value, HEX);
  switch (results.value)
  {
    case IR_OFF: setColor(0, 0, 0); break;
    //COLORS
    case IR_R: setColor(255, 0, 0); break;
    case IR_G: setColor(0, 255, 0); break;
    case IR_B: setColor(0, 0, 255); break;

    //END COLORS
    case IR_FADE7: fadeSeven = true; break;

  }// End Case
} //END translateIR

I have removed the extra delay at the end to get a bit more responsive code.

And in loop()

void loop()   /*----( LOOP: RUNS CONSTANTLY )----*/
{
  if (ricevitore.decode(&results)) // have we received an IR signal?
  {
    translateIR();
    ricevitore.resume(); // receive the next value*/
  }

  if(fadeSeven == true)
  {
    fade7();
  }
} /*--(end main loop )-- */

You probably want/need to find a means to stop it (e.g. another button on the remote can set fadeSeven to false). Instead of a bool variable, you can use a global mode variable if you have more modes to cater for.

Now, you still have a problem with the responsiveness because (I suspect) you implemented the for-loops (so 255 x 10 ms delay). Just like there is no need for the while-loop, there is usually also no need for for-loops either in Arduino code.

So you need to break your for-loops into smaller chunks. We're going to use a static new variable in fade7() and use that; it will be incremented each time that you call fade7() while fading. The below shows the implementation for the first two steps.

bool fade7()
{
  // keep track of the current step; start step with INIT_R2G
  static byte currentStep = INIT_R2G;
  // counter for fading; replaces i in the for-loops
  static int fadeCounter = 0;

  switch (currentStep)
  {
    case INIT_R2G:
      // initialise
      lv[0] = 255; // choose a value between 1 and 255 to change the color.
      lv[1] = 0;
      lv[2] = 0;
      // reset fadeCounter
      fadeCounter = 0;
      // go to next step
      currentStep = FADE_R2G;
      break;
    case FADE_R2G:
      // test and increment fadeLevel
      if (fadeCounter++ < 255)
      {
        lv[0] -= 1;
        lv[1] += 1;

        analogWrite(RED, lv[0]);
        analogWrite(GREEN, lv[1]);
        delay(delayTime);
      }
      else
      {
        // done with this fading, go to next step
        currentStep = INIT_G2B;
      }
      break;

If you need more responsiveness, you will need to start looking at millis() based timing; the delayTime that you use is 10 ms, so it should still be reasonable responsive.

Not compiled, not tested so there might be errors. It should however give you the idea.