Debounced buttons and timing

Hi - I'm working on a "simple" project that is rapidly becoming complex and I'm stuck with some basic concepts that I'm sure some of the more seasoned vets on this board can solve in an instant. For now I'll phrase everything in simplistic terms to see if I can solicit some conceptual help before I get too deep into the code.

My project boils down to this - four momentary buttons, four LEDs. Each of the buttons should be available to the user once the program is loaded, and when pressed, each should light up it's own LED, BUT:
-a press of each button should light it's corresponding LED for only 100ms;
-two or more buttons can't be pressed at once;
-each button may not light up an LED again within 20 seconds of being pressed

Can anyone offer some conceptual advice or even some simple code? I started out with the debounce tutorial ( http://www.arduino.cc/en/Tutorial/Debounce), which I understand, but - can I expand this to each of the four buttons in the same loop by adding the same code but with different buttons/pins?

As for the timing - what is a good approach here? Should I use millis() and "if" statements with subtractive timing?

Thanks in advance for your help.

geek_tk:
-two or more buttons can't be pressed at once;

Physically? Or do you want the code to detect that? Keep in mind that millions of instructions are run per second and "same time" to you may not mean "same time" to the processor.

which I understand, but - can I expand this to each of the four buttons in the same loop by adding the same code but with different buttons/pins?

Yes. You may find it easier to implement everything into arrays or even an array of structs to keep the code clean.

As for the timing - what is a good approach here? Should I use millis() and if/else statements?

Yes, the Blink without Delay example is a good way to learn how to use millis() instead of the delay() statement.

Thanks Arrch for your help.

Can you point me to a tutorial on the arrays and structs you refer to? I forsee this getting complex quite quickly to keep track of all the different buttons, states, and timing variables so I am interested in anything that will help me keep it tidy.

I had a look at the blink without delay example, and I see that it will be useful. Perhaps I am limited here by my own (mis)understanding, but is it possible to keep track of all four buttons simultaneously in this fashion?

geek_tk:
Can you point me to a tutorial on the arrays and structs you refer to? I forsee this getting complex quite quickly to keep track of all the different buttons, states, and timing variables so I am interested in anything that will help me keep it tidy.

Just google c++ arrays and structs, there are plenty of tutorials for them all around the web; they are very important aspects of the C language.

geek_tk:
I had a look at the blink without delay example, and I see that it will be useful. Perhaps I am limited here by my own (mis)understanding, but is it possible to keep track of all four buttons simultaneously in this fashion?

Yes.

Thanks Arrch.

Here's a simplified bit of my code. It complies OK. I have a model of a ship, with lights on the mast and at a few other places. For now I just left in one light, and one button to simplify things so I can get the concept down before I add in all the other buttons and lights.

const int MAST = 13; //pin for mast light
const int MASTbutton = 8; //button for mast light

int MASTstate = HIGH; //the current state of the top of mast light
int MASTbuttonState; //the current reading from the top of mast 
int lastMASTbuttonState = LOW; //the previous reading from the top of mast 

long lastMASTbuttonDebounceTime = 0;  //the last time the top mast was toggled
long MASTbuttonDebounceDelay = 50; // the debounce time, increase if output flickers

void setup() {
  
pinMode (MAST, OUTPUT);
pinMode (MASTbutton, INPUT);

}

void loop() {
  
int MASTbuttonReading = digitalRead(MASTbutton); //read the state of the top cannon button into a local variable
if (MASTbuttonReading != lastMASTbuttonState)
   
    lastMASTbuttonDebounceTime= millis();  //reset the bouncing timer
  
  if((millis() - lastMASTbuttonDebounceTime)> MASTbuttonDebounceDelay) {
    //whatever the reading is at, it's been there for longer than the debounce delay,
    //so take it as it's current state
    MASTbuttonState = MASTbuttonReading;
  }
    //set the top mast light using the state of the button:
  digitalWrite (MAST, MASTbuttonState);
      //save the reading. Next time through the loop it will be in the lastMASTbuttonSstate:
    lastMASTbuttonState = MASTbuttonReading;

}

What code would I need to add into the loop so that when pressed, this button for my mast light only lights up the mast light for one second, and may not be pressed again for 20 seconds?

Would I need to add another local variable that stores the time when the MASTbuttonState turns from LOW to HIGH, and then turns the state back from HIGH to LOW when millis() exceeds that time by 1 second?

geek_tk:
Would I need to add another local variable that stores the time when the MASTbuttonState turns from LOW to HIGH, and then turns the state back from HIGH to LOW when millis() exceeds that time by 1 second?

Yes. To keep it off for twenty seconds, you can just use a state variable. Whenever the button is pressed, the state variable is set to say 1. Then, after 20 seconds it's set back to zero. When you go to turn it on, you should be checking for the status of the state variable.