Push Button Interrupt Start at Beginning of Loop

Hello Everyone,

I am working on a program that switches between two different sets of functions with a button. I learned that an interrupt is important for my application. The program goes through many steps. It would be unrealistic to place a bunch of button reads in the code leading to an interrupt as ideal. So far the results with an interrupt are making me happy minus one aspect.

An interrupt resumes the code right where it left off. What I would like to have is the interrupt start from the beginning of void loop. This way when you push a button you instantly see a change and dont keep pressing the button.

Im sure there is a way to get the button to be noticed at any point in the code and make an immediate change.

Here is the code that I have worked out through tutorial to get the debounced push button interrupt working.

int pin = 13;
volatile int state = HIGH;
int i = 0; 


void setup() {
    pinMode(pin, OUTPUT);
    pinMode(9,INPUT);
    attachInterrupt(9, bll, CHANGE);
}

void loop() {
    digitalWrite(pin, state);
}

void bll() {
//Serial.println("in bll");
static unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = millis();
  if (interrupt_time - last_interrupt_time > 400){
i++;

    state = !state;
    Serial.print("PUSHED bll ");
    Serial.println(i);
    if (i>=5){i = 0;}
  }
  last_interrupt_time = interrupt_time;
}

and here is the code that im working with.

#include <Adafruit_NeoPixel.h>

#define PIN 2


int buttonPin = 9;
volatile int state = HIGH;
int buttonChange = 1;

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(6, PIN, NEO_GRB + NEO_KHZ800);

int redPin = 11;   // Red LED,   connected to digital pin 9
int grnPin = 9;  // Green LED, connected to digital pin 10
int bluPin = 10;  // Blue LED,  connected to digital pin 11

int b = 0;

void setup()
{
pinMode(buttonPin,INPUT);
attachInterrupt(buttonPin, push1, CHANGE);
pixels.begin();
pixels.show();

}

void loop()
{

 while (buttonChange == 1){
 crossFade(0,2,4,4,4,4,red,1,3,5,5,5,5,blue);
 crossFade(0,2,4,4,4,4,blue,1,3,5,5,5,5,red);
 pixels.setPixelColor(0, pixels.Color(0,0,255));
 pixels.setPixelColor(2, pixels.Color(0,0,255));
 pixels.setPixelColor(4, pixels.Color(0,0,255));
 crossFade(0,2,4,4,4,4,blue,1,3,5,5,5,5,blue);

 pixels.setPixelColor(1, pixels.Color(0,0,255));
 pixels.setPixelColor(3, pixels.Color(0,0,255));
 pixels.setPixelColor(5, pixels.Color(0,0,255));
 delay(1000);

 crossFade(0,0,0,0,0,0,red,1,2,3,4,5,5,blue);
 crossFade(0,0,0,0,0,0,blue,1,2,3,4,5,5,red);
  crossFade(0,0,0,0,0,0,blue,1,2,3,4,5,5,blue);

  }
 while (buttonChange == 2{
 crossFade(0,2,4,4,4,4,red,1,3,5,5,5,5,blue);
 crossFade(0,2,4,4,4,4,blue,1,3,5,5,5,5,red);
 pixels.setPixelColor(0, pixels.Color(255,0,0));
 pixels.setPixelColor(2, pixels.Color(255,0,0));
 pixels.setPixelColor(4, pixels.Color(255,0,0));
 crossFade(0,2,4,4,4,4,red,1,3,5,5,5,5,red);
 pixels.setPixelColor(1, pixels.Color(255,0,0));
 pixels.setPixelColor(3, pixels.Color(255,0,0));
 pixels.setPixelColor(5, pixels.Color(255,0,0));
 delay(1000);
 crossFade(3,3,3,3,3,3,blue,1,2,0,4,5,5,red);
 crossFade(3,3,3,3,3,3,red,1,2,0,4,5,5,blue);
 crossFade(3,3,3,3,3,3,red,1,2,0,4,5,5,red);


 }
}

void push1()
{
static unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = millis();
  if (interrupt_time - last_interrupt_time > 400){
buttonChange++;

    state = !state;
    Serial.print("PUSHED bll ");
    Serial.println(buttonChange);
    if (buttonChange>1){buttonChange = 0;}
  }
  last_interrupt_time = interrupt_time;
}

I’m not sure how fast / slow those neopixel (?) functions are. But replacing delay() by a millis() based solution will basically speed up the loop.

A modified blink example does show this (there is an example somewhere on this site as well if I’m not mistaken; search for blink without delay).

#define pinButton 7
#define pinLed 13
#define FLASHDELAY 1000

void setup() {

  pinMode(pinLed, OUTPUT);
  pinMode(pinButton, INPUT_PULLUP);
  digitalWrite(pinLed, LOW);

  Serial.begin(9600);
}

void loop() {
  static unsigned long prevMillis = 0;
  if (digitalRead(pinButton) == HIGH)
  {
    if (millis() - prevMillis > FLASHDELAY)
    {
      Serial.println(digitalRead(pinLed));
      digitalWrite(pinLed, digitalRead(pinLed) ^ 0x01);
      prevMillis = millis();
    }
  }
}

This way the loop() is a lot more responsive as there are no delays.

I did not dig deep in your code but the while loops probably also make loop() unresponsive.

Sterretje,

Thank you for your response. The neopixels are a product with a library produced by adafruit. They are nice RGB LEDs and I highly recommend them to anyone attempting to work with RGBLEDs and an arduino. NeoPixels Products Category on Adafruit Industries.

The function crossFade that I am using to fade between colors takes about a thousand steps. This happens each time crossFade is called and the code runs through all crossFade functions before checking the button state. This is the problem. I want it to abort the crossFade functions once a new button state is reached and see what while loop is appropriate for the new button state.

  • Rex

This might be a good application for the Timer library. I've found it easy to use, and quite flexible.

http://playground.arduino.cc/Code/Timer

Rather than delays, you can have a timer trigger a function (A) then function A start a new timer for function B, rather than using delay. It's essentially the millis() - prevMillis() > threshold statement that sterretje wrote, but could be easier to manage the code for it.

Also, millis() can overflow after a long time, meaning that your if statement will not return the expected result. I believe the Timer library handles this, but I couldn't say for sure.

Newtonian:
Also, millis() can overflow after a long time, meaning that your if statement will not return the expected result.

As long as you use unsigned long variables and use code similar to the code below, then you don't need to worry about millis() rolling over.

  if (millis() - previousTime > timeInterval)

@rexhex, We (at least I do) really need to see your full code to know how you'd start back at the beginning.

DuaneDegn:
@rexhex, We (at least I do) really need to see your full code to know how you'd start back at the beginning.

I think it's the full code :wink:

rexhex:
I want it to abort the crossFade functions once a new button state is reached and see what while loop is appropriate for the new button state.

You can not abort 3rd party functions easily; if a call to crossfade takes 5 seconds before it returns, it takes 5 seconds and there is not much that you can do about it. But you can break from the while loop after each function call in that while loop. I'm not familiar with the neopixel library so don't know if there are options to abort e.g. a crossfade.

Below is probably not the neatest but in its simplest form (not tested)

  while (buttonChange == 1) {
    crossFade(0, 2, 4, 4, 4, 4, red, 1, 3, 5, 5, 5, 5, blue);
    if (buttonChange != 1)
      break;
    crossFade(0, 2, 4, 4, 4, 4, blue, 1, 3, 5, 5, 5, 5, red);
    if (buttonChange != 1)
      break;
    pixels.setPixelColor(0, pixels.Color(0, 0, 255));
    if (buttonChange != 1)
      break;
    pixels.setPixelColor(2, pixels.Color(0, 0, 255));
    if (buttonChange != 1)
      break;
    pixels.setPixelColor(4, pixels.Color(0, 0, 255));
    if (buttonChange != 1)
      break;
    crossFade(0, 2, 4, 4, 4, 4, blue, 1, 3, 5, 5, 5, 5, blue);
    if (buttonChange != 1)
      break;

    pixels.setPixelColor(1, pixels.Color(0, 0, 255));
    if (buttonChange != 1)
      break;
    pixels.setPixelColor(3, pixels.Color(0, 0, 255));
    if (buttonChange != 1)
      break;
    pixels.setPixelColor(5, pixels.Color(0, 0, 255));
    if (buttonChange != 1)
      break;
    delay(1000);
    if (buttonChange != 1)
      break;

    crossFade(0, 0, 0, 0, 0, 0, red, 1, 2, 3, 4, 5, 5, blue);
    if (buttonChange != 1)
      break;
    crossFade(0, 0, 0, 0, 0, 0, blue, 1, 2, 3, 4, 5, 5, red);
    if (buttonChange != 1)
      break;
    crossFade(0, 0, 0, 0, 0, 0, blue, 1, 2, 3, 4, 5, 5, blue);
    if (buttonChange != 1)
      break;

  }

The break gets you out of the while() loop and the program will continue with the next while(). Do the same for the other while() loop.

As said, possibly not the neatest. Further you can implement a statemachine. Each crossfade / pixel / delay call becomes a state.

void loop()
{
  static int loopstate1 = 0;
  static int loopstate2 = 0;

  if (buttonChange == 1)
  {
    // guarantee that if buttonChange changes, we start the buttonChange == 2 statemachine at the beginning again
    loopstate2 = 0;

    // simple statemachine for buttonChange 1
    switch (loopstate1)
    {
      case 0:
        crossFade(0, 2, 4, 4, 4, 4, red, 1, 3, 5, 5, 5, 5, blue);
        loopstate1++;
        break;
      case 1:
        crossFade(0, 2, 4, 4, 4, 4, blue, 1, 3, 5, 5, 5, 5, red);
        loopstate1++;
        break;
      case 2:
        pixels.setPixelColor(0, pixels.Color(0, 0, 255));
        loopstate1++;
        break;
      case 3:
        pixels.setPixelColor(2, pixels.Color(0, 0, 255));
        loopstate1++;
        break;
      case 4:
        pixels.setPixelColor(4, pixels.Color(0, 0, 255));
        loopstate1++;
        break;
      case 5:
        crossFade(0, 2, 4, 4, 4, 4, blue, 1, 3, 5, 5, 5, 5, blue);
        loopstate1++;
        break;
      case 6:
        pixels.setPixelColor(1, pixels.Color(0, 0, 255));
        loopstate1++;
        break;
      case 7:
        pixels.setPixelColor(3, pixels.Color(0, 0, 255));
        loopstate1++;
        break;
      case 8:
        pixels.setPixelColor(5, pixels.Color(0, 0, 255));
        loopstate1++;
        break;
      case 9:
        delay(1000);
        loopstate1++;
        break;
      case 10:
        crossFade(0, 0, 0, 0, 0, 0, red, 1, 2, 3, 4, 5, 5, blue);
        loopstate1++;
        break;
      case 11:
        crossFade(0, 0, 0, 0, 0, 0, blue, 1, 2, 3, 4, 5, 5, red);
        loopstate1++;
        break;
      case 12:
        crossFade(0, 0, 0, 0, 0, 0, blue, 1, 2, 3, 4, 5, 5, blue);
        loopstate1 = 0;
        break;
    } // end_of_switch
  } // end_of_if(buttonChange == 1)

  if (buttonChange == 2)
  {
    // guarantee that if buttonChange changes, we start the buttonChange == 1 statemachine at the beginning again
    loopstate1 = 0;

    // simple statemachine for buttonChange 2
    switch (loopstate2)
    {
      ... for you to complete
    }
  }
}

Not tested but the above will basically loop through states using a counter; when the last stage is reached, the counter is reset so the code starts with the first state again. This will also make it easier to implement a millis() based delay. It will be cleaner (your 'code blocks' become smaller) to use a function for each of the buttonChange statemachines but I leave that up to you.

By the way,
1)
as far as I can see buttonChange can never be 2.
2)
this line will cause compiler errors

while (buttonChange == 2{

rexhex:
I am working on a program that switches between two different sets of functions with a button. I learned that an interrupt is important for my application.

Then you learned dead wrong!

rexhex:
The program goes through many steps. It would be unrealistic to place a bunch of button reads in the code leading to an interrupt as ideal.

Well, no, because that is the way to do it.

rexhex:
So far the results with an interrupt are making me happy minus one aspect.

An interrupt resumes the code right where it left off.

Exactly. That is precisely what an "interrupt" is for. "Noobs" fantasise that it is somehow a way to "interrupt" the flow of their program. It is not.

rexhex:
What I would like to have is the interrupt start from the beginning of void loop. This way when you push a button you instantly see a change and don't keep pressing the button.

Then you have to write the code to do it.

rexhex:
I'm sure there is a way to get the button to be noticed at any point in the code and make an immediate change.

There is. It's called "polling". You check the button at regular intervals.

So let's get down to business.

Your concern is the tedium of adding this poll at every step, and providing a neat way to change tack when you detect a button press. OK, thee are two practical ways of doing this.

One (as I now note sterretje is describing) is to write functions such as your "crossFade()" which perform the poll for the pushbutton. This is dead simple, you merely write a new function "crossFadeIf()" which first does the button check and if that returns FALSE, proceeds to call the original crossFade() with the arguments specified and itself returns FALSE. At each step of your overall procedure, you call the function with an "if ... return(TRUE)" and encapsulate this overall procedure within a function. So this overall function will internally execute all its steps unless at any point, the button check becomes TRUE when each function within will promptly exit the onion.

The alternative and more flexible approach is a state machine. In this version, the outside loop() "spins" at full speed executing only one instruction (of your fading) on each traversal. This is controlled by a switch value which determines which function is executed on each traversal, being incremented each time. A second switch, toggled by your button code at the start of the loop(), controls which of the alternative arrays is used.

You can use a switch statement for this, or preferably, an array whose elements are the pointer to the function for each step plus the arguments to that function. (If you are clever enough, the actual functions themselves are in the array!)

This actually makes the array itself your "meta-code", and arguably simplifies the task of creating new patterns as each step becomes simply an array (compound) element.

The array would look something like:

fade01[][] =
{{&crossFade,0,2,4,4,4,4,red,1,3,5,5,5,5,blue},
 {&crossFade,0,2,4,4,4,4,blue,1,3,5,5,5,5,red},
 {&pixels.setPixelColoras,0, 0,0,255},
 {&pixels.setPixelColoras,2, 0,0,255},
 {&pixels.setPixelColoras,4, 0,0,255},
 {&crossFade,0,2,4,4,4,4,blue,1,3,5,5,5,5,blue}
}

DuaneDegn, the code contains too many characters for a single post. Here are the functions for the crossFade().

int calculateStep(int prevValue, int endValue) {
  int step = endValue - prevValue; // What's the overall gap?
  if (step) {                      // If its non-zero, 
    step = 1020/step;              //   divide by 1020
  } 
  return step;
}


int calculateVal(int step, int val, int i) {

  if ((step) && i % step == 0) { // If step is non-zero and its time to change a value,
    if (step > 0) {              //   increment the value if step is positive...
      val += 1;           
    } 
    else if (step < 0) {         //   ...or decrement it if step is negative
      val -= 1;
    } 
  }
  // Defensive driving: make sure val stays in the range 0-255
  if (val > 255) {
    val = 255;
  } 
  else if (val < 0) {
    val = 0;
  }
  return val;
}



void crossFade(int p0, int p1, int p2, int p3, int p4, int p5, int color[3],int p6, int p7, int p8, int p9, int p10, int p11, int color2[3]) {
  // Convert to 0-255
  int R = (color[0] * 255) / 100;
  int G = (color[1] * 255) / 100;
  int B = (color[2] * 255) / 100;

  int stepR = calculateStep(prevR, R);
  int stepG = calculateStep(prevG, G); 
  int stepB = calculateStep(prevB, B);
  
  int R2 = (color2[0] * 255) / 100;
  int G2 = (color2[1] * 255) / 100;
  int B2 = (color2[2] * 255) / 100;

  int stepR2 = calculateStep(prevR2, R2);
  int stepG2 = calculateStep(prevG2, G2); 
  int stepB2 = calculateStep(prevB2, B2);

  for (int i = 0; i <= 1020; i++) {
    redVal = calculateVal(stepR, redVal, i);
    grnVal = calculateVal(stepG, grnVal, i);
    bluVal = calculateVal(stepB, bluVal, i);
    redVal2 = calculateVal(stepR2, redVal2, i);
    grnVal2 = calculateVal(stepG2, grnVal2, i);
    bluVal2 = calculateVal(stepB2, bluVal2, i);

pixels.setPixelColor(p0, pixels.Color(redVal,grnVal,bluVal));
pixels.setPixelColor(p1, pixels.Color(redVal,grnVal,bluVal));
pixels.setPixelColor(p2, pixels.Color(redVal,grnVal,bluVal));
pixels.setPixelColor(p3, pixels.Color(redVal,grnVal,bluVal));
pixels.setPixelColor(p4, pixels.Color(redVal,grnVal,bluVal));
pixels.setPixelColor(p5, pixels.Color(redVal,grnVal,bluVal));



pixels.setPixelColor(p6, pixels.Color(redVal2,grnVal2,bluVal2));
pixels.setPixelColor(p7, pixels.Color(redVal2,grnVal2,bluVal2));
pixels.setPixelColor(p8, pixels.Color(redVal2,grnVal2,bluVal2));
pixels.setPixelColor(p9, pixels.Color(redVal2,grnVal2,bluVal2));
pixels.setPixelColor(p10, pixels.Color(redVal2,grnVal2,bluVal2));
pixels.setPixelColor(p11, pixels.Color(redVal2,grnVal2,bluVal2));
pixels.show();


    delay(wait); // Pause for 'wait' milliseconds before resuming the loop

    if (DEBUG) { // If we want serial output, print it at the 
      if (i == 0 or i % loopCount == 0) { // beginning, and every loopCount times
        Serial.print("Loop/RGB: #");
        Serial.print(i);
        Serial.print(" | ");
        Serial.print(redVal);
        Serial.print(" / ");
        Serial.print(grnVal);
        Serial.print(" / ");  
        Serial.println(bluVal); 
      } 
      DEBUG += 1;
    }
  }
  // Update current values for next loop
  prevR = redVal; 
  prevG = grnVal; 
  prevB = bluVal;
  prevR2 = redVal2; 
  prevG2 = grnVal2; 
  prevB2 = bluVal2;
  delay(hold); // Pause for optional 'wait' milliseconds before resuming the loop
}

sterretje and Paul__B, Thank you for this information. This is exactly what I was hoping to learn on this post. Both of these methods look like they will work perfectly for my application. It is not that I did not want to do any polling but that it seems unrealistic to have a normal button debounce at every step of the code. This is why I am happy to have learned of the interrupt feature which will let me use these two methods well. I am going to read up on the state machine as it has been mentioned to be a superior method. As of this moment I am going to alter the code with the while loops and breaks creating that good old “onion”.

““Noobs” fantasise that it is somehow a way to “interrupt” the flow of their program. It is not.” Thats funny. I was fantasizing that it might do this but mainly that it allowed me to see a button press immediately no matter where the code was.

Thank you for all of your help. I am very thankful for this community always leading me in the right direction on my projects.

-Rex

also, sterretje, good catch! "while (buttonChange == 2{" It was not running as it did before because i deleted that ")" but have been to concerned with the button situation to investigate.

rexhex:
DuaneDegn, the code contains too many characters for a single post. Here are the functions for the crossFade().

You can attach the file to your reply. It’s more work on our part to look at the code but it sure beat trying to figure out what’s happening from only part of the program.

rexhex:
It is not that I did not want to do any polling but that it seems unrealistic to have a normal button debounce at every step of the code.

OK, so here is the next level of the process.

De-bouncing is the tricky part. Interrupts would make it particularly hard as the processor is capable of responding to every bounce of the button with an interrupt, which then interferes unnecessarily with the running of the main program. You really want the debounce routine to be under your (program) control, rather than under control of the bouncy button.

Now there are two approaches to de-bouncing code. One is to poll the switch state and then simply poll it again (if and only if its state is “new”) after the nominal bounce interval, say 10 ms so if it is in the “new” state at both start and end of that interval, it is presumed now to be stable. In fact, if you intersperse your button poll calls to the pixel routines, then the delays that are presently causing you concerns, will actually perform that delay for you, at least those that actually move data to the NeoPixels, however not all calls take the same amount of time and in fact, they vary considerably. But that may be good enough; your debounce routine then is just to see if the new button state is maintained across successive calls to the button routine.

My preferred debounce method is to check the button state on every pass around the loop, and only consider it stable if is the same (and new) state on every poll within the nominal debounce time. In general, polling the button is just as easy as polling the time (millis()) so the time check need only be done on each pass if the button is staying in a “new” state and you only perform that time check during the debounce verification interval anyway.

That said, in your case, the first method will probably suffice.


I think in designing your code, you really need to understand what each of those “pixel” calls do, and which ones take a substantial time. Those that do not could be grouped together to work within a single poll of the button and the time that group takes, be considered to be the debounce time.

Sorry about the late response, I was away in the trinity alps. It was a fantastic time.

The pixel.setPixelColor stores data for a specific LED and its color value. When pixel.show() is called it sends all of the data to the LEDs. These calls do not take up much time at all.

I was able to get the button to work at any point with all of your help. I am polling in the crossfade function which has a for loop. I also poll after any pixel.setPixelColor in the main loop but I think its not necessary.

#include <Adafruit_NeoPixel.h>

#define PIN 2

int buttonPin = 9;
volatile int state = HIGH;
int buttonChange = 1;
int prevButtonChange = 1;
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(6, PIN, NEO_GRB + NEO_KHZ800);

int black[3]  = { 0, 0, 0 };
int white[3]  = { 0, 0, 0 };
int red[3]    = { 100, 0, 0 };
int redGreen[3] = { 0, 0, 100 };
int redBlue[3] = { 0, 100, 0 };
int green[3]  = { 100, 0, 100 };
int greenBlue[3] ={ 100, 0, 0};
int blue[3]   = { 0, 0, 100 };
int yellow[3] = { 60, 5, 100 };
int dimWhite[3] = { 70, 70, 70 };

int redVal = black[0];
int grnVal = black[1]; 
int bluVal = black[2];
int redVal2 = black[0];
int grnVal2 = black[1]; 
int bluVal2 = black[2];

int wait = 2;
int hold = 0;
int DEBUG = 1;
int loopCount = 60;
int repeat = 0;
int j = 0; 

int prevR = redVal;
int prevG = grnVal;
int prevB = bluVal;

int prevR2 = redVal2;
int prevG2 = grnVal2;
int prevB2 = bluVal2;

void setup()
{
pinMode(buttonPin,INPUT);
attachInterrupt(buttonPin, push1, CHANGE);
pixels.begin();
pixels.show();

  if (DEBUG) {
    Serial.begin(9600); 
  }
}

void loop()
{
  
prevButtonChange = buttonChange;

 while (buttonChange == 1){
 crossFade(0,2,4,4,4,4,red,1,3,5,5,5,5,blue);
 crossFade(0,2,4,4,4,4,blue,1,3,5,5,5,5,red);
 pixels.setPixelColor(0, pixels.Color(0,0,255));
   if (buttonChange != prevButtonChange){
    break;}
 pixels.setPixelColor(2, pixels.Color(0,0,255));
   if (buttonChange != prevButtonChange){
    break;}
 pixels.setPixelColor(4, pixels.Color(0,0,255));
    if (buttonChange != prevButtonChange){
    break;}
 crossFade(0,2,4,4,4,4,blue,1,3,5,5,5,5,blue);
 pixels.setPixelColor(1, pixels.Color(0,0,255));
    if (buttonChange != prevButtonChange){
    break;}
 pixels.setPixelColor(3, pixels.Color(0,0,255));
    if (buttonChange != prevButtonChange){
    break;}
 pixels.setPixelColor(5, pixels.Color(0,0,255));
   if (buttonChange != prevButtonChange){
    break;}
 delay(1000);
 crossFade(0,0,0,0,0,0,red,1,2,3,4,5,5,blue);
 crossFade(0,0,0,0,0,0,blue,1,2,3,4,5,5,red);
  crossFade(0,0,0,0,0,0,blue,1,2,3,4,5,5,blue);
  }
 while (buttonChange == 2){
 crossFade(0,2,4,4,4,4,red,1,3,5,5,5,5,blue);
 crossFade(0,2,4,4,4,4,blue,1,3,5,5,5,5,red);
 pixels.setPixelColor(0, pixels.Color(255,0,0));
   if (buttonChange != prevButtonChange){
    break;}
 pixels.setPixelColor(2, pixels.Color(255,0,0));
    if (buttonChange != prevButtonChange){
    break;}
 pixels.setPixelColor(4, pixels.Color(255,0,0));
   if (buttonChange != prevButtonChange){
    break;}
 crossFade(0,2,4,4,4,4,red,1,3,5,5,5,5,red);
   if (buttonChange != prevButtonChange){
   break;}
 pixels.setPixelColor(1, pixels.Color(255,0,0));
   if (buttonChange != prevButtonChange){
    break;}
 pixels.setPixelColor(3, pixels.Color(255,0,0));
   if (buttonChange != prevButtonChange){
    break;}
 pixels.setPixelColor(5, pixels.Color(255,0,0));
   if (buttonChange != prevButtonChange){
    break;}
 delay(1000);
 crossFade(3,3,3,3,3,3,blue,1,2,0,4,5,5,red);
 crossFade(3,3,3,3,3,3,red,1,2,0,4,5,5,blue);
 crossFade(3,3,3,3,3,3,red,1,2,0,4,5,5,red);
 }
}

void push1()
{
static unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = millis();
  if (interrupt_time - last_interrupt_time > 400){
buttonChange++;
    Serial.print("PUSHED bll ");
    Serial.println(buttonChange);
    if (buttonChange==3){buttonChange = 1;}
  }
  last_interrupt_time = interrupt_time;
      Serial.print("PUSHED bll ");
    Serial.println(buttonChange);
}

int calculateStep(int prevValue, int endValue) {
  int step = endValue - prevValue; 
  if (step) {
    step = 1020/step;
  } 
  return step;
}


int calculateVal(int step, int val, int i) {

  if ((step) && i % step == 0) { 
    if (step > 0) { 
      val += 1;           
    } 
    else if (step < 0) { 
      val -= 1;
    } 
  }
  if (val > 255) {
    val = 255;
  } 
  else if (val < 0) {
    val = 0;
  }
  return val;
}

void crossFade(int p0, int p1, int p2, int p3, int p4, int p5, int color[3],int p6, int p7, int p8, int p9, int p10, int p11, int color2[3]) {
  int R = (color[0] * 255) / 100;
  int G = (color[1] * 255) / 100;
  int B = (color[2] * 255) / 100;

  int stepR = calculateStep(prevR, R);
  int stepG = calculateStep(prevG, G); 
  int stepB = calculateStep(prevB, B);
  
  int R2 = (color2[0] * 255) / 100;
  int G2 = (color2[1] * 255) / 100;
  int B2 = (color2[2] * 255) / 100;

  int stepR2 = calculateStep(prevR2, R2);
  int stepG2 = calculateStep(prevG2, G2); 
  int stepB2 = calculateStep(prevB2, B2);

  for (int i = 0; i <= 1020; i++) {
    redVal = calculateVal(stepR, redVal, i);
    grnVal = calculateVal(stepG, grnVal, i);
    bluVal = calculateVal(stepB, bluVal, i);
    redVal2 = calculateVal(stepR2, redVal2, i);
    grnVal2 = calculateVal(stepG2, grnVal2, i);
    bluVal2 = calculateVal(stepB2, bluVal2, i);

pixels.setPixelColor(p0, pixels.Color(redVal,grnVal,bluVal));
pixels.setPixelColor(p1, pixels.Color(redVal,grnVal,bluVal));
pixels.setPixelColor(p2, pixels.Color(redVal,grnVal,bluVal));
pixels.setPixelColor(p3, pixels.Color(redVal,grnVal,bluVal));
pixels.setPixelColor(p4, pixels.Color(redVal,grnVal,bluVal));
pixels.setPixelColor(p5, pixels.Color(redVal,grnVal,bluVal));



pixels.setPixelColor(p6, pixels.Color(redVal2,grnVal2,bluVal2));
pixels.setPixelColor(p7, pixels.Color(redVal2,grnVal2,bluVal2));
pixels.setPixelColor(p8, pixels.Color(redVal2,grnVal2,bluVal2));
pixels.setPixelColor(p9, pixels.Color(redVal2,grnVal2,bluVal2));
pixels.setPixelColor(p10, pixels.Color(redVal2,grnVal2,bluVal2));
pixels.setPixelColor(p11, pixels.Color(redVal2,grnVal2,bluVal2));
pixels.show();

   if (buttonChange != prevButtonChange){
    break;}
    delay(wait); 

    if (DEBUG) { 
      if (i == 0 or i % loopCount == 0) {
        Serial.print("Loop/RGB: #");
        Serial.print(i);
        Serial.print(" | ");
        Serial.print(redVal);
        Serial.print(" / ");
        Serial.print(grnVal);
        Serial.print(" / ");  
        Serial.println(bluVal); 
      } 
      DEBUG += 1;
    }
  }
  prevR = redVal; 
  prevG = grnVal; 
  prevB = bluVal;
  prevR2 = redVal2; 
  prevG2 = grnVal2; 
  prevB2 = bluVal2;
  delay(hold); 
}
void setSolidColor(int p0, int p1, int p2, int p3, int p4, int p5, int color[3],int p6, int p7, int p8, int p9, int p10, int p11, int color2[3]) {
  int R = (color[0] * 255) / 100;
  int G = (color[1] * 255) / 100;
  int B = (color[2] * 255) / 100;
  pixels.setPixelColor(p0, pixels.Color(R,grnVal,bluVal));
pixels.setPixelColor(p1, pixels.Color(R,grnVal,bluVal));
pixels.setPixelColor(p2, pixels.Color(R,grnVal,bluVal));
pixels.setPixelColor(p3, pixels.Color(R,grnVal,bluVal));
pixels.setPixelColor(p4, pixels.Color(R,grnVal,bluVal));
pixels.setPixelColor(p5, pixels.Color(R,grnVal,bluVal));

pixels.setPixelColor(p6, pixels.Color(R,grnVal2,bluVal2));
pixels.setPixelColor(p7, pixels.Color(R,grnVal2,bluVal2));
pixels.setPixelColor(p8, pixels.Color(R,grnVal2,bluVal2));
pixels.setPixelColor(p9, pixels.Color(R,grnVal2,bluVal2));
pixels.setPixelColor(p10, pixels.Color(R,grnVal2,bluVal2));
pixels.setPixelColor(p11, pixels.Color(R,grnVal2,bluVal2));
}

It is not necessary to have all those button reads. A state machine will do it.

Here is a sketch I wrote for LED patterns that is quite similar to what you are doing.

I shouldn’t post this because I was playing with converting it to using port manipulation and never got a chance to finish it. but everything else is working.

just think of the PORTD commands as where you put your crossfades and
replace the (!(PIND & (1 << PD2))) with a digitalRead (or you could move your button to pin 2) otherwise it should work fine for your application.

switch(pattern) in loop() controls the current pattern

switch (patternstep) in the pattern functions controls the current step in the current pattern

it reads the button and the potentiometer after every patternstep

Shouldn’t be to hard to convert it for your project.

I’m heading to bed now but if you have any questions I’ll check back in the morning.

////////////Output Pins 3,4,5,6,7 \\\\\\\\\\\\\\\\\
const byte potPin = A0;                            // Analog Input Pin
byte patternStep, pattern, intervalModified;    // Counters/Flags
unsigned long interval, starTime;               // Timing

void setup()
{
  Serial.begin(9600);
  DDRD = 0xFA;         // Set Pins 1 & 3-7  As Outputs, Pins 0,2 as inputs
  PORTD = 0xFC;        // Set INPUT_PULLUP on button, Initialize Outputs LOW
}


void loop()
{
  pattern += buttonCheck();             // Increment pattern when button pressed
  if (pattern > 7) pattern = 0;
  Serial.println(pattern);
  unsigned long currenTime = millis();
  if (currenTime - starTime >= interval)
  {
    patternStep++;
    starTime += interval;
    interval = potRead();
    intervalModified = 0;
    Serial.println("Time's up");
  }

  switch (pattern) {
    case 0:   // All On
      PORTD |= 0xF8;                  // Turn Off All Lights
      Serial.println("0 executed");
      break;
    case 1:     
      followTheLeader();
      Serial.println("Pattern 1");
      break;
    case 2:
      scanner();
      break;
    case 3:
      negScanner();
      break;
    case 4:
      upndown();
      break;
    case 5:
      centerBloom();
      break;
    case 6:
      all();
      break;
    case 7:   // All Off
      PORTD |= 0xF8;             // Turn On All Lights
      break;
  }
}


int potRead()     // Sets Base Interval
{
  int potValue;
  
  potValue = analogRead(potPin);       // Read Potentiometer
  potValue = map(potValue, 0, 1023, 850, 50);
  return potValue;
}


byte buttonCheck()   // Return positive Once per press
{
  static int isButtonPressed = 0;

  if (!(PIND & (1 << PD2)))       // Read button
  {
    isButtonPressed++;                // Increment Debounce variable
  } else {
    isButtonPressed = 0;
  }

  if (isButtonPressed == 3)           // confirm valid Button Press adjust for your button
  {
    starTime = millis();
    patternStep = 0;                
    PORTD = 0x04;                    // Turn Off All Lights
    return 1;
  } else {
    return 0;
  }
}


void followTheLeader()      // Light up all the LEDs in turn then shut them off in turn
{
  switch (patternStep) {
    case 0:                 // | PATTERN |
      PORTD &= ~0x08;       //  0 0 0 0 1  010
      Serial.println("Step 0");
      break;
     
    case 1:
      PORTD &= ~0x10;        //  0 0 0 1 1  010
      Serial.println("Step 1");
      break;
     
    case 2:
      PORTD &= ~0x20;        //  0 0 1 1 1  010
      Serial.println("Step 2");
      break;
     
    case 3:
      PORTD &= ~0x40;        //  0 1 1 1 1  010
      Serial.println("Step 3");
      break;
     
    case 4:
      PORTD &= ~0x80;        //  1 1 1 1 1  010
      Serial.println("Step 4");
      if (!intervalModified)
      {
      interval /= 4;        //  1/4 Interval
      intervalModified = 1;
      }
      break;
     
    case 5:
      PORTD |= 0x08;       //  1 1 1 1 0  010
      Serial.println("Step 5");
      break;
     
    case 6:
      PORTD |= 0x10;       //  1 1 1 0 0  010
      Serial.println("Step 6");
      break;
     
    case 7:
      PORTD |= 0x20;       //  1 1 0 0 0  010
      Serial.println("Step 7");
      break;
     
    case 8:
      PORTD |= 0x40;       //  1 0 0 0 0  010
      Serial.println("Step 8");
      break;
     
    case 9:
      PORTD |= 0x80;       //  0 0 0 0 0  010
      patternStep = 0;
      Serial.println("Step 9");
      break;
  }
}


void centerBloom()          // start in center expand then contract
{
  switch (patternStep)
  {
    case 0:
      PORTD |= 0x20;            // Turns on LED #2 (pin 5)
      break;
    case 1:
      PORTD |= 0x50;            // Turns on LED #1 & LED #3 (pins 4,6)
      break;
    case 2:
      PORTD |= 0xf4;            // Turns on LED #0 & LED#4 (pins 3,7)
      break;
      interval *= 2;
    case 3:
      PORTD &= ~0xf4;             // Turn off LED #0 & LED#4 (pins 3,7)
      break;
    case 4:
      PORTD &= ~0x50;             // Turn off LED #1 & LED #3 (pins 4,6)
      break;
    case 5:
      PORTD &= ~0x20;             // Turn off LED #2 (pin 5)
      patternStep = 0;
      interval *= 2;
      break;

  }
}


void scanner()
{
  switch (patternStep)
  {
    case 0:
      PORTD = 0x10;   // LED #0 OFF(pin 3)& LED #1 ON(pin 4)
      break;
    case 1:
      PORTD = 0x20;   // LED #1 OFF(pin 4)& LED #2 ON(pin 5)
      break;
    case 2:
      PORTD = 0x40;   // LED #2 OFF(pin 5)& LED #3 ON(pin 6)
      break;
    case 3:
      PORTD = 0x80;   // LED #3 OFF(pin 6)& LED #4 ON(pin 7)
      interval *= 2;
      break;
    case 4:
      PORTD = 0x40;   // LED #3 ON(pin 6)& LED #4 OFF(pin 7)
      break;
    case 5:
      PORTD = 0x20;   // LED #2 ON(pin 5)& LED #3 OFF(pin 6)
      break;
    case 6:
      PORTD = 0x10;   // LED #1 ON(pin 4)& LED #2 OFF(pin 5)
      break;
    case 7:
      PORTD = 0x08;   // LED #0 ON(pin 3)& LED #1 OFF(pin 4)
      patternStep = 0;
      break;
  }
}


void negScanner()
{
  switch (patternStep)
  {
    case 0:
      PORTD = 0xf0;
      break;
    case 1:
      PORTD = 0xE8;
      break;
    case 2:
      PORTD = 0xD8;
      break;
    case 3:
      PORTD = 0xB8;
    case 4:
      PORTD = 0x78;
      interval *= 2;
      break;
    case 5:
      PORTD = 0xB8;
      break;
    case 6:
      PORTD = 0xD8;
      break;
    case 7:
      PORTD = 0xE8;
      break;
    case 8:
      PORTD = 0x08;
      interval *= 2;
      patternStep = 0;
      break;
  }
}


void upndown()
{
  switch (patternStep)
  {
    case 0:
      PORTD |= 0x08;
      break;
    case 1:
      PORTD |= 0x10;
      break;
    case 2:
      PORTD |= 0x20;
      break;
    case 3:
      PORTD |= 0x40;
      break;
    case 4:
      PORTD |= 0x80;
      break;
    case 5:
      PORTD &= ~0x80;
      break;
    case 6:
      PORTD &= ~0x40;
      break;
    case 7:
      PORTD &= ~0x20;
      break;
    case 8:
      PORTD &= ~0x10;
      break;
    case 9:
      PORTD &= ~0x08;
      patternStep = 0;
      break;
  }
}

Hutkikz, Thank you for your post with code from a project you have done in the past. paul and sterretje mentioned state machines as a better way of doing this. I still am not sure how they work and have not done much research on them yet. I would like to get to it when I have time because they seem to be more clean. Thankfully what I have now is working but it will be nice later to clean it up with a state machine.

again thank you for your response,

Rex