Cannot get action to happen ONLY while button is pressed

I started, what I though would be a simple project:

Run a loop (in this case a simple “figure 8” chaser around a 7 segment led)

If a button is held down, begin a countdown from 5 (on the 7- segment led) and fire a relay when it gets to zero. (repeats as long as the button is held down)

however, if the button is released, the countdown stops and the chaser loop continues

From what I understand this can be done with interrupts as with delays it doesn’t work properly.

to make this easier to picture, the 7 segment led has a little animation //I have this working already

someone steps on a pressure pad (switch) that is connected to pin 2
the 7 - segment led begins a count down and continues the countdown as long as the switch is activated, when zero is reached pin 13 fires LOW. this should repeat as long as the switch is low

However, if the switch goes back HIGH at any point the countdown is voided and the little animation continues.

The problem I am having is:
#1 the countdown is non functional
#2 I cannot get the interrupt to function as I desire

code:

//common anode info
byte seven_seg_digits[10][7] = {                           
{ 0,0,0,0,0,0,1 },  // = 0                                                        
{ 1,0,0,1,1,1,1 },  // = 1
{ 0,0,1,0,0,1,0 },  // = 2
{ 0,0,0,0,1,1,0 },  // = 3
{ 1,0,0,1,1,0,0 },  // = 4
{ 0,1,0,0,1,0,0 },  // = 5
{ 0,1,0,0,0,0,0 },  // = 6
{ 0,0,0,1,1,1,1 },  // = 7
{ 0,0,0,0,0,0,0 },  // = 8
{ 0,0,0,1,1,0,0 }   // = 9
};

                                                           
//interrupt switch
int pedalIn = 0; //foot pedal input to pin 2


void setup() { 
 
    
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT); 
 
 
attachInterrupt(pedalIn, firingsquad, LOW);
}


void sevenSegWrite(byte digit) {
  byte pin = 9;
  for (byte segCount = 0; segCount < 7; ++segCount) {
    digitalWrite(pin, seven_seg_digits[digit][segCount]);
    ++pin;
  }
}

void firingsquad() {
  for (byte count = 10; count > 0; --count) {
   delay(1000);
   sevenSegWrite(count - 1); 
  }
  delay(4000);
}

void loop() 
{
  chaser();
  }

void chaser() {
digitalWrite (8, LOW);
delay(40);
digitalWrite (5, HIGH);
delay(40);
digitalWrite (7, LOW);
delay(40);
digitalWrite (4, HIGH);
delay(40);
digitalWrite (9, LOW);
delay(40);
digitalWrite (8, HIGH);
delay(40);
digitalWrite (3, LOW);
delay(40);
digitalWrite (7, HIGH);
delay(40);
digitalWrite (8, LOW);
delay(40);
digitalWrite (9, HIGH);
delay(40);
digitalWrite (6, LOW);
delay(40);
digitalWrite (3, HIGH);
delay(40);
digitalWrite (5, LOW);
delay(40);
digitalWrite (8, HIGH);
delay(40);
digitalWrite (4, LOW);
delay(40);
digitalWrite (6, HIGH);
delay(40);
  
}

1) Don't use delay, use millis(), (as everyone else says, see the "Blink without Delay" example)

2) Don't do the bulk of you work inside the interrupt, just have the interrupt throw a flag like boolean firing_squad_flag = false (and set it to true inside the interrupt). Then inside loop() just have an if statement before you do all that other junk.

int pedal_int = 0;
boolean firing_squad_flag = false;
int seg_num = 0;
int prev_millis = 0;

void setup() {
  //setup pins
  attachInterrupt(pedal_int, firing_int, LOW);
}

void loop() {
  if (firing_squad_flag) {
    firing_squad();
    firing_squad_flag = false;
  }
  check_seg();
}

void firing_int() {
  firing_squad_flag = true;
}

void firing_squad() {
  //countdown code (don't use delay)
}

void check_seg() {
  if ((millis() - prev_millis()) > 40) {
    seg_num++;
    update_seg(seg_num);
  }
}

void update_seg(int seg_num) {
   //write from seven_seg_digits based on seg_num
}

That was really rough and I wrote it up in like 5 mins, there are plenty of other ways to do it in my opinion, but something like that would work fine. The key is that you don't get locked into your delay routine inside chaser(), just let it update one at a time based on the millis().

As far as the part where you were having problems when the pedal went HIGH, I'm not exactly sure what you meant there, do you want it to repeat as long as the pedal is held down? If so, that is a simple if statement at the end of firing_squad() (something like if (!digitalRead(2)) {//trigger a repeat}). You could do something like FALLING instead of LOW, which might fix your problem.

Quote:
“Warning: Since interrupts are disabled inside an ISR, and since the latest version of the Arduino IDE uses interrupts for Serial reading and writing, and also for incrementing the counter used by “millis” and “delay” you should not attempt to use those functions inside an ISR. To put it another way:”

Excellent coverage of interrupts here:
http://www.gammon.com.au/forum/?id=11488

•Don’t attempt to delay, eg: delay (100);
•You can get the time from a call to millis, however it won’t increment, so don’t attempt to delay by waiting for it to increase.
•Don’t do serial prints (eg. Serial.println (“ISR entered”); )
•Don’t try to do serial reading.

OK, that seems to make since...I had read that DELAY would be my downfall.

One of my issues now remains with the creation of a 5...4...3...2...1...0 countdown for seven segment WITHOUT using delay. I have tried to kludge something together of the little I understand from the "blink w/o delay tutorial" but it is not successful thus far. Guess I'll poke at it some more.

The same way I outlined in the check_seg() function above. Just have some sort of loop inside the firing_squad subroutine that constantly checks the millis.

while (current_seg_number > 0) {
  if ((millis() - prev_millis()) > 1000) {
    prev_millis() = millis();
    //now update the segment with the next number
    current_seg_number--;
    //write the current_seg_number to the 7seg display
  }
}

•Don't try to do serial reading.

There is no problem with reading serial data in an ISR. Doing so does not depend on interrupts. Expecting more serial data, while in an ISR, IS a mistake.