Pausing code with a button push

Hello everyone,
I have what I hope is a simple problem. I am using an Arduino board as a programmable sequencer. Right now, if I hit the button, it steps thru the code and turns things on and off at programmed intervals. I would like to be able to pause the sequence with a second push of the same button and have it resume from the same position on yet another push of the same button. Just like the Play/Pause button on some CD players.

Does anyone have any suggestions? The code I am using is derived from the tutorial code that turns an LED on with a button mixed with the Blink code. I can post a copy when I get home if that will help.

Sean

I can post a copy when I get home if that will help

You do that, and I’ll have a look.
Sure many others will find it helpful as well. :slight_smile:

http://www.arduino.cc/playground/Learning/ArduinoSleepCode

This might help you.

You can assign an interupt to a button that starts the sleep function, that way you can pause your code at any point. Then in the sleep function you detach the interrupt and attach it again so it starts the wake function, so when you press the button again it continues where it left off.

Wouldn’t a simple while statement loop, if the switch is active, keep the program ‘stalled’ until the switch is released?, no need for something more complex like an ISR or sleep to do such a simple step I would think.

Lefty

I'd be inclined to agree with Lefty.

If you're controlling the sequencing in your own software, it is likely there's a single point in the code, between one event and the next where it's easy to examine the state of a pin, and simply "mark time" until the button is released or pressed again.

OK, I can't use the sleep function because I am using ALL of the digital pins as outputs. Here is my code.

  int INDICATOR = 13;        // choose the pin for power indicator
  int CH1 = 0;                     // choose the pin for CH1
  int CH2 = 1;                     // choose the pin for CH2
  int CH3 = 2;                     // choose the pin for CH3
  int CH4 = 3;                     // choose the pin for CH4
  int CH5 = 4;                     // choose the pin for CH5
  int CH6 = 5;                     // choose the pin for CH6
  int CH7 = 6;                     // choose the pin for CH7
  int CH8 = 7;                     // choose the pin for CH8
  int CH9 = 8;                     // choose the pin for CH9
  int CH10 = 9;                   // choose the pin for CH10
  int CH11 = 10;                 // choose the pin for CH11
  int CH12 = 11;                 // choose the pin for CH12
  int RELAY = 12;                // choose the pin for RELAY
  int BUTTON = 14;             // choose the input pin (for a pushbutton)
  int val = 0;                      // variable for reading the pin status

void setup() {
  pinMode(INDICATOR, OUTPUT);      // declare INDICATOR as output
  pinMode(CH1, OUTPUT);            // declare CH1 as output
  pinMode(CH2, OUTPUT);            // declare CH2 as output
  pinMode(CH3, OUTPUT);            // declare CH3 as output
  pinMode(CH4, OUTPUT);            // declare CH4 as output
  pinMode(CH5, OUTPUT);            // declare CH5 as output
  pinMode(CH6, OUTPUT);            // declare CH6 as output
  pinMode(CH7, OUTPUT);            // declare CH7 as output
  pinMode(CH8, OUTPUT);            // declare CH8 as output
  pinMode(CH9, OUTPUT);            // declare CH9 as output
  pinMode(CH10, OUTPUT);          // declare CH10 as output
  pinMode(CH11, OUTPUT);          // declare CH11 as output
  pinMode(CH12, OUTPUT);          // declare CH12 as output
  pinMode(RELAY, OUTPUT);         // declare RELAY as output
  pinMode(BUTTON, INPUT);         // declare pushbutton as input
}

void loop(){
  digitalWrite(INDICATOR, LOW);    // turn INDICATOR on
  val = digitalRead(BUTTON);       // read input value
  if (val == LOW) {                // check the input state
    digitalWrite(CH1, LOW);        // turn CH1 OFF
    digitalWrite(CH2, LOW);        // turn CH2 OFF
    digitalWrite(CH3, LOW);        // turn CH3 OFF
    digitalWrite(CH4, LOW);        // turn CH4 OFF
    digitalWrite(CH5, LOW);        // turn CH5 OFF
    digitalWrite(CH6, LOW);        // turn CH6 OFF
    digitalWrite(CH7, LOW);        // turn CH7 OFF
    digitalWrite(CH8, LOW);        // turn CH8 OFF
    digitalWrite(CH9, LOW);        // turn CH9 OFF
    digitalWrite(CH10, LOW);       // turn CH10 OFF
    digitalWrite(CH11, LOW);       // turn CH11 OFF
    digitalWrite(CH12, LOW);       // turn CH12 OFF
    digitalWrite(RELAY, LOW);      // turn RELAY OFF
  } else {
  digitalWrite(CH1, HIGH);         // sets CH1 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH1, LOW);          // sets CH1 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH2, HIGH);         // sets CH2 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH2, LOW);          // sets the CH2 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH3, HIGH);         // sets CH3 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH3, LOW);          // sets the CH3 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH4, HIGH);         // sets CH4 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH4, LOW);          // sets CH4 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH5, HIGH);         // sets CH5 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH5, LOW);          // sets CH5 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH6, HIGH);         // sets CH6 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH6, LOW);          // sets CH6 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH7, HIGH);         // sets CH7 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH7, LOW);          // sets CH7 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH8, HIGH);         // sets CH8 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH8, LOW);          // sets CH8 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH9, HIGH);         // sets CH9 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH9, LOW);          // sets CH9 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH10, HIGH);        // sets CH10 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH10, LOW);         // sets CH10 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH11, HIGH);        // sets CH11 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH11, LOW);         // sets CH11 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH12, HIGH);        // sets CH12 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH12, LOW);         // sets CH12 off
  digitalWrite(RELAY, HIGH);       // sets RELAY on
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH1, HIGH);         // sets CH1 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH1, LOW);          // sets CH1 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH2, HIGH);         // sets CH2 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH2, LOW);          // sets CH2 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH3, HIGH);         // sets CH3 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH3, LOW);          // sets CH3 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH4, HIGH);         // sets CH4 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH4, LOW);          // sets CH4 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH5, HIGH);         // sets CH5 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH5, LOW);          // sets CH5 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH6, HIGH);         // sets CH6 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH6, LOW);          // sets CH6 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH7, HIGH);         // sets CH7 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH7, LOW);          // sets CH7 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH8, HIGH);         // sets CH8 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH8, LOW);          // sets CH8 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH9, HIGH);         // sets CH9 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH9, LOW);          // sets CH9 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH10, HIGH);        // sets CH10 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH10, LOW);         // sets CH10 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH11, HIGH);        // sets CH11 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH11, LOW);         // sets CH11 off
  delay(2000);                     // waits for 2 seconds
  digitalWrite(CH12, HIGH);        // sets CH12 on
  delay(500);                      // waits for 0.5 second
  digitalWrite(CH12, LOW);         // sets CH12 off
  digitalWrite(RELAY, LOW);        // sets RELAY off  
  }
}

Any other suggestions are welcome but I think I may be out of luck. One other condition, this is triggered by a remote receiver and only applies the 5v for a little less than .5 sec. So, I can't hold the button to run the sequence.

Sean

If the outputs don't change very often, you could just add a shift register, and move 8 of them off the Arduino pins.

Ran

If the outputs don’t change very often

What do you mean by this? I searched for shift register in relation to Arduino and found a nice how-to on driving 8 LEDs with a 595. It looks like this would work well. Is there some kind of limit on the time between changes? I only have one pin active at any given time and it is only on for .5 seconds then there is a substantial delay before the next pin needs to be active. As I see it, I could drive several of these registers and have more channels while keeping pins 3 and 4 open for the sleep/wake function. Now that that part is sorted, could someone provide me with a link that shows how to get it to resume from the same line after sleep?

Sean

@RESET,

Your sketch above is over 150 lines long.
If you rewrote it with arrays and loops, it would be around half that, and it would be very much easier, as I suggested above, to slip in a simple test at the end of each “for” loop, to check the state of a button, and simply loop waiting for the button to be released.
You would restart from exactly where you expected.

Rule of Thumb: If you find yourself writing the same thing, or nearly the same thing, over and over again, then it’s probably time for a loop.

If you want to get really fine control, then you might have to turn the problem on its head, and remove the "delay"s.

[EDIT] The sketch above comes in under 50 lines.

const byte pins []  = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
#define N_PINS (sizeof (pins) / sizeof (pins[0]))
#define RELAY      12
#define INDICATOR  13
#define BUTTON     14
byte i;  // general purpose index.

void setup() {
  for (i = 0; i < N_PINS; ++i) {
    pinMode(pins [i], OUTPUT);
  }  
  pinMode(RELAY, OUTPUT);         // declare RELAY as output
  pinMode(INDICATOR, OUTPUT);      // declare INDICATOR as output
  pinMode(BUTTON, INPUT);         // declare pushbutton as input
}

void loop(){
  digitalWrite(INDICATOR, LOW);    // turn INDICATOR on
  if (digitalRead(BUTTON) == LOW) {                // check the input state
    for (i = 0; i < N_PINS; ++i) {
      digitalWrite (pins [i], LOW);
    }
    digitalWrite(RELAY, LOW);      // turn RELAY OFF
  } else {
    for (i = 0; i < N_PINS; ++i) {
      digitalWrite(pins[i], HIGH);
      delay(500);                      // waits for 0.5 second
      digitalWrite(pins [i], LOW);
      if ((N_PINS - 1) == i){
        digitalWrite(RELAY, HIGH);       // sets RELAY on
      }  
      delay(2000);                     // waits for 2 seconds
    }  
  
    for (i = 0; i < N_PINS; ++i) {
      digitalWrite(pins[i], HIGH);
      delay(500);                      // waits for 0.5 second
      digitalWrite(pins [i], LOW);
      if ((N_PINS - 1) == i){
        digitalWrite(RELAY, LOW);       // sets RELAY off
        break;
      }  
      delay(2000);                     // waits for 2 seconds
    }  
  }
}

Now, since the sketch spends most of its time in “delay”, simply write a wrapper around delay (“void myDelay (long pause)”) that calls “delay(1)” “pause” times, and each time checks the state of the button.
This may need a bit of tuning to ensure timing accuracy, but will be easier to implement than an interrupt to do the same.

@AWOL,
The 2 second delays are only there as place holders. When I implement the sequencer they will all be different. Otherwise, I would have used a loop as it would have been easier. I think I will use the beginning of your code sample though, with the pin assignments and such. Is there an issue with the code being more than 150 lines?

I don’t think the test will work either since I would have to know when the loop is going to finish in order to have the <0.5 second button push take effect. I also can’t hold the button to pause the loop since the Rx only applies the voltage for a split second. Is there no code that tells Arduino to constantly look at the input for a state change?

Sean

When I implement the sequencer they will all be different

So, that's just another array!

No issue with the program being 150 lines, but: 1) it takes about another kilobyte of memory. 2) it is more difficult to spot bugs

If you do as I said, you'll be looking at the button every millisecond.

"Not very often" is fuzzy, but would be "in the range of tens of times per second" (or less). Somewhere in the range of "hundreds/second" shift registers start to become iffy, because of the amount of time it takes to clock out the data to them. And "tens of thousands/sec" is right out.

But your rate of change is very slow, and well-suited to using them.

Ran

I still don't understand how this is going to work when I hit the button and it waits but then, less than .5 seconds later, the receiver cuts the voltage and the input state is low again. I need something that will see the state change to high and pause the code until I hit the button and the state goes to high again.

@RESET
You’re thinking about making things way more difficult than they need to be.

Off the top of my head:

// replace "delay" with this wherever you see it.
void myDelay (long pause)
{
  for (int i = 0; i < pause; ++i) {
    delay (1);
    while (!digitalRead (buttonPin) ) // If the button pin is low, this will wait until it goes high again. Otherwise it'll just skip
      {}
    }
}

This gives you 1mS latency.
How tight do you need it?
If the button handling is harder - just pop the logic in here.

Awol, I apologize, I am new to this. I am just trying to get my head wrapped around the concept and some things just aren't clicking. Your code note said

If the button pin is low, this will wait until it goes high again. Otherwise it'll just skip

This is where I am getting hung up. My button push to initiate the sequence is 5v supplied by a remote receiver. It applies that voltage for just under 500mS. My current code sees that state high and starts the code rolling. If I understand your note correctly, when the execution gets to the section you are talking about it will wait for me to hit the button again. This is not what I am looking for. I don't have the option of holding the button, the receiver only applies the voltage for a set period of time. If I could hold the button, this would make sense as it would act as a dead man switch and only run the code if the button is pushed. I know there is a solution, maybe I am just not communicating as clearly as I think I am.

Sean

OK, sorry, I haven't got the same investment in this as you do, and I may have not read all that you have explained as carefully as I should and I have over-simplified a little.

Tell me where I'm going wrong: You're saying that the pause button sends a HIGH to start the pause sequence, but that after 500 000 uS, it goes low again.

The next time you see a HIGH on the button, that's the start of a pause, and it will go low after 500 000 uS. So, when I see a HIGH again, I should start stuff rolling again.

Whichever way you dice this one (interrupt or no interrupt), you're going to need a simple state machine.

A few extra lines of code.

The reason I wrote 0.5 second as "500 000uS" is to show you that you are operating on a seriously vast scale of time here, interrupts or no interrupts.

In a user interface, if it took 1/2 second from clicking a mouse to seeing something happen on the screen, you'd probably click again. You need to respond as soon as you see the event. But, subsequently, you need to know that the "HIGH" you're seeing is not a new "HIGH", but the "HIGH" that you responded to up to half a second ago.

But, sorry, this isn't a user interface with an operating system underneath, this is a bare microcontroller. It's all down to you.

OK, let me try to start over. I have an arduino setup as a sequencer. I have one of the analog inputs connected to an external receiver that provides the High logic signal to start the sequencer. This works and the arduino will step thru the sequence and stop when it gets to the end. If I trigger the receiver again, the sequence will start again. What I am looking for is some code that will constantly look at the state of the analog pin and pause the execution of the sequence if it goes high again while the sequence is running. I also need code that will resume the execution of the sequence when I trigger the receiver again. The receiver only sends the 5v pulse for 500mS. I mention this because I do not have the option of holding the "button" to continue the execution. Any code that looks at the state of the analog pin will need to pause the sequence if it sees the logic high. I also do not want to have to trigger the receiver at the end of each "delay" to keep the sequence going. The sequence needs to execute from begining to end UNLESS I trigger the receiver while it is running and resume from the same point when I trigger the receiver again.

Sean

Bump.

Anyone got any ideas or am I just not understanding what has been suggested so far? If you guys believe that the previous suggestions are what I need, would someone please explain how they will work in the way that I need?

Sean

I think you have everything you need.

But the part that you are perhaps not understanding is the state machine part.

You need something to keep track of the state. This would be a variable which holds the state of the button press. You can look at the debouncing example sketch for this and modify it.

As I see it, you will have to poll the pin. When it goes high, you can start your sequence and set a variable that indicates that your sequence is running. You would continue to poll the pin but you don't do anything about it until it goes low (which is after a wait of about 500ms). Once it goes low, set the button state to ready (another state variable you would have to set). When the button is ready you can respond to the pin going high by: pausing (if your sequence is running) or by starting (if your sequence is paused).

As I see it, you need two boolean variables and a bit of logic to implement this.

I'd diagram it out on paper and with some words first. You already have the part about detecting initial start, now you just need to detect when the button is ready to push again and something to mark which state you are in (running or paused).

Thanks, that make a little see. I will look into this and come back if I run into anything else.

Sean