Momentary switch for start/stop - loops are breaking my logic

I have an Arduino Mega 2560 and I'm using a momentary push-to-make switch and an LED.

From when the system is powered on, I want it to wait until the switch is pushed.
It should then switch the LED on, set the 'pwrStatus' variable to 1.

if pressed a second time, I'd like it to 'know' that the pwrStatus variable was 1, therefore the process is running and it must instead switch the LED off and stop the process.

I've tried to get this working using an Interrupt, but I think my problem is that it loops through the whole thing so fast, sometimes the length of the switch press means that it's evaluating whether the switch is open or closed at that exact moment, so it's unreliable.

Is there a way I can kind of 'isolate' it so that once the switch is pressed it stops looping until the switch has been released and it's finished the remaining function call(s)?

in my code sample below, the push switch is on pin 19 and the power LED is on pin 30. I'm using a resistor to pull the switch input down when not pressed.

Thank you for any help

const int interruptPin = 19;
int pwrStatus = 0;

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(30, OUTPUT);  //button power led
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(19), btnFunction, RISING);
}

// the loop function runs over and over again forever
void loop() {
}

void btnFunction() {

  if (pwrStatus == 0) {
    digitalWrite(30, HIGH);
    pwrStatus = 1;
  } else if (pwrStatus == 1) {
    digitalWrite(30, LOW);
    pwrStatus = 0;
  }
}

do you debounce the pin by adding a short delay (~20msec) whenever the pin state changes?

A couple of things.
First, you don't need to have a pull down resistor if you enable the pull up resistor on your pin. just tied it to ground.
Second, in your interrupt, it is best to just set a flag that it occurred and deal with it within your loop(). You then reset that variable.
Also look at debouncing your button.

Don't use an interrupt

You need to detect when the button becomes pressed rather than when it is pressed.

See the StateChangeDetection example in the IDE and you may also need to debounce the input to avoid multiple false button press detections

I'm not, please could you clarify - is this for an electronic/signalling reason or is this to allow the user to release the button? Thank you.

Check the tutorials for state machine and maybe use a library for button debouncing/reading RELIABLY.

  • When operated, mechanical switches are notorious for contact bounce.

  • There are several threads on this site that explain how to handle contact switch bounce using hardware or software techniques.

  • Also, look on YouTube for videos discussing switch bounce.

  • One simple way to handle contact switch bounce in software is to poll the switch every 50ms looking for a change in state.

look this over

// check multiple buttons and toggle LEDs

enum { Off = HIGH, On = LOW };

byte pinsLed [] = { 10, 11, 12 };
byte pinsBut [] = { A1, A2, A3 };
#define N_BUT   sizeof(pinsBut)

byte butState [N_BUT];

// -----------------------------------------------------------------------------
int
chkButtons ()
{
    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        byte but = digitalRead (pinsBut [n]);

        if (butState [n] != but)  {
            butState [n] = but;

            delay (10);     // debounce

            if (On == but)
                return n;
        }
    }
    return -1;
}

// -----------------------------------------------------------------------------
void
loop ()
{
    switch (chkButtons ())  {
    case 2:
        digitalWrite (pinsLed [2], ! digitalRead (pinsLed [2]));
        break;

    case 1:
        digitalWrite (pinsLed [1], ! digitalRead (pinsLed [1]));
        break;

    case 0:
        digitalWrite (pinsLed [0], ! digitalRead (pinsLed [0]));
        break;
    }
}

// -----------------------------------------------------------------------------
void
setup ()
{
    Serial.begin (9600);

    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        pinMode (pinsBut [n], INPUT_PULLUP);
        butState [n] = digitalRead (pinsBut [n]);
    }

    for (unsigned n = 0; n < sizeof(pinsLed); n++)  {
        digitalWrite (pinsLed [n], Off);
        pinMode      (pinsLed [n], OUTPUT);
    }
}

Thanks very much all, this definitely gives me a good push in the right direction.

Hello taymar02

I have created a simple button led manager in OOP for you.

You can simply add bottons and leds as you like without changing the program.

Ckeck it and play around.

//https://forum.arduino.cc/t/momentary-switch-for-start-stop-loops-are-breaking-my-logic/1387868
//https://europe1.discourse-cdn.com/arduino/original/4X/7/e/0/7e0ee1e51f1df32e30893550c85f0dd33244fb0e.jpeg
#define ProjectName "Momentary switch for start/stop here"
#define NotesOnRelease "Arduino MEGA tested"
// make names
enum TimerEvent {NotExpired, Expired};
enum TimerControl {Halt, Run};
enum OutPutAction {Off, On};
// make variables
uint32_t currentMillis = millis();
//---------------------------
// Specify the I/O pins here
constexpr uint8_t LedPins[] {9};
constexpr uint8_t ButtonPins[] {A0};
//---------------------------
// make structures
//-----------------------------------------
struct TIMER
{
  uint32_t interval;
  uint32_t now;
  uint8_t expired(uint32_t currentMillis)
  {
    uint8_t timerEvent = currentMillis - now >= interval;
    if (timerEvent == Expired) now = currentMillis;
    return timerEvent;
  }
};
//-----------------------------------------
struct BOTTONLED
{
  uint8_t led;
  uint8_t button;
  uint8_t stateOld;
  TIMER   debounce;
  void make(uint8_t led_, uint8_t button_)
  {
    led = led_;
    pinMode (led, OUTPUT);
    digitalWrite(led, On);
    delay(1000);
    digitalWrite(led, Off);
    delay(100);
    button = button_;
    pinMode(button, INPUT_PULLUP);
    stateOld = digitalRead(button) ? Off : On;
    debounce.interval = 20;
  }
  void exec(uint32_t currentMillis)
  {
    if (debounce.expired(currentMillis) == Expired)
    {
      uint8_t stateNew = digitalRead(button) ? Off : On;
      if (stateOld != stateNew)
      {
        stateOld = stateNew;
        if (stateNew == On) digitalWrite(led, digitalRead(led) ? Off : On);
      }
    }
  }
};
//-----------------------------------------
struct HEARTBEAT
{
  uint8_t led;
  uint32_t beat[2];
  TIMER beatTimer;
  void make (uint8_t led_, uint32_t beat_)
  {
    led = led_;
    beat[Off] = beat_;
    beat[On] = beat_ / 10;
    pinMode (led, OUTPUT);
    beatTimer = {beat[Off], millis()};
  }
  void exec (uint32_t currentMillis)
  {
    if (beatTimer.expired(currentMillis) == Expired)
    {
      digitalWrite (led, digitalRead(led) ? Off : On);
      beatTimer.interval = beat[digitalRead(led)];
    }
  }
};
//-----------------------------------------
// make objects
HEARTBEAT heartBeat;
BOTTONLED buttonLeds[sizeof(LedPins)];
//-----------------------------------------
// make application
void setup()
{
  Serial.begin(115200);
  for (uint8_t n = 0; n < 32; n++) Serial.println("");
  Serial.print("Source: "), Serial.println(__FILE__);
  Serial.print(ProjectName), Serial.print(" - "), Serial.println(NotesOnRelease);
  heartBeat.make(LED_BUILTIN, 1000);
  uint8_t element = 0;
  for (auto &buttonLed : buttonLeds)
  {
    buttonLed.make(LedPins[element], ButtonPins[element]);
    element++;
  }
  delay(2000);
  Serial.println(" =-> and off we go\n");
}
void loop()
{
  currentMillis = millis();
  heartBeat.exec(currentMillis);
  for (auto &buttonLed : buttonLeds) buttonLed.exec(currentMillis);
}

Have a nice day and enjoy coding in C++.

Thank you very much, that's very kind of you