Hello there. I need to create a project that includes 4 button switches and a servo.
The plan is this: When and only when the 4 buttons are pushed in the correct order, the servo is activated. The project must also reset after 10 seconds, regardless of button pushing to give the arduino a chance to reset all the variables so that the user can start over if he pressed the buttons in the wrong order.
The program works but not perfectly. The issue is that if i press red button once, and then the wrong remaining password, the variable red_key is and will always be true until the program restarts after 10 seconds. So after pressing red, and then the wrong password and then the remaining correct password which is blue green yellow the servo will start regardless that red button was not pressed in the correct order. I have tried (but failed) to create a table for the correct password and a table for the attempt, and to assign values to the attempt table and then comparing them with the password table but i cant get this to work. Any help will be appreciated!
My code so far is the following:
#include <Servo.h>
/**
*the code is red blue green yellow
*after short period of time the code counter resets
*/
Servo servo1;
int pos1 = 0;
//naming arduino pins
const int red = 2;
const int blue = 3;
const int green = 4;
const int yellow = 5;
boolean red_key = false;
boolean blue_key = false;
boolean green_key = false;
boolean yellow_key = false;
//a function i found that resets the arduino through an assemply command.
//it does work for what i am trying to do
void software_Reset();
char a='s'; // a is initialized to s signaling the start of the program
//a variable that counts the time the program has been running
unsigned long timer;
void setup(){
servo1.attach(9);//set pin9 for servo
servo1.write(0);//initialize servo
servo1.detach();//to prevent static noize of servo
pinMode(red, INPUT_PULLUP);
pinMode(blue, INPUT_PULLUP);
pinMode(green, INPUT_PULLUP);
pinMode(yellow, INPUT_PULLUP);
Serial.begin(9600);
}
void loop(){
if(timer >= 10000)
{
software_Reset(); // reset after 10 seconds
}
int button_state_red = digitalRead(red);
int button_state_blue = digitalRead(blue);
int button_state_green = digitalRead(green);
int button_state_yellow = digitalRead(yellow);
if(button_state_red && a=='s')
{ //if button red is pushed and previous state of variable a is s then button red was pushed
//in the correct order
red_key = true;
a='r'; // r (red) is setting that the next "previous state" should be red
}
if(button_state_blue&& red_key && a=='r'){
//again button blue pressed now, previous button pressed was red according to a.
blue_key = true;
a='b';
}
if(button_state_green && red_key && blue_key && a=='b')
{
//same as before. i am aware that if blue_key then definately red_key but i will fix it
//after program works perfectly
green_key = true;
a='g';
}
if (button_state_yellow && green_key && red_key && blue_key && a=='g')
{
//same thing here. if green_key then definately red and blue key, i will fix this too
yellow_key = true;
}
if(red_key && blue_key && green_key && yellow_key)
{
/**
*opening the lock
*/
servo1.attach(9);
for(pos1 = 0; pos1 < 180; pos1 += 1){
servo1.write(pos1);
delay(10);
}
servo1.detach();
/**
*closing the lock after 5sec
*/
delay(5000);
servo1.attach(9);
servo1.write(0); //return to default state
delay(650);//delay until return to default state
servo1.detach();
/**
*reset the buttons
*/
red_key = false;
blue_key = false;
green_key = false;
yellow_key = false;
//software_Reset(); not using this will testing
}
timer=millis();
Serial.println(timer);
}
void software_Reset()
{
asm volatile(" jmp 0");
}
Can you please edit the post to use code tags? See How to use this forum.
Also, after a quick look, if you placed all the buttons into an array and number them that way it would be pretty simple. Then the right sequence of colors is now a right sequence of numbers. And that's pretty easy to check. Can do that in like 25% of your code
And extending it to 20 buttons would not take any more code 
Sorry for not using code tags, i have edited the post to use them.
Update: I got it to work, thanks for your time.
Then it's always a nice thing to paste the working code here as well 
IDE has AutoFormat in the Tools menu, it would clean those braces up.
Don't use an int (int16_t) where a byte (uint8_t) will do, like for pin numbers. byte holds 0 to 255.
An Uno has 2048 bytes of RAM not just for variables but the stack space used by every function call in your code and your libraries code. Good practices save RAM, bad practices push limits or cross them. The more room you have left, the more potential your sketch has to grow or be reused.
Does that really only check the buttons once every 10 seconds?
Septillion, I am doing this project at my practice and I Am currently at home so I can't post the full code, however I will try to show you in an example below how it works.
My original idea as I said above was to use two tables, one with the password set and one which will be set while the user attempts to put in the correct one, however I had a load of logical errors which were due to random reasons, such as where to use software_Reset(); , also the println commands I use weren't working so because I have to have this complete by Saturday morning I decided to keep it really simple. This was the least of my problems, as I was waiting for a relay I had ordered because the buttons I was given have lights on them that require 12v to work. If I have time,I will post the full code here tomorrow just for your curiosity.
Goforsmoke, I hadn't really thought of using unsigned integers for naming the pins. While I don't think it's relative to my problem at hand,I would like to thank you because I intend to use arduino for other random projects and this is useful advice. I will set them to unsigned before delivering the project. Also, no the program does not check the buttons every ten seconds. It simply was resetting every ten seconds, regardless of when or whether buttons where being pushed. This code has been removed as it is no longer useful. The program now resets only after the successful attempt to turn on the servo.
//Septillion the code is something like this:
if(button_state_red)
{
red_key=true;
button_state_red=false;
}
if (button_state_blue)
{
if (red_key)
{
blue_key=true;
button_state_blue=false;
}
}
if( button_state_green)
{
if(blue_key)
{
green_key=true;
button_state_green=false;
}
else
{
red_key=false;
button_state_green=false;
}
}
if (button_state_yellow)
{
if(green_key)
{
yellow_key=true;
button_state_yellow=false;
}
else
{
blue_key=false;
red_key=false;
button_state_yellow=false;
}
}
if(yellow_key)
{
servo_attach etc
etc
software_Reset();
}
It's not just that they're signed, ints are signed 16 bit. They take 2 bytes each.
If you want 8 bit signed then you can use char or int8_t. They hold -128 to 127.
I like bytes for pin numbers and counters.
I use them to time contact switch debounce.
They're good for up to 1/4 second waits (non-blocking way of course), loads of hardware and serial IO code has 100ms waits all through it, why use 32-bit variables (unsigned long) to time what 8-bits can? What happens when you want to connect 20 or more?
When you go into the AVR chip itself, it is 8-bit registers doing one 8-bit operation at a time. 16 bit values take longer, good thing Uno has 16000 cycles per millisecond, hey?
We learn to int as beginner C/C++ PC programmers. It gets to be automatic. Time to be aware, in AVR's the ceilings are low, don't bump your head!
ATMEL AVR datasheet links for all:
ATMEL AVR datasheet links for the ATmega328P (Uno) chip family. Be SURE to get the complete one.
You can find threads on this forum covering most aspects of that datasheet in one way or another.
linkin2895:
such as where to use software_Reset();
Simple, don't 
linkin2895:
as I was waiting for a relay I had ordered because the buttons I was given have lights on them that require 12v to work.
I don't see the relation between 12V for button lights and a relay...
Simple rewrite:
#include <Servo.h>
//The buttons: Red, Blue, Green, Yellow
const byte ButtonPins[] = {2, 3, 4, 5};
//servo pin
const byte ServoPin = 9;
//order of buttons to be correct
//Not limited to all buttons once
//Because of no debounce/state change, no buttons twice after each other
const byte ButtonOrder[] = {0, 1, 2, 3};
//time after which a incorrect password is reset
const unsigned int TimeoutTime = 10 * 1000U;
//holds number of correct presses
byte numberInputsCorrect = 0;
//holds if a wrong key is pressed
bool inputError = false;
//holds last time a button was pressed
unsigned long timeoutTimer;
//servo object
Servo servo;
//servo start/wrong position
const byte ServoPositionStart = 0;
//servo correct position
const byte ServoPositionCorrect = 180;
void setup(){
for(byte i = 0; i < sizeof(ButtonPins); i++){
pinMode(ButtonPins[i], INPUT_PULLUP);
}
}
void loop(){
//we only check as long order isn't finished
if(numberInputsCorrect < sizeof(ButtonOrder)){
//check all buttons
for(byte i = 0; i < sizeof(ButtonPins); i++){
//If a button is presses
if(digitalRead(ButtonPins[i]) == LOW){
//reset the Timeout
timeoutTimer = millis();
//check if it's the correct next button
//but only if we didn't see an error
if(i == ButtonOrder[numberInputsCorrect] && !inputError){
numberInputsCorrect++;
}
//because no state change/debounce it might be still the last button
//can only be if we at least have 1 correct button press`
else if(numberInputsCorrect && i == ButtonOrder[numberInputsCorrect - 1]){
//but we don' need to do anyting then :p
}
//otherwise it' a wrong button
else{
inputError = true;
}
}
}
}
//if no button pressed for TimeoutTime
if(millis() - timeoutTimer > TimeoutTime){
//reset number of correct presses and possible error
resetAttempt();
}
//let's see if the order is correct
if(numberInputsCorrect == sizeof(ButtonOrder)){
actuateLock();
resetAttempt();
}
}
void resetAttempt(){
//reset number of correct presses and possible error
numberInputsCorrect = 0;
inputError = false;
}
void actuateLock(){
/**
*opening the lock
*/
servo.attach(ServoPin);
for(byte pos = ServoPositionStart; pos < ServoPositionCorrect; pos++){
servo.write(pos);
delay(10);
}
servo.detach();
/**
*closing the lock after 5sec
*/
delay(5000);
servo.attach(ServoPin);
servo.write(ServoPositionStart); //return to default state
delay(650);//delay until return to default state
servo.detach();
}
Now it's even possible to have a sequence with one button used multiple times. For example Red, Blue, Red, Green, Blue, Yellow. And even extend it to 20 keys etc.
If you want, I released a button library with array example that uses 4 bytes per button, each with its own 8-bit timer since I only need 5ms to debounce jumpers tapping ground.
You can run this with 4 buttons or just 4 jumpers in the pin holes, ground any to press.
Make sure serial monitor is set to 250000 baud. Serial output queue empties quick at 25K cps.
It's easy-use. Your sketch does not have to track previous state, THAT is part of the status byte.
If the return is 1, the button has been released and debounced.
If the return is 2, the button has been pressed and debounced.
There's 8 states but those 2 detect change and bit 2 is the debounce state used in debounce.
For most sketches, all that matters is a 2.
Make the buttons, initialize the buttons, put the run function and read in loop() and look for 2.
buttonarrayexample.ino (2.19 KB)
button.cpp (2.31 KB)
button.h (1.02 KB)
Yeah, debouncing (whatever way/library) might be a good idea. That gives you the option to use the state change for actions which means you can also have something like red red blue as a sequence. I fixed the debounce/lack of state change by ignoring the last correct button 0:-)
I use Bounce2 as my default debouncer. Yes, uses a bit more memory because of the longs but gives you some extra options besides debouncing and it's very simple. But yeah, less memory is great! And I mis handling of long press etc in Bounce2 but that would also make it more complex.... Always a trade of 
Look at the size of my files, includes comments.
The button read looks complex but that sub-microsecond bit dance gives pin state history from initial contact to stable state confirmed in one single value. It even knows to quit timing every other bounce. And in any case, it's a library, who looks inside of those? It's how you use it that counts. How easy it is to use, resources taken, when RAM started out tight it all counts.