Using Interrupt Service Routine to exit infinite for loop

Hi,

I'm trying to control a strip of LED lights with an IR remote. My code uses a state machine to change the colors of the strip. One of the cases sends the strip into a function that I want to loop through various colors until I press another button on the remote. However, right now it just goes through the colors once and then ends (there's no loop function right now). When I put it in a loop, it will not respond to my IR remote state machine because it's stuck in the loop.

I've heard of using a interrupt service routine to get out of the loop when the value of a pin changes (I would want to use the signal pin from the IR receiver). However, I do not know how I would use the interrupt routine in my code - I've never used them before. Any help? Code for reference. Thanks!

#include <IRremote.h>

#define REDPIN 6
#define GREENPIN 9
#define BLUEPIN 5
#define FADESPEED 5
#define RECEIVER_PIN 2 // define the IR receiver pin
IRrecv receiver(RECEIVER_PIN); // create a receiver object of the IRrecv class
decode_results results; // create a results object of the decode_results class
unsigned long key_value = 0; // variable to store the key value
int r,g,b;

void setup() {
  Serial.begin(9600); // begin serial communication with a baud rate of 9600
  receiver.enableIRIn(); // enable the receiver
  receiver.blink13(true); // enable blinking of the built-in LED when an IR signal is received
  pinMode(REDPIN, OUTPUT);
  pinMode(GREENPIN, OUTPUT);
  pinMode(BLUEPIN, OUTPUT);
}


void loop() {
  if (receiver.decode(&results)) { // decode the received signal and store it in results
    if (results.value == 0xFFFFFFFF) { // if the value is equal to 0xFFFFFFFF
      results.value = key_value; // set the value to the key value
    }
    switch (results.value) { // compare the value to the following cases
      case 0xFFA25D:
        //Serial.println("POWER");]
        turnOff();
        break;
      case 0xFF629D:
        Serial.println("VOL+");
        break;
      case 0xFFE21D:
        Serial.println("FUNC/STOP");
        break;
      case 0xFF22DD:
        Serial.println("|<<");
        break;
      case 0xFF02FD:
        Serial.println(">||");
        break ;
      case 0xFFC23D:
        Serial.println(">>|");
        break ;
      case 0xFFE01F:
        Serial.println("DOWN");
        break ;
      case 0xFFA857:
        Serial.println("VOL-");
        break ;
      case 0xFF906F:
        Serial.println("UP");
        break ;
      case 0xFF6897:
        //Serial.println("0");
        cycleColors();
        break ;
      case 0xFF9867:
        Serial.println("EQ");
        break ;
      case 0xFFB04F:
        Serial.println("ST/REPT");
        break ;
      case 0xFF30CF:
        //Serial.println("1");
        toggleRed();
        break ;
      case 0xFF18E7:
        //Serial.println("2");
        toggleGreen();
        break ;
      case 0xFF7A85:
        Serial.println("3");
        break ;
      case 0xFF10EF:
        Serial.println("4");
        break ;
      case 0xFF38C7:
        Serial.println("5");
        break ;
      case 0xFF5AA5:
        Serial.println("6");
        break ;
      case 0xFF42BD:
        Serial.println("7");
        break ;
      case 0xFF4AB5:
        Serial.println("8");
        break ;
      case 0xFF52AD:
        //Serial.println("9");
        toggleWhite();
        break ;
    }
    key_value = results.value; // store the value as key_value
    receiver.resume(); // reset the receiver for the next code
    delay(50);
  }
}

void toggleRed(){
  analogWrite(GREENPIN, 0);
  analogWrite(REDPIN, 0);
  analogWrite(BLUEPIN, 0);
  for (r = 0; r < 256; r++) {
    analogWrite(REDPIN, r);
    delay(FADESPEED);
  }
}

void toggleGreen(){
  analogWrite(GREENPIN, 0);
  analogWrite(REDPIN, 0);
  analogWrite(BLUEPIN, 0);
  for (g = 0; g < 256; g++) {
    analogWrite(GREENPIN, g);
    delay(FADESPEED);
  }
}

void turnOff(){
    for (r=255,g=255,b=255; r>=0,g>=0,b>=0; r--,g--,b--){
    analogWrite(REDPIN, r);
    analogWrite(GREENPIN, g);
    analogWrite(BLUEPIN, b);
    delay(FADESPEED);
  }
}

void cycleColors(){ 
  analogWrite(GREENPIN, 0);
  analogWrite(REDPIN, 0);
  analogWrite(BLUEPIN, 0);
  // fade from blue to violet
  for (r = 0; r < 256; r++) { 
    analogWrite(REDPIN, r);
    delay(FADESPEED);
  } 
  // fade from violet to red
  for (b = 255; b > 0; b--) { 
    analogWrite(BLUEPIN, b);
    delay(FADESPEED);
  } 
  // fade from red to yellow
  for (g = 0; g < 256; g++) { 
    analogWrite(GREENPIN, g);
    delay(FADESPEED);
  } 
  // fade from yellow to green
  for (r = 255; r > 0; r--) { 
    analogWrite(REDPIN, r);
    delay(FADESPEED);
  } 
  // fade from green to teal
  for (b = 0; b < 256; b++) { 
    analogWrite(BLUEPIN, b);
    delay(FADESPEED);
  } 
  // fade from teal to blue
  for (g = 255; g > 0; g--) { 
    analogWrite(GREENPIN, g);
    delay(FADESPEED);
  }


}

void toggleWhite(){
  analogWrite(GREENPIN, 0);
  analogWrite(REDPIN, 0);
  analogWrite(BLUEPIN, 0);

  for (r=0,g=0,b=0; r<256,g<256,b<256; r++,g++,b++){
    analogWrite(REDPIN, r);
    analogWrite(GREENPIN, g);
    analogWrite(BLUEPIN, b);
    delay(FADESPEED);
  }
}

The loop that I would want to create and exit when a button is pushed is the function cycleColors().

You don't need an ISR for that. Just read the pin, and if the value has changed, execute a break statement.

If I read the pin at the beginning of the loop, it would have to run through the entire loop before it will check to break, correct? I.e., I'd have to press the button on my remote at a specific time (at the beginning of the loop) so it would break from the loop?

Read the pin anywhere you like and as many times as you like.

In the posted code, I don't see an attempt to read a pin or solicit other input.

If you want help with a specific bit of code, post that code, using code tags.

Make a switch - case inside that first level case. Let a colour index increase each time until it exceeds the limit for this second level case.

Well, wherever you may have heard of that, it is complete rubbish (and we woduld be passably interested to know where you "heard" it :face_with_raised_eyebrow:)! That is absolutely not what an interrupt is for in any respect. :roll_eyes:

It appears you misunderstand the use of a "state machine". A "for" loop blocking the code is the exact thing you use a state machine to avoid.

As @Railroader says, you need to insert a state machine which performs your "for" function, inside whatever other sate machines you are using, to perform the function. You do not mix state machines with blocking code.

somewhere in a loop or case

    if ( read( pin ))  // true if HIGH
    (
      break;
    }

gets out.

If your code didn't block execution (closed loop as well as delays) then
some function would set a variable that would stop a loop.

I would start by using a variable to remember the last button that was pressed and ignore the repeat code. Yor switch will react on this new variabe and not on results.value.

  // variable to remember last pressed button on remote
  static uint32_t mode;

  if (receiver.decode(&results)) {
    if (results.value != 0xFFFFFFFF) {
      mode = results.value;
    }
    receiver.resume(); // reset the receiver for the next code
  }

  switch (mode) { // compare the value to the following cases
    case 0xFFA25D:
      //Serial.println("POWER");]
      turnOff();
      break;

// other cases here
...
    case 0xFF52AD:
      //Serial.println("9");
      toggleWhite();
      break ;
  }
}

Now you need to get rid of the for-loops and the delays in your functions; I have just picked one that I saw.

void toggleRed()
{
  static byte redValue;
  static uint32_t delayStart;

  analogWrite(GREENPIN, 0);
  analogWrite(REDPIN, 0);
  analogWrite(BLUEPIN, 0);

  if (millis() - delayStart >= FADESPEED)
  {
    delayStart = millis();
    analogWrite(REDPIN, redValue++);
  }
}

The trick is that you call this function repeatedly (which is what loop() does for you). The function only executes one fade step when it is time and return immediately to loop() where you can read the IR again. You will have to rework the other functions.

The analogWrite(REDPIN, 0); will cause a short glitch (I doubt you will see it) with the given code.

Not tested, not compiled. This is basically to give you the idea.

Note:
For your use case (I did not look at all cases), the variable delayStart can probably be a global variable instead of a local static variable.