Well interrupts can't do that because as soon as an interrupt has finished doing its thing then it returns to the exact place in the code it was in before the interrupt took place. If this is in the middle of a delay it will still be in the delay and the delay will finish as if nothing has happened. It is only when your code gets to look at the pattern number to run will any change be noticed, which unless you do something special, will be at the end of the pattern cycle.
Most of the patterns you see on line are written with delay functions in them so can't just copy any code once you get round to adding patterns to your code.
The secrete to stopping this is to write everything as a state machine. This is a sort of pseudo multitasking where the code is written so that checking what needs to be done next is done as fast as possible.
When you do this it solves your other two problems at the same time.
- Checking if your next button has been presses can be done in the code with no need to use interrupts.
- Checking if you need to shut down does not need an interrupt or a timer, it is just another task your code checks to see if it is time to do.
The blink without delay is an ultra simplified version of this technique that just blinks two LEDs independently, the idea is that you take that technique and make it do as many tasks as you want, but that is a big step for a beginner and there are many posts that explain that task.
I have my own take on that task here:-
http://www.thebox.myzen.co.uk/Tutorial/State_Machine.html
The forum has a large stick post dedicated to this problem as well:-
https://forum.arduino.cc/t/demonstration-code-for-several-things-at-the-same-time/217158
I have posted an example of LED patterns written as a state machine before but here it is again. It takes patterns given as examples in two main addressable strip libraries and shows how they should be written as a state machine.
// Multiple patterns in a state machine format
// using the FastLED library
// by Mike Cook 2017
#include "FastLED.h"
// first set up the parameters to use in the pattern calling
unsigned long patternInterval [] = { 500, 40, 20, 200, 5 }; // how often each pattern updates
unsigned long lastUpdate [5] ; // for millis() when last update occurred
boolean patternEnabled [] = {true,true,false,true,true}; // should the pattern be called at all
byte patternState[5]; // state machine variable for patterns - this initialises them to zero
// now set up the LEDs to use
#define NUM_LEDS 64
#define DATA_PIN 3
#define CLOCK_PIN 13
CRGB leds[NUM_LEDS];
// Constants for patterns
// for Fire2012
#define COOLING 20
#define SPARKING 50
#define COLOR_ORDER BGR
// now set up the array of pointers to each pattern
void (*patternPtrs[5])(int index,byte state); //the array of pattern pointers
void setup() {
//initialises the array of pattern pointers
patternPtrs[0] = blinkOne;
patternPtrs[1] = cylon;
patternPtrs[2] = fire;
patternPtrs[3] = colorWipe;
patternPtrs[4] = rainbowCycle;
//initialises the FastLED driver you want
//FastLED.addLeds<APA102,leds, NUM_LEDS); // 13 clock and 11 data
FastLED.addLeds<APA102, DATA_PIN, CLOCK_PIN, BGR>(leds, NUM_LEDS); // 13 clock and 11 data
FastLED.setBrightness(8);
}
void loop() {
for(int i = 0; i<5; i++) { // go through all the patterns and see if it is time to call one
if(patternEnabled[i] && millis() - lastUpdate[i] > patternInterval[i]){
lastUpdate[i] = millis();
callPatterns(i, patternState[i]);
}
}
}
void callPatterns(int index, byte state) {
(*patternPtrs[index])(index,state); //calls the pattern at the index of `index` in the array
}
// These are the pattern functions written as a state machine
// this is the Blink program in FastLED's example folder
void blinkOne(int index,byte state) {
if(state == 0){
leds[3] = CRGB::Blue;
FastLED.show();
patternState[index] = 1; // move on the state machine for the next call
}
if(state == 1){
leds[3] = CRGB::Black;
FastLED.show();
patternState[index] = 0;
}
}
// this is the Cylon program in FastLED's example folder
// we will use LEDs 8 to 15 to show this
void cylon(int index,byte state) {
static int i = 8; // replaces the loop index
if(state == 0){
leds[i] = CRGB::Red;
FastLED.show();
patternState[index] = 1; // move on the state machine for the next call
}
if(state == 1){
// now that we've shown the leds, reset the i'th led to black
leds[i] = CRGB::Black;
i++; // increment what was the loop variable
if(i >= 16){ // we have finished one direction
patternState[index] = 2;
i--;
}
else {
patternState[index] = 0;
}
}
// Now go in the other direction only green
if(state == 2){
leds[i] = CRGB::Green;
FastLED.show();
patternState[index] = 3; // move on the state machine for the next call
}
if(state == 3){
// now that we've shown the leds, reset the i'th led to black
leds[i] = CRGB::Black;
i--; // decrement what was the loop variable
if(i < 8){ // we have finished the return, go back to the start
patternState[index] = 0;
i= 8; // ready to start again
}
else {
patternState[index] = 2;
}
// note that this could be better implemented but it has been written like this to keep it close to the original example
// so you can see what changes have been made
}
}
// this is the Fire2012 program in FastLED's example folder
void fire(int index,byte state) {
// using LEDs 16 to 32
// Array of temperature readings at each simulation cell
const byte startLED = 16; // first LED in section
const byte numLEDs = 16;
static byte heat[numLEDs];
random16_add_entropy( random());
// Step 1. Cool down every cell a little
for( int i = 0; i < numLEDs; i++) {
heat[i] = qsub8( heat[i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2));
}
// Step 2. Heat from each cell drifts 'up' and diffuses a little
for( int k= numLEDs - 1; k >= 2; k--) {
heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
}
// Step 3. Randomly ignite new 'sparks' of heat near the bottom
if( random8() < SPARKING ) {
int y = random8(7);
heat[y] = qadd8( heat[y], random8(160,255) );
}
// Step 4. Map from heat cells to LED colors
for( int j = 0; j < numLEDs; j++) {
leds[j+startLED] = HeatColor( heat[j]); // transfer heat array to LEDs
}
FastLED.show(); // display this frame
}
//colorWipe modified from Adafruit example to make it a state machine
// uses LEDs 32 to 40
void colorWipe(int index,byte state) {
static int i =0; // used as state variable
static byte firstLED = 32;
static byte numLEDs = 8;
leds[i+firstLED] = CRGB::Yellow;
FastLED.show();
i++;
if(i >= numLEDs){
i = 0;
for(int j;j<numLEDs; j++) leds[j+firstLED] = CRGB::Black; // blank out strip
}
}
// rainbowCycle modified from Adafruit example to make it a state machine
// uses LEDs 48 to 64
void rainbowCycle(int index,byte state) {
static uint16_t j=0; // used as state variable
static byte firstLED = 48;
static byte numLEDs = 16;
for(int i=0; i< numLEDs; i++) {
leds[i+firstLED].setHSV( (((i * 256 / numLEDs) + j) & 255), 255, 255);
}
FastLED.show();
j++;
if(j >= 256*5) j=0;
}