For the bag movement alarm system, I need to have a secret security code that can shut off the alarm. I wanted to have this be very simple, so just use a multi button interface. For the project, I decided to chose 4 buttons (largely as this gives a good number for simple security, and also because I currently only have 5 push buttons total!). I also added the speaker element, to give an sense of what the end result would be.
So I built on top of the power circuit I build before, adding the buttons and the speaker.
To add the buttons:
1) I connected one side to the positive power rail through a resistor (1K), and the other side directly to the ground rail. If I connected it the other way (through a resistor to ground, and the other side to the positive rail), then the ground would be connected to the board through the resistor, and would turn on before I wanted it to (see the problem I had earlier). Interestingly, the rail is actually at +9V, but it still seems to work (the pins on the Uno may be taking a beating… I should probably connect this to the +5V).
2) I connected the side that was connect from the +9V rail to the digital pins on the board (I chose pins 8 to 11), through a 47k ohm resistor. I needed this resistor, else any of the buttons (not just the power button) would make the board turn on. With these resistors, pressing the code buttons did not turn the device on. Maybe this would simplify things, but was not my objective.
3) When programming, the HIGH means that the button is not pressed, and the LOW means the that button is pressed.
My first cut at the button program (without the speaker) used delays, and simply turned the LED on and off.
// ——–CONSTANTS—————
const int OnOffPin = 7;
const int button1Pin = 8;
const int button2Pin = 9;
const int button3Pin = 10;
const int button4Pin = 11;const int SecretCode[] = {1, 2, 3, 4};
//———— VARIABLES———————
int CodeEntered[] = {0, 0, 0, 0};void setup() {
// initialize digital OnOffPin as an output.
pinMode(OnOffPin, OUTPUT);// initialize digital pin 13 to show when the OnOffPin is high
pinMode(13, OUTPUT);//turn the Circuit on via transistor
digitalWrite(OnOffPin, HIGH); // turn the OnOffPin HIGH and thus the transistor gate open by making the voltage HIGH
digitalWrite(13, HIGH);//intialize digital pin 8 to 12 as button inputs
pinMode(button1Pin, INPUT);
pinMode(button2Pin, INPUT);
pinMode(button3Pin, INPUT);
pinMode(button4Pin, INPUT);Serial.begin(9600);
}void loop() {
int button1 = digitalRead(button1Pin); //normally tied to ground
int button2 = digitalRead(button2Pin);
int button3 = digitalRead(button3Pin);
int button4 = digitalRead(button4Pin);if (button1 == HIGH) {
shiftCodeEntered(1);
delay(500);
}if (button2 == HIGH) {
shiftCodeEntered(2);
delay(500);
}if (button3 == HIGH) {
shiftCodeEntered(3);
delay(500);
}if (button4 == HIGH) {
shiftCodeEntered(4);
delay(500);
}Serial.print(CodeEntered[0]);
Serial.print(CodeEntered[1]);
Serial.print(CodeEntered[2]);
Serial.print(CodeEntered[3]);
Serial.println();
delay(500);if (CodeEntered[0] == SecretCode[0] && CodeEntered[1] == SecretCode[1] && CodeEntered[2] == SecretCode[2] && CodeEntered[3] == SecretCode[3]){
digitalWrite(13, LOW);
Serial.println(“Matched!”);
}} //loop
void shiftCodeEntered(int digit) {
CodeEntered[0] = CodeEntered[1];
CodeEntered[1] = CodeEntered[2];
CodeEntered[2] = CodeEntered[3];
CodeEntered[3] = digit;
}
I worked! However, you needed to wait between button presses to put in the code, as the delay to ensure that one press of the button did not give you a ton of that input meant that you couldn’t press things fast, even if it was a different button. It also meant that if you pressed and held, you would get a new input of that button every 1/2 second. Not my objective.
So the next iteration, I added two things: (1) the speaker, and (2) made the code non-blocking (i.e. used states and millis() instead of delay)
To connect the speaker:
1) I utilized a 2n3906 transistor, with the base connected to a digital PWM pin (pin 6 in this case) via a variable resistor (a 100k pot). I needed a lot of resistance else, again, the board would turn on automatically.
2) I connected the source to ground, and the drain to the negative side of a small piezo speaker.
3) The positive side of the piezo speaker was connected to the positive power. I was using a +5V speaker which I connected to the Uno +5V pin, but when I tested another speaker that could take the larger voltage I attached it to the +9v rail on the breadboard.
To make it non-blocking, I utilized various states, which involved using arrays to store those states so that I could reuse the function of reading the pin, comparing to the old state, and deciding if the button had been pressed.
The code is:
/* alarmbox.ino
Implements a security code using buttons as the way to input the code.
Number of buttons, the number of digits in the secret code, and the
numerical value of each button (for the code) can be defined independently.This code can be used to take any action when the secret code is entered
correctly. In this example, the on board LED is on while the secret code
has not been correctly entered, and then is turned off when the secret
code is entered correctly.The sketch also prints the code that has been entered so far to the serial
monitor, along with if it has been matched (only when matched).There is no need for resetting the code entered periodically as the code
entered is simply made up of the last X number of digits that had been
entered (X depends on the length of the secret code).The circuit:
* If no LED on the board that is attached to pin 13, attach an LED from
pin 13 to ground
* For each button you choose to have for inputs into the entered code
(in this sketch there are four buttons, but you can change that):
— Use a normally open button (i.e. only on when you press it, then it pops up)
— Connect from one side of the button to ground through a resistor
(A decent size, in this example 1k resistors were used but you can use 10k
or similar larger value.)
— Connect from the other side of the button to a digital I/O pin on the
arduino board. (in this sketch, button 1 was connected to pin 8,
button 2 to pin 9, button 3 to pin 10, button 4 to pin 11, but you can
change that as long as you change it in the sketch (noted where you do
that).Created December 2014
by MakerSelf (makerself.wordpress.com)
Please use, modify and be merry!*/
//========================================
// ——–CONSTANTS—————
const int secretCodeLength = 7;
const int secretCode[secretCodeLength] = {1, 2, 3, 4, 3, 2, 1};
const int codeEnteredInitializedValue = 0;const int numberOfButtons = 4;
const int buttonPins[numberOfButtons] = {8, 9, 10, 11}; //put your pins here
const int buttonValues[numberOfButtons] = {1, 2, 3, 4}; //put the values of the button you want to use here
const int buttonInterval = 300; //this is the sensitivity of the button (i.e. how quickly can you double press the button)
const byte buttonPressedState = LOW;
const byte buttonNotPressedState = HIGH;const int OnOffPin = 7;
const int speakerPin = 6;//———— VARIABLES———————
int codeEntered[secretCodeLength]; //this gets initilized
byte buttonStates[numberOfButtons]; //this gets initilized
unsigned long previousButtonMillis[numberOfButtons]; //this gets initilized
byte speakerState;unsigned long previousSerialMillis; //for debugging or printing to the serial if that is the objective. Gets initialized to zero when begin Serial Communication.
// ==================================================================================// ——–MAIN PROGRAM—————
void setup() {
//turn the Circuit on via transistor
pinMode(OnOffPin, OUTPUT); // initialize digital OnOffPin as an output.
digitalWrite(OnOffPin, HIGH); // turn the OnOffPin HIGH and thus the transistor gate open by making the voltage HIGH
pinMode(13, OUTPUT); // use digital pin 13 to show when the OnOffPin is high. for debugging, to be removed in final version.
digitalWrite(13, HIGH); // use digital pin 13 to show when the OnOffPin is high. for debugging, to be removed in final version.//intialize speaker
pinMode(speakerPin, OUTPUT);
speakerState = true;//initialize codeEntered and Buttons
initializeCodeEntered(); //makes the code entered be all zeros (or whatever value you define)
initializeButtons(); //make the buttons ready to be pressed//to communicate and debug
Serial.begin(9600); //for debugging, to be removed in final version if you do not plan to use the serial monitor in your project.
previousSerialMillis = 0; //for debugging, to be removed in final version if you do not plan to use the serial monitor in your project.
} //end setupvoid loop() {
//——–REAL WORLD—————
readButtonsAndShiftEnteredCode(); //read the buttons and add them to the entered code if a button has been pressed
boolean isMatched = doesCodeEnteredMatchSecretCode(); //check to see if the code matchesif (isMatched == true) { //if the entered code matches the secret code, take some action
digitalWrite(13, LOW); //NOTE: insert the things that you want to do when there is matched code here
speakerState = false;
} // if isMatched
// NOTE: you can put an else in here if there are actions to be taken repeatedly
// while code is not matched. However, this will get done extremely rapidly
// unless you put in another millis checker (i.e. only do when a certain number
// of millis has gone by)
//———————————if (millis() > 5000) {
speakerPlay(speakerState);
}//——–SERIAL COMMUNICATIONS————- //This section is unnecessary if you do not plan to use the serial monitor in your project.
//print out the entered code and if matched.
if ((millis() – previousSerialMillis) > 1000) { //only print every so often, eg. every 1 second
for (int x = 0; x < secretCodeLength; x++){ //print the secretCode
Serial.print(codeEntered[x]);
} //end for
if (isMatched == true) { //print if it matches
Serial.print(” Matched!”);
} //end if
Serial.println(); //make new line
previousSerialMillis = millis(); //record when you did the last print
} //end serial printing if
//——————————————-} //end loop
// ==================================================================================
// ——–FUNCTIONS—————
void readButtonsAndShiftEnteredCode() { //PURPOSE: read the buttons and then call shiftCodeEntered if a button has been are pressed
for (int x = 0; x < numberOfButtons; x++) { //step through all buttons
unsigned long currentMillis = millis();
if ((currentMillis – previousButtonMillis[x]) > buttonInterval) { //check to see if enough time has passed since last press
int newButtonState = digitalRead(buttonPins[x]); //if enough time has passed, read the buttons
if (newButtonState == buttonPressedState && buttonStates[x] == buttonNotPressedState) { //only add to the code entered if it has from NotPressed to Pressed (i.e pressing and holding does not give more than one press)
shiftCodeEntered(buttonValues[x]); //if button was pressed, add its value to code array
previousButtonMillis[x] = currentMillis; //reset button time
} //end button state check if
buttonStates[x] = newButtonState; //update the button state
} //end millis if
} //end of number of buttons for loop
} //end readButtonsvoid shiftCodeEntered(int buttonValue) { //PURPOSE: shifts the code one to the left, and then adds the value of the most recent pressed button on the right
for (int x = 0; x < secretCodeLength; x++) { //step through the code
codeEntered[x] = codeEntered[x+1]; //for each code spot, shift it to the left. the first digit gets lost.
if (x == (secretCodeLength-1)) { //if the last spot, add the new digit from the button press
codeEntered[x] = buttonValue;
} //if
}//for
} //end shiftCodeEnteredvoid initializeCodeEntered() { //PURPOSE: initializes all spots in the code entered at the initization value, which is typically zero. This is so you dont get an error and should be a different set of characters than is on the buttons (i.e. if the buttons are of value 1 – 4, make initilization 0) or ensure that this code will not be the secret code (i.e. can have any code except for all zeros)
//make each of the codes equal to the initial value it is to be assigned (typically 0)
for (int x = 0; x < secretCodeLength; x++) { //step through the code entered
codeEntered[x] = codeEnteredInitializedValue; //for each digit, get it to the initilized value (typically zero)
}// end for
} //end resetCodeEnteredvoid initializeButtons() { //PURPOSE: initilizes all the buttons as inputs, as not pressed, and waiting for a press
for (int x = 0; x < numberOfButtons; x++) { //step through the buttons
pinMode(buttonPins[x], INPUT); //for each button, make that buttons pin an input
buttonStates[x] = buttonNotPressedState; //for each button, intialize each button as not pressed
previousButtonMillis[x] = 0; //for each button, intialize when it was last pressed at zero (so can record future presses correctly)
}
} //end initializeButtonsboolean doesCodeEnteredMatchSecretCode() { //PURPOSE: check each digit of the entered code and the secret code against each other to see if they match
boolean isMatched = true; //assume the code is matched, and then make it not matched as soon as there is one different
for (int x = 0; x < secretCodeLength; x++) { //step through the code entered
if (isMatched == false || codeEntered[x] != secretCode[x]) { //if the code entered digit at that code spot does not equal the secret code digit at the code spot, or do not match in any previous checks, then make it false
isMatched = false; //for each digit, get it to the initilized value (typically zero)
} //end if
} // end for
return isMatched; //return this to main program, to tell it if the code was matched or not
} //end doesCodeEnteredMatchSecretCodevoid speakerPlay(boolean isSpeakerOn) {
if (isSpeakerOn == true) {
// tone(speakerPin, 100, 1000);
tone(speakerPin, 200, 1000);
} //end if speaker on
} //end speakerPlay
I was extremely happy with the solution for the button based security code. I had searched online for some examples, but had not discovered something that did what I wanted to do. This is also very simple in that it does not need a reset button or anything like that: it just uses the last digits that had been pressed to make the comparison.
I stripped it down a bit (removed the power circuit details) and then decided to post it to the forum on the Arduino website to (1) share, to save work for others should they want to do something similar and (2) see if I could get some feedback on how to make it better. The stripped down version that focuses only on the button security code is available at the post on the Arduino.cc forum.
The final set up looks pretty cool:
And a video of it in action! I really do need to get a louder buzzer! But the key thing: it works!!