Using an interrupt in code

Sorry if this seems like a dumb question to some buy my first go at this. My goal is to have a trigger run one pattern on a LED strip and then if interrupted by the other trigger it will stop patter on LED strip and run pattern on LED ring. I have gotten the patterns to play with triggers but I'm confused on how to make the 2nd trigger interrupt the first pattern. Here is my code so far

#include <Adafruit_NeoPixel.h>

Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, 6, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip2 = Adafruit_NeoPixel(60, 6, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel ring = Adafruit_NeoPixel(16, 5, NEO_GRB + NEO_KHZ800);

int pir1Pin = 2;
int pir2Pin = A2;
int led3Pin = 7;
int led1Pin = 6;
int led2Pin = 5;


void setup() {
  pinMode(pir1Pin, INPUT);
  pinMode(pir2Pin, INPUT);
  pinMode(led1Pin, OUTPUT);
  pinMode(led2Pin, OUTPUT);
  pinMode(led3Pin, OUTPUT);
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  strip2.begin();
  strip2.show(); // Initialize all pixels to 'of
  ring.begin();
  ring.show();
}

void loop() {

  if(digitalRead(pir2Pin)==HIGH) {
 
  colorWipe(strip.Color(  0,   0, 255), 10); // Blue
  colorWipe(strip2.Color(  0,   0, 255), 10); // Blue
  } 
   
  if(digitalRead(pir1Pin)==HIGH) 
 
  rainbow(5);

}


// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait) {
  // Hue of first pixel runs 5 complete loops through the color wheel.
  // Color wheel has a range of 65536 but it's OK if we roll over, so
  // just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
  // means we'll make 5*65536/256 = 1280 passes through this loop:
  for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
    // strip.rainbow() can take a single argument (first pixel hue) or
    // optionally a few extras: number of rainbow repetitions (default 1),
    // saturation and value (brightness) (both 0-255, similar to the
    // ColorHSV() function, default 255), and a true/false flag for whether
    // to apply gamma correction to provide 'truer' colors (default true).
    ring.rainbow(firstPixelHue);
    // Above line is equivalent to:
    // strip.rainbow(firstPixelHue, 1, 255, 255, true);
    ring.show(); // Update strip with new contents
    delay(10);  // Pause for a moment
  }

      for (int i=0; i < ring.numPixels(); i++)
{
      ring.setPixelColor(i, 0);        //turn every pixel off
}
ring.show();
    }


// Rainbow-enhanced theater marquee. Pass delay time (in ms) between frames.
void theaterChaseRainbow(int wait) {
  int firstPixelHue = 0;     // First pixel starts at red (hue 0)
  for(int a=0; a<30; a++) {  // Repeat 30 times...
    for(int b=0; b<3; b++) { //  'b' counts from 0 to 2...
      strip.clear();         //   Set all pixels in RAM to 0 (off)
      // 'c' counts up from 'b' to end of strip in increments of 3...
      for(int c=b; c<strip.numPixels(); c += 3) {
        // hue of pixel 'c' is offset by an amount to make one full
        // revolution of the color wheel (range 65536) along the length
        // of the strip (strip.numPixels() steps):
        int      hue   = firstPixelHue + c * 65536L / strip.numPixels();
        uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
        strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
      }
      strip.show();                // Update strip with new contents
      delay(2);                 // Pause for a moment
      firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
  }
      
      for (int i=0; i < strip.numPixels(); i++)
{
      strip.setPixelColor(i, 0);        //turn every pixel off
}
strip.show();
    }
}    


// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
  for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
    strip.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip.show();                          //  Update strip to match
    delay(50);                           //  Pause for a moment
  }

      for (int i=0; i < strip.numPixels(); i++)
{
      strip.setPixelColor(i, 0);        //turn every pixel off
}
strip.show();
    }



 

Hello, also introduce readings to the triggers in the patterns, if that occurs, change a variable created for that purpose and immediately make a "return". Manage that variable in the loop to call one or another pattern.

An interrupt is a special term, (https://create.arduino.cc/projecthub/rafitc/interrupts-basics-f475d5) and interrupts typically do a short task and then return control back to whatever was happening before the interrupt. If the interrupted code isn't written to change its behavior, the interrupt won't be able to change it.

The path forward is to re-write your animations to be "non blocking" and eliminate their delays and loops around delays.

There's a some non-blocking pixel animations in this simulation:

They take the for(int i...)...delay(..);} loops apart into persistent state variables and do the insides of the for loops conditionally, depending on millis(). The value of this pattern is that it quickly returns control to the main loop, which can switch behavior by changing the state variables.

thanks i'll give that a try and see what I can come up with. I appreciate the help!

Another choice is to check the other trigger inside the loop of the animation. For example, in colorWipe, change:

  for (int i = 0; i < strip.numPixels(); i++) // For each pixel in strip...
  {
    strip.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip.show();                          //  Update strip to match
    delay(50);                           //  Pause for a moment
  }

to

  for (int i = 0; i < strip.numPixels(); i++) // For each pixel in strip...
  {
    strip.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip.show();                          //  Update strip to match

    if (digitalRead(pir1Pin) == HIGH)
      return;  // Switch to the rainbow pattern

    delay(50);                           //  Pause for a moment
  }

Similarly, in the rainbow() loop/loops you would put:

      if (digitalRead(pir2Pin) == HIGH)
        return;
1 Like

Ooh -- that a much better choice for changing existing code.

@DaveX

I used some of your suggestion with the buttoncycler link you sent it got me a result on some of what I want. I'm still struggling on how to get where I want to be. The 2 strips color wipe after the trigger from the fist sensor, I'm still a bit lost on how to get the 2nd sensor to stop the color wipe on the 2 strips and do a rainbow on the ring. Sorry if I'm being troublesome I've only ever done basic code i.e. if this than that.....

I used the wokwi.com site to try and test that was super helpful.

here is what I have so far......

https://wokwi.com/projects/326961789731340883

and the code if the link doesn't work

 #include <Adafruit_NeoPixel.h>

#define STRIP1_COUNT  60
#define STRIP1_PIN  6
#define STRIP2_COUNT  60
#define STRIP2_PIN 7
#define RING_COUNT  16
#define RING_PIN  5
#define PIR1_PIN  A0
#define PIR2_PIN  2


Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(STRIP1_COUNT, STRIP1_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip2 = Adafruit_NeoPixel(STRIP2_COUNT, STRIP2_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel ring = Adafruit_NeoPixel(RING_COUNT, RING_PIN, NEO_GRB + NEO_KHZ800);

int pir1Pin = A0;
int pir2Pin = 2;
int strip1Pin = 7;
int strip2Pin = 6;
int ringPin = 5;

boolean oldState = LOW;
int   mode       = 0;   // Currently-active animatin mode, 0-9
int j=-1;  // extra state variable for some modes


void setup() {
  Serial.begin(115200);
  pinMode(pir1Pin, INPUT);
  pinMode(pir2Pin, INPUT);
  pinMode(strip1Pin, OUTPUT);
  pinMode(strip2Pin, OUTPUT);
  pinMode(ringPin, OUTPUT);
  strip1.begin();
  strip1.show(); // Initialize all pixels to 'off'
  strip2.begin();
  strip2.show(); // Initialize all pixels to 'off'
  ring.begin();
  ring.show();  // Initialize all pixels to 'off'
}

void loop() {
  // Get Current sensor state.
  boolean newState = digitalRead(PIR1_PIN);
  
  // Check if state changed from low to high (sensor trigger)
  if ((newState == HIGH) && (oldState == LOW)) {
    // Short Delay to debounce sensor.
    delay(20);
    // Check if sensor is still high after debounce.
    newState = digitalRead(PIR1_PIN);
    if (newState == HIGH) {
      if (++mode > 4) mode = 0;
    }
  }
  // set the last-read sensor state to the old state.
  oldState = newState;
  // Manage the LED state bassed on mode
  switch (mode) {
    case 0:
      colorFill(strip1.Color( 0, 0, 0), 0);
      colorFill(strip2.Color( 0, 0, 0), 0);
      mode = 2;
      break;
    case 2:  // idle in off
      break;
    case 3:  
      colorWipe(strip1.Color( 0, 0, 255), 10);  //Blue
      colorWipe(strip2.Color( 0, 0, 255), 10);  //Blue      
      mode = 4;
      break;
    default:
      mode = 0;
      break;  
  }
}  


// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait) {
  // Hue of first pixel runs 5 complete loops through the color wheel.
  // Color wheel has a range of 65536 but it's OK if we roll over, so
  // just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
  // means we'll make 5*65536/256 = 1280 passes through this loop:
  for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
    // strip.rainbow() can take a single argument (first pixel hue) or
    // optionally a few extras: number of rainbow repetitions (default 1),
    // saturation and value (brightness) (both 0-255, similar to the
    // ColorHSV() function, default 255), and a true/false flag for whether
    // to apply gamma correction to provide 'truer' colors (default true).
    ring.rainbow(firstPixelHue);
    // Above line is equivalent to:
    // strip.rainbow(firstPixelHue, 1, 255, 255, true);
    ring.show(); // Update strip with new contents
    delay(10);  // Pause for a moment
  }

      for (int i=0; i < ring.numPixels(); i++)
{
      ring.setPixelColor(i, 0);        //turn every pixel off
}
ring.show();
    }




// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
  for(int i=0; i<strip1.numPixels(); i++) { // For each pixel in strip...
  for(int i=0; i<strip2.numPixels(); i++) { // For each pixel in strip...
    strip1.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip2.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip1.show();                          //  Update strip to match
    strip2.show();                          //  Update strip to match

    delay(90);                           //  Pause for a moment
  }

      for (int i=0; i < strip1.numPixels(); i++)
      for (int i=0; i < strip2.numPixels(); i++)      
{
      strip1.setPixelColor(i, 0);        //turn every pixel off
      strip2.setPixelColor(i, 0);        //turn every pixel off      
}
strip1.show();
strip2.show();
  }
}


// Fill strip pixels with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorFill(uint32_t color, int wait) {
  for (int i = 0; i < strip1.numPixels(); i++) { // For each pixel in strip...
  for (int i = 0; i < strip2.numPixels(); i++) { // For each pixel in strip...  
    strip1.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip2.setPixelColor(i, color);         //  Set pixel's color (in RAM)    
  }
  strip1.show();                          //  Update strip to match
  strip2.show();                          //  Update strip to match
  delay(wait);                           //  Pause for a moment
  }
}  



I fell like being able to insert this somewhere will do what I want......

    if digitalRead(PIR2_Pin = HIGH);
    colorFill(strip1.Color( 0, 0, 0), 0);
    colorFill(strip2.Color( 0, 0, 0), 0);
    rainbow(10); 

I read this as that there is a wiping mode that blocks other things from happening. The two paths forward are to re-write it as non-blocking, or to use @Johnwasser's suggestion to test and exit from deep in the blocking loop. You could use some if-then statements to change the mode

Looking at this code, the nested loops look a bit odd. (today I learned the autoformat key in Wokwi to be option-shift-F on a Mac)

// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
  for (int i = 0; i < strip1.numPixels(); i++) { // For each pixel in strip...
    for (int i = 0; i < strip2.numPixels(); i++) { // For each pixel in strip...
      strip1.setPixelColor(i, color);         //  Set pixel's color (in RAM)
      strip2.setPixelColor(i, color);         //  Set pixel's color (in RAM)
      strip1.show();                          //  Update strip to match
      strip2.show();                          //  Update strip to match
      // XXXXX check for exiting  XXXXX
      delay(90);                           //  Pause for a moment
    }

    for (int i = 0; i < strip1.numPixels(); i++)
      for (int i = 0; i < strip2.numPixels(); i++)
      {
        strip1.setPixelColor(i, 0);        //turn every pixel off
        strip2.setPixelColor(i, 0);        //turn every pixel off
      }
    strip1.show();
    strip2.show();
  }
}

Why are the loops nested? It seems like it will wipe the colors and clear them for the number of pixels in the first strand.

It looks like you might intend to do both loops in parallel, but the nested-ness hides the outer for(int i ... loop's i from the internal loop's environment, They might be better separated, but the design intention is unclear.

Your code is a mix of a set of state-machine modes and blocking functions. I would set rainbow up with it's own mode, and use that stuff to transition out of mode 3:
(untested)

// edited if syntax, digitalRead syntax, and = to ==

I appreciate all your help and information ( a lot of it is over my head honestly) my main goal for this project is this

push button 1 and the 2 LED strips do a color wipe (that takes about 5 seconds to complete) then go off (black)

this happens every time button 1 is pressed

but if button 2 is pressed during the color wipe, I would like the color wipe to stop (or be interruped) and the LED ring to do rainbow pattern. and after the rainbow pattern everything resets to off (black)

I've been able to get the color wipe to work and the rainbow to work, but can't get the rainbow to interrupt the color wipe

and instead of buttons I'm using a vibration sensor for button 1 and either a pir sensor or an IR break beam for button 2

Sounds like you know what you want. It's just translating stuff like "if button 2 is pressed during the color wipe, I would like the color wipe to stop (or be interruped) and the LED ring to do rainbow pattern." into ifs and thens.

The solution is much like @johnwasser's suggestion in Using an interrupt in code - #5 by johnwasser -- and depending on how harshly you want to get out of the wipe's for() loop, you change mode to your (to be written) rainbow case, and use something like return, or break, or shortcut the for loop's test with i= strip.numPixels() to let the wipe clean up after itself or not.

What if someone did button 1 during the rainbow?

that works other than the color wipe not resetting back to black after it fills the strips or when the ring is triggered with the rainbow. is there something I'm missing to reset the color wipe to run again from off to color so that when button 1 is pressed a 2nd time the wipe starts over?

You can add another mode that clears the strips and then transitions into rainbow mode.

If you think in terms of modes or states, you can design how you move between them. Each clause, like "reset the color wipe" could be a mode or state that does a task and then transitions to the next state. You might be able to reset the color wipe (transition back to the beginning of wipe) with an if(digitalRead(PIR1_PIN) == HIGH){mode = 3; return;}

The trick is determining all your "states" and how they transition between each other.

Arduino Project Hub -- is a State Machine intro on Arduino

One thing that is odd about using the Wokwi Passive InfraRed sensors in the as compared to plain buttons is that they stay latched on for 5 seconds their last press. That makes it awkward to detect if they've been pressed a 2nd time.

I will be using a vibration sensor for what I'm referring to as button 1, the PIR in wokwi was the one i choose to try and test the code with

Adafruit Fast Vibration Sensor Switch

so this new code with some of what you have suggested has gotten me the closest to what i'm trying to accomplish. I'm still lacking the ability to get the led strips to go black after the color wipe fills them with blue. Would using a case statement resolve this issue but still do what I have so far?

#include <Adafruit_NeoPixel.h>

Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(60, 7, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip2 = Adafruit_NeoPixel(60, 6, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel ring = Adafruit_NeoPixel(16, 5, NEO_GRB + NEO_KHZ800);

int pir1Pin = 2;
int pir2Pin = A0;
int strip1Pin = 7;
int strip2Pin = 6;
int ringPin = 5;


void setup() {
  pinMode(pir1Pin, INPUT);
  pinMode(pir2Pin, INPUT);
  pinMode(strip1Pin, OUTPUT);
  pinMode(strip2Pin, OUTPUT);
  pinMode(ringPin, OUTPUT);
  strip1.begin();
  strip1.show(); // Initialize all pixels to 'off'
  strip2.begin();
  strip2.show(); // Initialize all pixels to 'of
  ring.begin();
  ring.show();
}

void loop() {

  if(digitalRead(pir2Pin)==HIGH) {
 
  colorWipe(strip1.Color(  0,   0, 255), 10); // Blue
  colorWipe(strip2.Color(  0,   0, 255), 10); // Blue

  strip1.clear();
  strip1.show();
  strip2.clear();
  strip2.show();

  } 
   
  if(digitalRead(pir1Pin)==HIGH) 
 
  rainbow(5);

}


// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait) {
  // Hue of first pixel runs 5 complete loops through the color wheel.
  // Color wheel has a range of 65536 but it's OK if we roll over, so
  // just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
  // means we'll make 5*65536/256 = 1280 passes through this loop:
  for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
    // strip.rainbow() can take a single argument (first pixel hue) or
    // optionally a few extras: number of rainbow repetitions (default 1),
    // saturation and value (brightness) (both 0-255, similar to the
    // ColorHSV() function, default 255), and a true/false flag for whether
    // to apply gamma correction to provide 'truer' colors (default true).
    ring.rainbow(firstPixelHue);
    // Above line is equivalent to:
    // strip.rainbow(firstPixelHue, 1, 255, 255, true);
    ring.show(); // Update strip with new contents
    delay(10);  // Pause for a moment
  }

      for (int i=0; i < ring.numPixels(); i++)
{
      ring.setPixelColor(i, 0);        //turn every pixel off
}
ring.show();
    }


// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
   for (int i = 0; i < strip1.numPixels(); i++) // For each pixel in strip...
   for (int i = 0; i < strip2.numPixels(); i++) // For each pixel in strip...
  {
    strip1.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip2.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip1.show();                          //  Update strip to match
    strip2.show();                          //  Update strip to match


    if (digitalRead(pir1Pin) == HIGH)
      return;  // Switch to the rainbow pattern

    delay(50);   
  
  }
}


1 Like

Yes, you can use a switch-case to get the led strips to go black after the color wipe fills them with blue.

Here's a hacked-up version of your earlier code, where I was playing at making it non-blocking and doing some debugging messages:

I don't know if it is doing close to what you want, but I used a couple transitional stages/modes/states in a switch-case that cleaned up the partially-colored states before transitioning into the color states.

The Serial output gives periodic updates of the current mode and mode_ii state variables, along with some other random diagnostic code characters.

1 Like

That is exactly what I was looking for! Thank you for all your help!!! This is going to be a birthday present for my grandson. it goes with his ballpit. The goal was if he threw the ball and hit the background the strips would light up and go off (hence the color wipe) and if he kept hitting the "backboard" it would repeat, but if he got the ball through a hole the ring would go off.

1 Like

Cool.

The nice thing about switch-case and modes/states is that they help separate the logic into distinct chunks.