Arduino with debounced buttons and millis function

Greetings,

needles to say i am a rookie with arduino and just can't achieve a certain goal i am after.

For months now i have been trying to make a pinball program and nothing really gives me a completed result. What i learned so far is delay must be replaced with millis function because of sound effects and background music.
I would need a program that when 5 buttons(later replalced with sensors) are pressed, leds are turned on respectedly. When all 5 leds are on, a sound effect is triggered and the leds blink a few times with random blink from 50 to 250 ms and then turn off. Buttons should be debounced because of a score counter for each time a button is activated.

For any kind of help i will be very grateful.

So just to clarify, let's see if I got that: each button1-5 has a led1-5 associated with it. If button3 is pressed, led3 comes on. If button5 is pressed, led5 comes on. Each press adds to a score.

Eventually, all 5 will have been pressed, all the leds are on, and that's when you get a sound and the leds dance for a bit?

What you didn't say is:

  • Is it the press of the button that causes the led to come on, in other words the release does nothing?
  • If a button is pressed a second or subsequent time, ie its led is already on, does it add to the score but leave the led on? Or what?
  • Does each button add the same amount to the score, or are they weighted?

But details aside, I'd use the idea of state change detect (which has a poor man's debounce built in) to see each button press, but with all the buttons and associated leds (and scores if different) in arrays.

A press of button[j] turns led[j] on (no harm in doing that many times for repeat presses) and adds score[j] to the total. Keep a count of numberOfButtonsPressed and do the led dance when it's 5.

sounds like you've learned that nothing else can be done using delay(), however a short used of delay() for 10 msec is useful to debounce button presses and isn't long enough to interfere with most other processing.

i suggest using a button press to capture a timestamp (using millis()) which both enables an action as well as marking when the action is complete. that timestamp is checked if not zero and turns off the LED if expired

consider the following

// turn on LED with button press

#define But1    A1
#define But2    A2
#define But3    A3

#define Led1    10
#define Led2    11
#define Led3    12

byte butPins [] = { But1, But2, But3 };
byte ledPins [] = { Led1, Led2, Led3 };

#define N_BUT   sizeof(butPins)

byte butLst  [N_BUT];
unsigned long msec  [N_BUT] = {};

#define ON_TIME    1000

// -------------------------------------
void
loop (void)
{
    // check for button presses
    for (byte i = 0; i < N_BUT; i++)  {
        byte but = digitalRead (butPins [i]);

        if (butLst [i] != but)  {
            butLst [i] = but;

            // turn LED on and capture timestamp
            if (LOW == but)  {
                msec [i] = millis ();
                digitalWrite (ledPins [i], LOW);
            }
        }
    }
    
    // check if LED on and turn off if time expired
    for (byte i = 0; i < N_BUT; i++)  {
        if (msec [i])  {
            if (millis () - msec [i] > ON_TIME)  {
                msec [i] = 0;
                digitalWrite (ledPins [i], HIGH);
            }
        }
    }

    // short delay to debounce buttons
    delay (10);
}

// -------------------------------------
void
setup (void)
{
    // initialize all inputs and button state
    for (byte i = 0; i < N_BUT; i++)  {
        pinMode (butPins [i], INPUT_PULLUP);
        butLst [i] = digitalRead (butPins [i]);
    }

    // initialize all outputs and turn off LEDs
    for (byte i = 0; i < N_BUT; i++)  {
        pinMode (ledPins [i], OUTPUT);
        digitalWrite (ledPins [i], HIGH);
    }
}

Thank you very much for the reply.

Yes twinkleyan i should explain bit more detailed. At first all the LEDS should be off. Then for every button there would be a LED assigned to it. So if button 1 is pressed, led 1 turns on , button 2 is pressed, led 2 turns on etc.
When all 5 LEDs are on:
-a sound effect happens
-all 5 LEDs do a dance a few times and than turn off.

And that basically would run forever until the ball is sensed by the game stop sensor.

For the score, considering that this will be a pinball machine, anytime the ball goes over the sensor(button for now), the score should be counted based on the number given in the program.

Thank you for the program gcjr but i am worried about the delay because when i did my own programs the MARIO theme song which should be playing in the background at any given moment, is always restarted when a delay is in use.

Have a look at the code below; everything's in arrays (eg the button pin number, its associated led pin number, whether or not that button has been pressed, and the states.)

The score per button is in an array, I made them different to make it easy to see they work; just make them all the same if they should all be say 1 or 10.

All I did for the sound effect now was to play a buzzer tone high or low along with the delay()-less random blink of the leds.

I kept the total running after the 5 buttons have been pressed; I'm sure you'll see in the code where to reset it back to 0 if that's supposed to happen.

And there's a delay()-less blink on pin 13's led so you can see that nothing's blocked.

// https://forum.arduino.cc/index.php?topic=679079
// state change detect on a button struct array
// 21 april 2020

/*
  I [xcg584] would need a program that when 5 buttons(later replalced with sensors)
  are pressed, leds are turned on respectedly. When all 5 leds are on,
  a sound effect is triggered and the leds blink a few times with random
  blink from 50 to 250 ms and then turn off. Buttons should be debounced
  because of a score counter for each time a button is activated.
*/

/*
  BASED ON State change detection (edge detection) changed for INPUT PULLUP
  and with a struct array
  delay()-less millis()-based pulse pin 13
*/

// the buttons
struct buttons
{
  byte pin;
  byte led;
  bool state;
  bool prevState;
  byte score;
  byte pressed;
};
const byte buttonPins[] = {15, 16, 17, 18 , 19};
const byte numberOfButtons = sizeof(buttonPins) / sizeof(buttonPins[0]);
buttons mybuttons[numberOfButtons];
const byte ledPins[] = {2, 3, 4, 5, 6};
const byte scores[] = {10, 20, 30, 40, 50};

int runningTotal;
byte numberOfButtonsPressed = 0;

//the pulse led
int pulseLedInterval = 500;
unsigned long previousMillisPulse;
bool pulseState = false;

//the effect led
int effectLedInterval;
int effectLedIntervalMax = 250;
int effectLedIntervalMin = 50;
unsigned long previousMillisEffect;
bool effectState;
bool doEffect = false;

//the buzzer
byte buzzer = 9;

void setup()
{
  // initialize serial communication:
  Serial.begin(9600);
  Serial.println("setup() ... ");
  Serial.println("** 679079 **");
  Serial.print("Compiler: ");
  Serial.print(__VERSION__);
  Serial.print(", Arduino IDE: ");
  Serial.println(ARDUINO);
  Serial.print("Created: ");
  Serial.print(__TIME__);
  Serial.print(", ");
  Serial.println(__DATE__);
  Serial.println(__FILE__);

  // initialize the button pins as input with pullup so active low
  //    make sure the button is from pin to ground
  Serial.println(" ");
  Serial.print("Initialising "); Serial.print(numberOfButtons); Serial.println(" buttons");
  for (int i = 0; i < numberOfButtons; i++)
  {
    mybuttons[i].pin = buttonPins[i];
    pinMode(mybuttons[i].pin, INPUT_PULLUP);
    mybuttons[i].led = ledPins[i];
    pinMode(mybuttons[i].led, OUTPUT);
    mybuttons[i].state = digitalRead(mybuttons[i].pin);
    mybuttons[i].prevState = mybuttons[i].state;
    mybuttons[i].score = scores[i];
    mybuttons[i].pressed = 0;
    Serial.print("Button: "); Serial.print(i);
    Serial.print(", Pin: "); Serial.print(mybuttons[i].pin);
    Serial.print(", Led: "); Serial.print(mybuttons[i].led);
    Serial.print(", Score: "); Serial.print(mybuttons[i].score);
    Serial.print(", Pressed: "); Serial.print(mybuttons[i].pressed);
    Serial.print(", State: "); Serial.print(mybuttons[i].state);
    Serial.print(", Prev state: "); Serial.println(mybuttons[i].prevState);
  }

  //initialise pulse led
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, pulseState);

  Serial.println(" ");
  Serial.println("setup() done");
  Serial.println("Press a button....");
  Serial.println(" ");
}

void loop()
{
  doPulse();
  checkForButtonStateChange();
  if (doEffect) blinkLedsAndMakeANoise();
} //loop

void checkForButtonStateChange()
{
  for (int i = 0; i < numberOfButtons; i++)
  {
    mybuttons[i].state = digitalRead(mybuttons[i].pin);
    // compare the buttonState to its previous state
    if (mybuttons[i].state != mybuttons[i].prevState) // means it changed... but which way?
    {
      if (mybuttons[i].state == LOW)  // changed to pressed
      {
        Serial.print(i);
        Serial.print(" newly pressed");
        if (mybuttons[i].pressed == 0)
        {
          mybuttons[i].pressed = 1;
          numberOfButtonsPressed++;
          Serial.print(", Unique buttons pressed "); Serial.print(numberOfButtonsPressed);

        }
        digitalWrite(mybuttons[i].led, HIGH);
        runningTotal = runningTotal + mybuttons[i].score;
        Serial.print(", Score "); Serial.println(runningTotal);
      }
      // poor man's de-bounce
      delay(50);
    }
    // save the current state as the last state, for next time through the loop
    mybuttons[i].prevState = mybuttons[i].state;
  }

  if (numberOfButtonsPressed == 5)
  {
    Serial.print("All leds on, score "); Serial.println(runningTotal);
    for (int i = 0; i < numberOfButtons; i++)
    {
      mybuttons[i].pressed = 0;
    }
    doEffect = true;
    numberOfButtonsPressed = 0;
  }
} // checkForButtonStateChange()

void blinkLedsAndMakeANoise()
{
  static byte numberOfEffects = 0;
  if (millis() - previousMillisEffect >= effectLedInterval)
  {
    effectLedInterval = random(effectLedIntervalMin, effectLedIntervalMax);
    previousMillisEffect = millis();
    effectState = !effectState;
    for (int i = 0; i < numberOfButtons; i++)
    {
      digitalWrite(mybuttons[i].led, effectState);
      if (effectState) tone(buzzer, 1000); else tone(buzzer, 500);
    }
    numberOfEffects++;
  }

  if (numberOfEffects == 10)
  {
    doEffect = false;
    numberOfEffects = 0;
    for (int i = 0; i < numberOfButtons; i++)
    {
      digitalWrite(mybuttons[i].led, LOW);
    }
    noTone(buzzer);
  }
}

void doPulse()
{
  if (millis() - previousMillisPulse >= pulseLedInterval)
  {
    previousMillisPulse = millis();
    pulseState = !pulseState;
    digitalWrite(LED_BUILTIN, pulseState);
  }
} //doPulse

I think it does what you want.

Yesss thank you very much that is exactly what i needed. If i wanted to add another pair of 5 buttons and LEDs to do the exact same thing i asume i can just copy this and give everything a bit different name and it should to the trick right?

One more question. When i tried to add the mario theme song in the loop for it to run constantly the speakers just makes a buzzing sound but nothing really happens. Should i put the command for playing the sound somewhere else?

"When i tried to add the mario theme song in the loop for it to run constantly the speakers just makes a buzzing sound but nothing really happens. Should i put the command for playing the sound somewhere else?"

Sounds like your sketch repeatedly cues it to start (start over).

xcg584:
Yesss thank you very much that is exactly what i needed. If i wanted to add another pair of 5 buttons and LEDs to do the exact same thing i asume i can just copy this and give everything a bit different name and it should to the trick right?

Depends what you mean, "give everything a bit different name". If you mean duplicate all the code with different variable names, the answer is definitely no.

What you would do, is simply add the button and LED information to the existing arrays.

well if i understand the program correctly, when the first 5 buttons light up the first 5 leds, they blink and the sound wav file is played(the sound effect).
but if i added 5 more buttons in the array with corresponding leds, they would do the exact same things right?
however i would like the sound effect for the other 5 leds(when lit up) to be different wav file from the SD card, not the same one as for the first 5 leds.