Need help understanding interrupts

I have a project that needs to be built but I am stuck.

I came across this

attachInterrupt(digitalPinToInterrupt(switchPin), handleInterrupt, CHANGE);

to call an interrupt routine.

Only I have two buttons that I need to exert control in the following manner.

Press any button to start the timer associated with that button.
Press both to stop the timer that is running.

Only I don't want to have two seperate routines to determine which button(s) was pressed to set internal flags nor do I want separate debounce routines for each button pressed.

Where is there an example I can examine to get an idea on how I rewrite the code I have that exists in the loop() function that listens for a button press?

eg.

void loop(){
   buttonA = analogRead(pinX) == true;
   buttonB = analogRead(pinY) == true;
   debounceRoutine(); // causes a pause to prevent multiple triggers

  if( buttonA && buttonB ){ // both were pressed
    // do something like stop any counter
      counterAflag = false;
      counterBflag = false;
      counterUpdate(); // call routine that updates the appropriate counter
      // we now force infinite loop
      while(1){}
   }
   if( buttonA ){
      // set flag for A
      counterAflag = true;
      counterBflag = false;
   }
   if( buttonB ){
      // set flag for A
      counterAflag = false;
      counterBflag = true;
   }
}

Only I would like to use interrupts to set the flags and clear out redundant code in the loop to make it more compact and tidy.

Anyone done anything like what I am looking to do? Please point to the topic please.

Moved to Programming Questions....Please don't post in Uncategorized again.

Let's start with the basics such as why you are using analogRead() to read a digital pin ?

You need better forum categories because I scrolled and scrolled and couldn't see which to post in so ... don't blame the person posting, blame the forum categorizing method.

Its an example of roughly what I have, I use analaog or digital to demo an idea, it is not the actual code.

I am looking for examples that explain how I can have two buttons perform three things.

Button A starts timer A stops timer B
Button B starts timer B stops timer A
Button A & B stop any timer and halts the timers

Reset is on an independent switch.

Yes, this has been raised many times.

That is why there is an Uncategorized section.

1 Like

Interrupts are the wrong thing to use for something as slow as a human pushing a button.
There are many tutorials on how to read a button properly: Arduino button tutorials

This one goes a bit beyond that to more directly address what your doing:

// 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);
    }
}

have you ever thought about how you would distinguish between
Button A gets pressed
Button B gets pressed
vs.
Button A & B get pressed.

after how many milliseconds would you decide, if it is a sequence of individual button presses or the combinied button press?

Players press button A or button B depending on their team.

Button A & B would be pressed at the same time to stop any counting going on

// start the timer

	do{ // wait for start press A or B to start
		buttonA = digitalRead(pinA) == true;
		buttonB = digitalRead(pinB) == true;
	// some debouncing routine in here or I might just use independent 555 timers to act as debouncer on each button.
	}while(!buttonA || !buttonB)
	bool running = true;
	
	while(running){ 	
	// deal with which button was pressed

       // do some other stuff before reading state again

   	buttonA = digitalRead(pinA) == true || counterAflag; // if flag is set return true
	buttonB = digitalRead(pinB) == true || counterBflag;
   	

Something in that kind of flavour, the button once pressed, eg A button, will set a flag and each iteration of the loop will increment a counter by 1 roughly every 250ms as that is the delay introduced in the code, buttonA variable will be true if flagA is set even if button A is pressed again but the flags are changed when button B is pressed.

In the code a check is made to see if both A & B have been pressed long enough to stop the timers for A & B

The timers are external and independent of the script hence no timer variables.

tested with hardware:

/*
 
  Distinguish between Button A or Button B or Both Buttons 
  https://forum.arduino.cc/t/need-help-understanding-interrupts/1148305/9

  by noiasca
  2023-07-15
*/

// a simple Button class based on debounce example in OOP
class Button {                                   // class to debounce switches
    const uint8_t buttonPin;                     // the pin for the button
    static constexpr byte debounceDelay = 20;    // the debounce time - one value for all buttons (hence "static")
    const bool active;                           // is the button HIGH or LOW active
    bool lastButtonState = HIGH;                 // previous state of pin
    uint16_t lastDebounceTime = 0;               // previous debounce time (we just need short debounce delay, so the rollover of the variable works fine and spares 2 bytes compared to an unsigned long)

  public:
    Button(uint8_t attachTo, bool active = LOW) : buttonPin(attachTo), active(active) {}

    void begin() {                               // sets the pinMode and optionally activates the internal pullups
      if (active == LOW)
        pinMode(buttonPin, INPUT_PULLUP);
      else
        pinMode(buttonPin, INPUT);
    }

    bool wasPressed() {
      bool buttonState = LOW;
      byte reading = LOW;
      if (digitalRead(buttonPin) == active) reading = HIGH;
      if (((millis() & 0xFFFF ) - lastDebounceTime) > debounceDelay) {
        if (reading != lastButtonState && lastButtonState == LOW) {
          buttonState = HIGH;
        }
        lastDebounceTime = millis() & 0xFFFF;   // we store just the last byte from millis()
        lastButtonState = reading;
      }
      return buttonState;
    }
};

// combine two buttons to a button network
class ButtonNetwork {
    Button buttonA;                  // a button
    Button buttonB;                  
    uint32_t previousMillisA = 0;    // when was the button pressed
    uint32_t previousMillisB = 0;
    uint16_t waitTime = 400;         // wait time to press the second button
    bool isPressedA = false;         // was the button pressed shortly
    bool isPressedB = false;         

  public:
    ButtonNetwork(const uint8_t pinA, const uint8_t pinB) : buttonA(pinA), buttonB(pinB) {}

    void begin() {
      buttonA.begin(); // start button
      buttonB.begin();
    }

    void update(uint32_t currentMillis = millis()) {
      if (buttonA.wasPressed() && isPressedB == false) {
        previousMillisA = currentMillis;
        isPressedA = true;
      }
      if (buttonB.wasPressed() && isPressedA == false) {
        previousMillisB = currentMillis;
        isPressedB = true;
      }
      if (isPressedA == true && isPressedB == false && currentMillis - previousMillisA > waitTime) {
        Serial.println(F("just ButtonA"));
        isPressedA = false;
      }
      if (isPressedB == true && isPressedA == false && currentMillis - previousMillisB > waitTime) {
        Serial.println(F("just ButtonB"));
        isPressedB = false;
      }
      if (isPressedA == true && buttonB.wasPressed() && currentMillis - previousMillisA < waitTime) {
        Serial.println(F("both ButtonAB were pressed"));
        isPressedA = false;
        isPressedB = false;
      }
      if (isPressedB == true && buttonA.wasPressed() && currentMillis - previousMillisB < waitTime) {
        Serial.println(F("both ButtonBA were pressed"));
        isPressedA = false;
        isPressedB = false;
      } 

    }
};

ButtonNetwork buttonNetwork(A0, A1);  // assign two pins for the buttons

void setup() {
  Serial.begin(115200);
  Serial.println(F("\nStart"));
  buttonNetwork.begin();

}

void loop() {
  buttonNetwork.update();
}

output on Serial:

13:52:14.152 -> just ButtonB
13:52:15.048 -> just ButtonA
13:52:16.515 -> just ButtonA
13:52:18.660 -> just ButtonA
13:52:21.316 -> both ButtonAB were pressed
13:52:23.888 -> both ButtonAB were pressed
13:52:25.660 -> both ButtonBA were pressed
13:52:27.841 -> both ButtonBA were pressed
13:52:29.716 -> just ButtonA
13:52:30.783 -> just ButtonB
13:52:32.273 -> just ButtonA

Thanks for your reply but the code just went over my head. I am looking for something that fits in with KISS.

I wouldn't even know where to begin on implementing the idea.

Do you see the print statements?
Just start or stop the timers here.

I don't need to distinguish between button A + B pressed at the same time, just that they were both pressed simultaneously.

Put it this way, Only button A or Button B will be pressed be whatever team player is present, not both, only the player marshall will press A+B and that is to signal to stop sending counting pulses to the counters which are external to the arduino and these counters have built in debounce networks so there is no need to account for it, they can detect a 0.1ms pulse, so I have no worries there.

IMHO I think I am going to make a switch debouncer out of 555's because it simplifies the code a lot and I then don't need to have a debouncer routine.

I have omitted lots of code because you only need to get an idea of the direction I have gone in.

The timers have to have a pulse to a pin on them every few milliseconds to count, they are not clocks with start and stop, they are counters that increment with each pulse and have to increment every 250 ms otherwise each counter will just increment by 1 each button press.

The current method the site uses is actual timer and I intend on simplifying it to a counter system that provides an end of game score not a time, some players in the game are not the sharpest tool in the box...

What I am trying to do is produce something to help the game site because people don't press the start button, they just press the team button, so if the unit is waiting already raring to go and all it takes is a simple tema player to press their team button, job done.

The hard part is to get the "both buttons" option implemented so that the arduino reads that both buttons are pressed.

I could make two 555 debouncers and use a 3rd as a logic AND gate on a seperate pin to do the stop timers which thinking about it would simplify the process of coding further.

why not just use software? i showed you how

1 Like

One of them will always be pressed first, even if it is only my a microsecond and this could be recognised as a single button press. Do you see the problem ?

Because using 555's will remove heaps of code from the arduino. A debounce routine to work with two buttons is something like 20 lines of code, several variables...

you don't get any money back for unused code lines. :wink:

p.s.: If I'm correct, Arduino PWM frequency is able to generte a value as high as 65Khz and as low as 30Hz
so a simple analogWrite could be used to generate these "timer" pulses.

The counting system is in essence a state machine, that waits for a change in state.

The counter waits in a holding state at game start, either A or B is pressed and depending on what button is pressed, that counter then starts adding a point every 250 ms, or 4 points per second until another team player presses their team button.

The only way this can function is as a state machine until at end of game, the marshal hits both A+B to stop the counting going on.

I thought that interrupts would work best because its only setting button states and within the interrupts a test to see if the other button was being pressed could signal a different flag state but I am told that interrupts don't work like that despite seeing an example somewhere in the internet (don't ask for the link as I don't have it anymore) showing just this method of button controlling flag states.

If an can do away with switch debouncing routines to make the code smaller, all good as far as I can see.