[Solved] have multiple buttons act as one

Hello,

I’m currently writing a piece of code and I need to switch something on and off with the press of a button.
this in itself is a rather simple piece of code and I’ve got it working perfectly.

int state = HIGH;
int reading;
int previous = LOW;
int debounce = 1000;
unsigned long previousMillis = 0;

void setup () {
  pinMode(4, INPUT);

  digitalWrite(4, HIGH);

  Serial.begin(9600);
}

void loop() {
  unsigned long currentMillis = millis(); //store the time
  {
    reading = digitalRead(4); //read the state of the button
    if (reading == LOW && previous == HIGH && (unsigned long)(currentMillis - previousMillis) >= debounce) //when the button gets pressed and the debounce time has passed go to next part
    {
      if (state == HIGH) //change the state value
        state = LOW;
      else
        state = HIGH; 

      previousMillis = currentMillis; // saves the time to compare against in the next loop
    }
    previous = reading; // stores either high or low to compare against in the next loop
  }
  Serial.println(state); // prints a 1 or 0 so I can see if the value switched
}

When the button gets pressed the value changes and does not change again untill you release (the release part is important for the next part) the button and press it again.

Now what I want to do is achieve the same but with having multiple physical buttons as imputs.
I know I can just wire the buttons in parrallel but it is prefered to have the buttons wired individually incase 1 of the buttons needs to do something else in the future without having to change wiring.

So I’ve tried changing the code to accomodate for this.
I have succeeded at changing the code but now I’ve run into some unwanted behaviour.

int state = HIGH;
int reading;
int previous = LOW;
int debounce = 1000;
unsigned long previousMillis = 0;

void setup () {
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(7, INPUT);
  pinMode(8, INPUT);
  pinMode(9, INPUT);

  digitalWrite(2, HIGH);
  digitalWrite(3, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(5, HIGH);
  digitalWrite(6, HIGH);
  digitalWrite(7, HIGH);
  digitalWrite(8, HIGH);
  digitalWrite(9, HIGH);

  Serial.begin(9600);
  Serial.println("start");
}

void loop() {
  unsigned long currentMillis = millis(); // store time
  for(int i=2;i<10;i++) //to cycle trough the different buttons
  {
    reading = digitalRead(i); // checks a different button on each loop (2 trough 9)
    if (reading == HIGH && previous == LOW && (unsigned long)(currentMillis - previousMillis) >= debounce) //when the button gets pressed and the debounce time has passed go to next part
    {
      if (state == HIGH) // change the state value
        state = LOW;
      else
        state = HIGH;

      previousMillis = currentMillis; // saves the time to compare against in the next loop
    }
    previous = reading; // stroes either high or low to compare against in the next loop
  }
  Serial.println(state); // prints a 1 or 0 so I can see if the value switched
}

The unwanted behaviour I’ve run into is that after the debounce timer has passed (currently 1 second) and the button is still being pressed the value will switch again since the previous value has been changed due to the fact that the button that was checked before has the HIGH value.

Is there a way to modify this code to work as the single button code so that keeping the button pressed will not trigger another switch without adding a whole lot of extra code?

-Warzon3

If you start to use variables for the pins (and here, an array) you can make it nice and compact.

And here, you want to detect a button becoming pressed for every button. So you need to keep track of the previous state of ALL buttons individual.

And if you want easy:

#include <Bounce2.h>

const byte buttonPins[] = {2, 3, 4, 5, 6, 7, 8, 9}; //now you can select any pin in any order
bool state = false;

Bounce buttons[sizeof(buttonPins)];

void setup () {
  for (byte i = 0; i < sizeof(buttonPins); i++) {
    buttons[i].attach(buttonPins[i], INPUT_PULLUP);
  }

    Serial.begin(9600);
    Serial.println("start");
}

void loop() {
  for (byte i = 0; i < sizeof(buttonPins); i++) {
    buttons[i].update();
    if (buttons[i].fell()) {
      state = !state;
    }
  }
  Serial.println(state); // prints a 1 or 0 so I can see if the value switched
}

Based on the Bounce2 library to do all the heavy lifting or you.

and by the way

  pinMode(4, INPUT);
  digitalWrite(4, HIGH);

is now better written as  pinMode(4, INPUT_PULLUP); (and of course even better if you give a meaningful name to pin 4 and use it)

I think the problem is that you share the variables between the buttons. Ona cellphone at the moment so could not closely look at your code.

You need arrays for the previousMillis, state etc.

Next step would be making use of structs or classes per button.

And lastly there is a library called bounce2 (no experience with it) that might make your life easier ( I see that septillion posted code for that while I was typing this message).

Septillion thanks for your reply I will look into using arrays for this.

Your code does not seem to work however and I'm trying to figure out where it is not.
The serial monitor just keeps printing 0 so state remains false.
From my limited understanding of code everything appears to be correct tough.

J-M-L thanks for the tip saves a few lines.

@Warzon3, uhm, if you copied it right after me posting then uhm, try again. I made a small bug fix later (A)

There is on old an a new bounce library which one are you using? (Bounce or Bounce2)?

the challenge with the above code is that if 2 (or an even number of) buttons fall within the same for loop, then state changes values twice and thus displays false

@Septillion I seem to have copied it before the bug fix (:

the code works perfectly now.

So if my understanding of the use of arrays is correct I can just remove the 5 from the array if I ever want to use the button on pin 5 for something else? This is a much more elegant sollution then the one I would have hacked together.

@J-L-M

I have realised that there is the possibility of the switch not being noticable if 2 buttons are pressed at once but for the scenario I use it for that is not a problem.

Warzon3:
So if my understanding of the use of arrays is correct I can just remove the 5 from the array if I ever want to use the button on pin 5 for something else? This is a much more elegant sollution then the one I would have hacked together.

correct - you list the pins that are connected to buttons, don't even care about the order

Warzon3:
@J-L-M

I have realised that there is the possibility of the switch not being noticable if 2 buttons are pressed at once but for the scenario I use it for that is not a problem.

OK - as long as you know that's a risk it's fine

Yes, you indeed can. But then I would give the “ButtonPins” and “buttons” a better name. Aka, include what does do because if 5 is still a button it’s confusing if you call the rest “Buttons” and 5 something else. Maybe “DefaultButtonPins” and “DefaultButtons” or something.

And this way indeed detects every press even if another button is still pressed. So pressing two buttons (almost) at the same time will toggle the state back and forth quick.

If you want it to act as if all buttons are parallel

#include <Bounce2.h>

const byte buttonPins[] = {2, 3, 4, 5, 6, 7, 8, 9}; //now you can select any pin in any order
bool state = false;

Bounce buttons[sizeof(buttonPins)];

void setup () {
  for (byte i = 0; i < sizeof(buttonPins); i++) {
    buttons[i].attach(buttonPins[i], INPUT_PULLUP);
  }

    Serial.begin(9600);
    Serial.println("start");
}

void loop() {
  updateButtons();
  
  Serial.println(state); // prints a 1 or 0 so I can see if the value switched
}

void updateButtons(){
  for (byte i = 0; i < sizeof(buttonPins); i++) {
    buttons[i].update();
    //if a button is pressed but did not became pressed we skip checking for buttons becomming pressed
    if(!buttons[i].read() && !buttons[i].fell()){
      return;
    }
  }
  //otherwise we check for a button becomming pressed
  for (byte i = 0; i < sizeof(buttonPins); i++) {
    if (buttons[i].fell()) {
      state = !state;
      return;
    }
  }
}

Thank you for you assistance.

I have now marked this thread as Solved and I will certainly take your suggestions to make a better naming scheme (: