Interrupt procedure after reading IR signal

Hi everybody,
I have a small problem with my sketch and probably it is very basic one but I am not very experienced to know the solution.
My project is quite simple; I have a Ledstrip and I just want to apply some effects on the lights depending on a code which I send from an IR-remotecontrol. Selecting the desired sequence with the remotecontrol works ok but I want to implement a way to interrupt a running sequence just by pressing any key on the remotecontrol. With my actual sketch I can interrupt a sequence by pressing a wired digital button but not by sending/receiving an IR-code. What´s wrong with my code ?
Thanks a lot for a working hint...
Here is my code:

 // LED strip Color selector
 // Version V1.0
 // June 2024
 
#include <Adafruit_NeoPixel.h>
#define IR_SMALLD_NECx         //1st: define which protocol to use:
#include <IRsmallDecoder.h>   //2nd: include the library;
IRsmallDecoder irDecoder(2);  //3rd: create one decoder object with the correct digital pin;
irSmallD_t irData;            //4th: declare one decoder data structure;


#define StripPin 7 
#define NUMPIXELS 150 

Adafruit_NeoPixel pixels(NUMPIXELS, StripPin, NEO_GRB + NEO_KHZ800);

#define DELAYVAL 50 // Time (in milliseconds) to pause between pixels
#define BRIGHTNESS 5 // Set BRIGHTNESS to about 1/5 (max = 255)
#define selPin 4

uint8_t Select = 68;
bool StopCode = false;

int colR;
int colG;
int colB; 


void setup() {

  pinMode(selPin, INPUT);
  
  pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
  pixels.setBrightness(BRIGHTNESS);
  pixels.clear(); // Set all pixel colors to 'off'

    colR=0;
    colG=100;
    colB=200;
    EffectOverColor();

  Serial.begin(115200);
  Serial.println("--- Setup Ready ---");
}

void loop() 
{
 while( !irDecoder.dataAvailable(irData)) {;}
  
    //Serial.println(irData.cmd, DEC); 
    Select = (irData.cmd); Serial.println(Select);
    StopCode = false;  
 
 switch (Select)
  {
    case 69:
    // (1)Red Override
    colR=200;
    colG=30;
    colB=0;
    EffectOverColor();   
    break;  

    case 70:
    // (2)Green Override  
    colR=0;
    colG=200;
    colB=30;
    EffectOverColor();   
    break;

    case 71:
    // (3)Blue Override
    colR=50;
    colG=0;
    colB=200;
    EffectOverColor();   
    break; 
       
    case 68:
    // (4)Magenta Override
    colR=100;
    colG=0;
    colB=100;
    EffectOverColor();   
    break; 
       
    case 64:
    // (5)Cyan Override
    colR=0;
    colG=120;
    colB=120;
    EffectOverColor();   
    break; 

    case 67:
    // (6)Yellow Over
    colR=180;
    colG=80;
    colB=0;
    EffectOverColor();   
    break; 
       
    case 7:
    // (7)Strong Yellow Override
    colR=200;
    colG=200;
    colB=0;
    EffectOverColor();   
    break; 
       
    case 21:
    // Full Green Override
    colR=0;
    colG=150;
    colB=100;
    EffectOverColor();   
    break; 
       
    case 9:
    // All pixels override
    //while(!irDecoder.dataAvailable(irData))
    while (!StopCode)
  {
    colR=200;
    colG=0;
    colB=0;
    EffectOverColor();
    colR=0;
    colG=200;
    colB=0;
    EffectOverColor();
    colR=0;
    colG=0;
    colB=200;
    EffectOverColor();
    colR=200;
    colG=200;
    colB=0;
    EffectOverColor();
    colR=0;
    colG=200;
    colB=200;
    EffectOverColor();
    colR=200;
    colG=0;
    colB=200;
    EffectOverColor();
  }       
    break; 
         
    case 25:
    // all LED off
    colR=0;
    colG=0;
    colB=0;    
    EffectOverColor();
    break;           
 }    
} // END LOOP

 void EffectOverColor()
 {
  if(!StopCode)
  {
  for(int i=0; i < NUMPIXELS; i++) 
  { 
    if(digitalRead(selPin)) {StopCode = true;}
    if(irDecoder.dataAvailable(irData)) {StopCode = true;}
    if (StopCode == true) {Serial.println ("Interrupted..."); i=NUMPIXELS;}
    
    pixels.setPixelColor(i, pixels.Color(colR,colG,colB));
    pixels.show(); 
    delay(DELAYVAL);    
   }
  }
 }

What happens when you run the code, and press a button on the IR remote?

For help on this forum, it would be a good idea to put some comments in the code.

For example, what is the purpose of this line?

 while( !irDecoder.dataAvailable(irData)) {;}

this line just holds on the loop until something is received from the IR control ( data available).

What is wrong is your code does not remember that it received a code. Save the received code in a global variable and test for the global variable NOT being zero in the same area as you do the switch test that works.
Once once you recognize a non-zero saved code, zero the saved code and do the requested process.

Save the received code in a global variable is exactly what I am doing with my variable "bool StopCode". Unfortunately nothing is read to be buffered in StopCode

Please answer the question.

to answer your question, what is working fine so far is by pressing a button on the remotecontrol the function EffectOverColor() is starting using the corresponding color values from the switch cases. After finish the sequence the program comes back to the beginning of the main loop and waiting for a new code received from the IR remotecontrol. The case 9 should be an endles loop which can be interrupted by pressing any button on the remotecontrol.

Thanks. Now, please explain what does not work.

How can you know that "case 9" is actually executed?

what does not work is stopping the case 9 endless sequence by receiving any code from the IR remotecontrol. Using a digital input with a button it works.

I wonder what happens when this function is (abnormally) called with i equal to NUMPIXELS.

    pixels.setPixelColor(i, pixels.Color(colR,colG,colB));

To prematurely exit a loop, use break; or return; to exit the function.

sorry, may be my logic is a bit weired. Setting i=NUMPIXELS inside of the "for i" loop results in reaching the end of this loop at once because the i<NUMPIXELS is not yet true.
I also tried break and return without success. I think it is not the reaction to the condition but achieving the condition

...actually what I learned so for is that the IRsmallDecoder library is using the interrupt ability of certain inputs of the processor but the NEOPIXEL library switches off the interrupts during its runtime.

Do you know if the NEOPIXEL library re-enables interrupts when the function is finished?

I'm wondering this might work:

  • Enable pin change interrupt on IR receiver pin at the start of State 9
  • Set StopCode = TRUE in the interrupt routine
  • Disable pin change interrupt in EffectOverColor when StopCode==TRUE

Hopefully the interrupt flag would trigger the routine at the first possible time in between the pixels commands

good idea, I will try. Can you please help me with the syntax of enable/disable interrupts on pins ?

The Adafruit library checks and it is harmless, if bad practice, to refer to pixels that do not exist.

It can come in handy, like if you have a pixel train you wanna drive off the end of the track, no need to check if "cars" are off the edges.

Contrast to FastLED, where the pixels buffers are declared in the sketch, and are very much in need of being respected bounds-wise.

a7

I don't think you've said which arduino you're using, so I'm going to make an assumption that it is one of the common ones with the ATmega328p microcontroller. If it isn't one of those, then you can get the equivalent commands from the interrupt section of the datasheet.

There are 3 things that need to be turned on for a pin change interrupt

  • Global interrupts must be enabled.
  • Pin change interrupts must be enabled for the port that the pin is on
  • The pin change mask for that port must have the bit for that pin set to 1
//In setup()
cli();                  //DISABLE global interrupts
PCICR &= ~(1<<PCIE0);   //DISABLE pin change interrupts for Port D (pins 0-7)
PCMSK0 |= (1<<PCINT2);  //ENABLE pin change interrupt on pin 2
sei();                  //ENABLE global interrupts

//At the start of case 9
PCIFR  = 0;             //Clear all pin change interrupt flags. 
PCICR |= (1<<PCIE0);    //ENABLE pin change interrupts for Port D (pins 0-7)

//In EffectOverColor()
//Can get rid of this
if(digitalRead(selPin)) {StopCode = true;}
if(irDecoder.dataAvailable(irData)) {StopCode = true;}

//but add this to disable the pin change interrupt again in your if statement
if (StopCode == true) {
  Serial.println ("Interrupted..."); 
  i=NUMPIXELS;
  PCICR &= ~(1<<PCIE0);  //DISABLE pin change interrupts for Port D (pins 0-7)
}

//Add somewhere outside setup(), loop(), and EffectOverColor()
ISR(PCINT0_vect) {
  StopCode = true;
}

I think that should cover it. Others here may know a more readable version of how to do this, but this is the way the datasheet outlines how to do it.

About the PCIFR = 0 line, it is usually good to clear the flag before enabling the interrupts so that an interrupt doesn't trigger right away if the flag is set. However, if we're putting this at the start of case 9, then it could clear a flag that was set during the last EffectOverColor() in the loop and then you might not get a response. I would try it with and without and see if it either give you trouble.

Thanks a lot for your detailed instructions. Actually I am working with a ATmega 328P processor. I implemented the lines exactly how you suggested and the sketch compiled and loaded well but there was absolutely no effect. Then I remembered that somewhere (I don´t remember where) I noticed that the IRsmallDecoder library is using Interrupt. Having a look on the sourcecode of this library I found the particular lines which handle the interrupt ability and it looks similar to what you suggest. My conclusion is that the NEOPIXEL library needs to switch off and block interrupts to prevent interfering (I have seen this too in some publication on the web.) As I am not this deep inside in C, C++ etc... programming and processor hardware I cannot confirm or discard these statements.
Now my little project is just to have some light effects on my LED strip decoration and I think it is not worth enough to study more in deep these interrupt issues in order to be able changing or adjusting the code of the available libraries.
Anyway I appreciate your kind attention to help me.

I just did a little reading in the neopixel library, and found this line:

"Interrupt is only disabled if there is no PWM device available"

What if you try moving your StripPin from 7 to one that supports PWM? On the Uno, Nano, and Mini, that should be pins 3, 5, 6, 9, 10, and 11.

I also realized I forgot to turn the StopCode back to FALSE in my code I provided. If moving the StripPin leaves the interrupts enabled, you might be able to discard my code and go back to what you had.

if (StopCode == true) {
  Serial.println ("Interrupted..."); 
  i=NUMPIXELS;
  PCICR &= ~(1<<PCIE0);  //DISABLE pin change interrupts for Port D (pins 0-7)
  StopCode = false;
}