Exiting a series of for loops upon receiving an IR signal

Hello everyone, I have a slight programming issue and I’m wondering if what I’m trying to do is even possible. I’m creating a decorative light for my room and it involves RGB LED’s and an IR remote and sensor. The remote has 7 buttons and I’ve created 6 colors (1 per button) red, orange, yellow, green, blue and purple. The 7th button I’m attempting to toggle between 2 states, one state would be, the unit off (R,G,B at 0) and the other a state in which the LED strip will fade between all the colors every 20 seconds or so. I’ve attached my code that works up until I attempt to insert the “colorCycle” function which cycles through the colors at said interval. Can someone maybe help inform me of what I’m missing to accomplish this.

This code works for everything but the colorCycle state of the on/off button. It seems to me that I’m not leaving an opportunity for the while loop to be exited while doing colorCycle.

Thanks in advance to anyone that can help.

#define REDPIN 5
#define GREENPIN 6
#define BLUEPIN 3
#define FADESPEED 10 // make this higher to slow down
#define FADESPEEDB 25 // make this higher to slow down
#define TIMER 5

int r;
int g;
int b;

int colorKey;                 

int irPin = 2; // IR Sensor pin 1 wired to Arduino's pin 8
int start_bit = 2200; //Start bit threshold (Microseconds)
int bin_1 = 1000; //Binary 1 threshold (Microseconds)
int bin_0 = 400; //Binary 0 threshold (Microseconds)
int powerLED = 12;

long previousMillis = 0;        // will store last time strip was updated
long interval = 25000;           // interval at which color will stay at that color (milliseconds)



void setup() {
  pinMode(REDPIN, OUTPUT);
  pinMode(GREENPIN, OUTPUT);
  pinMode(BLUEPIN, OUTPUT);
  pinMode(irPin, INPUT);
  pinMode(powerLED, OUTPUT);
  
  Serial.begin(9600);
  Serial.println("Waiting: ");
  
}

void loop(){
   int key = getIRKey();
    colorKey = key;		       //Fetch the key
    if(key != 0)     {                 //Ignore keys that are zero
      Serial.println(key);            //For troubleshooting 
      Serial.println(colorKey);

       
       Serial.print("Key Recieved: ");


       
       
         if(colorKey == 144){
         Serial.print("red/CH Up");
                   fadeDown();
                   for(r = 0; r < 255; r++) {
                     analogWrite(REDPIN, r);
                     delay(FADESPEED);
                   }
         }
         if(colorKey == 145){
         Serial.print("Orange/CH Down");
                   fadeDown();
                    for (g = 0; g < 55; g++) {   //Orange
                     analogWrite(GREENPIN, g);  //Green 80, Red 255 makes
                     delay(FADESPEED);      
                     for(r = 0; r < 255; r++) {  //if(r < 255)                //Orange. Blue is at 0  
                                                 // r = r + 1;
                      analogWrite(REDPIN, r);
                      }  
                     delay(FADESPEED);      
                   }
         }
         if(colorKey == 146){
           Serial.println("Yellow/VOL Right"); 
                    fadeDown();
                     for (g = 0; g < 185; g++) {   //Yellow
                      analogWrite(GREENPIN, g);          //Green 195, Red 255 makes 
                      delay(FADESPEED);                        
                      for (r = 0; r < 255; r++) {      //if(r < 255)                //yellow. Blue still at 0 
                                                       //r = r + 1;
                      analogWrite(REDPIN, r);
                      }  
                     delay(FADESPEED);      
                   }
         }
         if(colorKey == 147){ 
           Serial.println("Green/VOL Left"); 
                    fadeDown();
                     for (g = 0; g < 255; g++) {   //Green
                     analogWrite(GREENPIN, g);
                     delay(FADESPEED);  
                     }
         }
         if(colorKey == 148){
           Serial.println("Blue/Mute"); 
                    fadeDown();
                     for (b = 0; b < 255; b++) {   //Blue
                     analogWrite(BLUEPIN, b);
                     delay(FADESPEED);  
                     }
         }
         if(colorKey == 165){
           Serial.println("Purple/AV/TV"); 
                    fadeDown();
                     for (r = 0; r < 255; r++) {   //Purple
                     analogWrite(REDPIN, r);          //Red 255, Blue 255 makes 

                       for(b = 0; b < 255; b++) {
                     
                     //if(b < 255)               //purple. 
                     //b = b + 1;
                     analogWrite(BLUEPIN, b);
                        }
                       delay(FADESPEEDB);   
                     }
         }   
         if(colorKey == 149) {           
                Serial.println("Power cycle/On/off");
                fadeDown();
                     if(digitalRead(powerLED) != 1){ //This toggles the statLED every time power button is hit
                       digitalWrite(powerLED, HIGH);
                       Serial.println("Power cycle/On");
                       colorKey = 150;
                       Serial.print("colorKey...");                      
                       Serial.println(colorKey);                                             
                          }else{
                        digitalWrite(powerLED, LOW); 
                        Serial.println("Power cycle/Off");
                        colorKey = 149;
                        fadeDown();
                        Serial.print("colorKey...");                      
                        Serial.println(colorKey);                        
                          
                  }
               
           }
                               
      }    
     //while(colorKey == 150){
      //colorCycle();
      //}
  }

Here is “colorCycle” function

void colorCycle() {

  for(b = 255; b > 0; b--){   //purple to red
    analogWrite(BLUEPIN, b);
    delay(FADESPEED);
     }
    unsigned long currentMillis = millis();    
     if(currentMillis - previousMillis > interval) {  
           previousMillis = currentMillis; 
  for (g = 0; g < 55; g++) {   //red to orange
    analogWrite(GREENPIN, g);  
    delay(FADESPEED);    
   }
  unsigned long currentMillis = millis();    
     if(currentMillis - previousMillis > interval) {  
           previousMillis = currentMillis;
  for (g = 55; g < 195; g++) {   //orange to yellow
    analogWrite(GREENPIN, g);    
    delay(FADESPEED);      
   }
  unsigned long currentMillis = millis();    
     if(currentMillis - previousMillis > interval) {  
           previousMillis = currentMillis;
  for (g = 195; g < 255; g++) {   //yellow to green
    analogWrite(GREENPIN, g); 
       for(r = 255; r > 0; r--){   
         analogWrite(REDPIN, r);
         }
      delay(FADESPEEDB);      
   }   
 unsigned long currentMillis = millis();    
     if(currentMillis - previousMillis > interval) {  
           previousMillis = currentMillis;
  for (g = 255; g > 0; g--) {   //green to blue
    analogWrite(GREENPIN, g); 
       for(b = 0; b < 255; b++){   
         analogWrite(BLUEPIN, b);
         }
      delay(FADESPEED);      
   }   
unsigned long currentMillis = millis();    
     if(currentMillis - previousMillis > interval) {  
           previousMillis = currentMillis;
  for (r = 0; r < 255; r++) {   //blue to purple
    analogWrite(REDPIN, r); 
    delay(FADESPEED);      
    }   
         }
       } 
      }
     }
   }
}

The preferred approach would be to change the architecture of your sketch so that all the fading was done asynchronously. This requires a subtle but fundamental change to the way you think about the problem. Look at the 'blink without delay' sketch for a simple worked example using this technique. Once you adopt this non-blocking approach, you can have as many features controlled separately as you wish without them needing to explicitly cooperate with each other.

The 'quick hack' approach would be to poll for received IR signals in the fade loops and break out of the fade loop if the input indicated that the mode had changed. It's going to produce some ugly code which would get worse in a hurry as you try to extend that approach, but would get you a working solution without changing the architecture of your sketch.

Hey,

any news for that topic? I have nearly the same question…

void fadeRGB(){
      int r, g, b;
        // 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);
        }  
}

This kind of fade blocks all other input to change f.e. to solid color… I´ve tried to use the blink without delay example but i think we
still need the for loop!?

best regards

You can exit the function early with a return statement.

You could check the input pin between each loop, and return if you detect a button push.

Thanks,

but in this case i have to push the butten in this microsecond the butten gets queried?? The for loop will block all other functions in the same way delay does so i think we need a way without loop?! Maybe with if statements??

Ah. I see. The issue is that you can’t poll the IR detector and do your fade delays at the same time.

I found a copy of getIRKey(), and it looks like the start bit threshold is 2200 uSec. That doesn’t leave you much time to use delay() statements.

I can see three possible solutions right off.

  1. Monitor the IR bit inside your fade loops. For example:
        // fade from blue to violet
        for (r = 0; r < 256; r++) {
          analogWrite(redPin, r);
          for (q = 0; q < FADESPEED * 10; ++q) {
            if (digitalRead(irPin) != 0)         // Somebody pushed a button on the remote
              return;
            delayMicroseconds(100);
          }
        }

I’m not really sure the irPin is active high. You might have to check if (digitalRead(irPin) == 0) instead, and you might have to fudge the delays and such, but you get the idea. The main point is that you check for IR signal often enough that you can return out of this function in enough time for the next call to getIRKey() to recognize the start bit. This is what PeterH was talking about with the quick hack approach.

  1. Use the asynchronous fade approach PeterH posted about. The problem with this is that, as I understand it, the call to getIRKey() blocks, waiting for IR signal. Once you call getIRKey(), it doesn’t return so you can’t do anything else until a key gets pressed. You could get around this by checking the irPin first, and not calling getIRKey() until you’re already sure someone has pushed a button on the remote. This could be made quite elegant with an array or two of colors and delay times.

  2. Use interrupts for one or the other of your processes. You could convert your fade process to use a timer interrupt, and use state variables to communicate with the interrupts service routine. That way, you could call getIRKey and couldn’t care less if it blocks. Your fades would happen anyway.

A proper IR library would have you connect the IR receiver to an interrupt pin. Just sitting there waiting for an IR input without being able to do anything else would be useless. Upon an interrupt, it would break out of any loop to go and service the interrupt before returning to the loop. So, within the interrupt routine, you would set a global state based on the received key and within your loop you would always test this state and what your loop does depends on the state. This way, it would respond to your keypresses. It may finish one iteration of the loop before "appearing" to respond (and that may be a while with using delays), but on the next iteration, it will act on the new state.

So following PeterH's way of thinking, you're speaking of essentially writing for loops without using delay()?

Interesting, I'm not doubting your knowledge in anyway I just don't immediately grasp how to fade an LED without using delay() in the for loops. I've looked through BlinkWithoutDelay and I don't see how to continue the for loop after checking previousMillis against currentMillis without it getting really messy really fast.

Could you possibly write a quick example of what you're talking about?