Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <Servo.h>
- /* Mini secret knock gumball machine
- * by Halifax Makerspace
- *
- * Based on the secret knock door unlock code:
- By Steve Hoefer http://grathio.com
- Version 0.1.10.20.10
- Licensed under Creative Commons Attribution-Noncommercial-Share Alike 3.0
- http://creativecommons.org/licenses/by-nc-sa/3.0/us/
- (In short: Do what you want, just be sure to include this line and the four above it, and don't sell it or use it in anything you sell without contacting me.)
- */
- // Pin definitions
- const int knockSensor = A0; // Piezo sensor on pin 0.
- const int gumServo = 9; // Gear motor used to turn the lock.
- const int redLED = 13; // Status LED
- // Tuning constants. Could be made vars and hoooked to potentiometers for soft configuration, etc.
- const int threshold = 70; // Minimum signal from the piezo to register as a knock
- const int rejectValue = 25; // If an individual knock is off by this percentage of a knock we don't unlock..
- const int averageRejectValue = 15; // If the average timing of the knocks is off by this percent we don't unlock.
- const int knockFadeTime = 150; // milliseconds we allow a knock to fade before we listen for another one. (Debounce timer.)
- const int lockTurnTime = 650; // milliseconds that we run the motor to get it to go a half turn.
- const int maximumKnocks = 20; // Maximum number of knocks to listen for.
- const int knockComplete = 1200; // Longest time to wait for a knock before we assume that it's finished.
- // Variables.
- int secretCode[maximumKnocks] = {50, 25, 25, 50, 100, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Initial setup: "Shave and a Hair Cut, two bits."
- int knockReadings[maximumKnocks]; // When someone knocks this array fills with delays between knocks.
- int knockSensorValue = 0; // Last reading of the knock sensor.
- int programButtonPressed = false; // Flag so we remember the programming button setting at the end of the cycle.
- Servo myservo;
- void setup() {
- pinMode(gumServo, OUTPUT);
- pinMode(redLED, OUTPUT);
- Serial.begin(9600); // Uncomment the Serial.bla lines for debugging.
- Serial.println("Program start."); // but feel free to comment them out after it's working right.
- myservo.attach(gumServo);
- myservo.write(25);
- }
- void loop() {
- // Listen for any knock at all.
- knockSensorValue = analogRead(knockSensor);
- if (knockSensorValue >=threshold){
- listenToSecretKnock();
- }
- }
- // Records the timing of knocks.
- void listenToSecretKnock(){
- Serial.println("knock starting");
- int i = 0;
- // First lets reset the listening array.
- for (i=0;i<maximumKnocks;i++){
- knockReadings[i]=0;
- }
- int currentKnockNumber=0; // Incrementer for the array.
- int startTime=millis(); // Reference for when this knock started.
- int now=millis();
- digitalWrite(redLED, LOW); // we blink the LED for a bit as a visual indicator of the knock.
- delay(knockFadeTime); // wait for this peak to fade before we listen to the next one.
- digitalWrite(redLED, HIGH);
- do {
- //listen for the next knock or wait for it to timeout.
- knockSensorValue = analogRead(knockSensor);
- if (knockSensorValue >= threshold){ //got another knock...
- //record the delay time.
- Serial.print(knockSensorValue);
- Serial.println(" knock.");
- now=millis();
- knockReadings[currentKnockNumber] = now-startTime;
- currentKnockNumber ++; //increment the counter
- startTime=now;
- // and reset our timer for the next knock
- digitalWrite(redLED, LOW);
- delay(knockFadeTime); // again, a little delay to let the knock decay.
- digitalWrite(redLED, HIGH);
- }
- now=millis();
- //did we timeout or run out of knocks?
- } while ((now-startTime < knockComplete) && (currentKnockNumber < maximumKnocks));
- //we've got our knock recorded, lets see if it's valid
- if (validateKnock() == true){
- dispenseGum();
- } else {
- Serial.println("Secret knock failed.");
- for (i=0;i<4;i++){
- digitalWrite(redLED, HIGH);
- delay(100);
- digitalWrite(redLED, LOW);
- delay(100);
- }
- }
- }
- void dispenseGum(){
- for (int pos = 25; pos <= 115; pos += 5) { // goes from 0 degrees to 180 degrees
- // in steps of 1 degree
- myservo.write(pos); // tell servo to go to position in variable 'pos'
- delay(15); // waits 15ms for the servo to reach the position
- }
- delay(500);
- for (int pos = 115; pos >= 25; pos -= 1) { // goes from 180 degrees to 0 degrees
- myservo.write(pos); // tell servo to go to position in variable 'pos'
- delay(15); // waits 15ms for the servo to reach the position
- }
- }
- // Sees if our knock matches the secret.
- // returns true if it's a good knock, false if it's not.
- boolean validateKnock(){
- int i=0;
- // simplest check first: Did we get the right number of knocks?
- int currentKnockCount = 0;
- int secretKnockCount = 0;
- int maxKnockInterval = 0; // We use this later to normalize the times.
- for (i=0;i<maximumKnocks;i++){
- if (knockReadings[i] > 0){
- currentKnockCount++;
- }
- if (secretCode[i] > 0){ //todo: precalculate this.
- secretKnockCount++;
- }
- if (knockReadings[i] > maxKnockInterval){ // collect normalization data while we're looping.
- maxKnockInterval = knockReadings[i];
- }
- }
- if (currentKnockCount != secretKnockCount){
- return false;
- }
- /* Now we compare the relative intervals of our knocks, not the absolute time between them.
- (ie: if you do the same pattern slow or fast it should still open the door.)
- This makes it less picky, which while making it less secure can also make it
- less of a pain to use if you're tempo is a little slow or fast.
- */
- int totaltimeDifferences=0;
- int timeDiff=0;
- for (i=0;i<maximumKnocks;i++){ // Normalize the times
- knockReadings[i]= map(knockReadings[i],0, maxKnockInterval, 0, 100);
- timeDiff = abs(knockReadings[i]-secretCode[i]);
- if (timeDiff > rejectValue){ // Individual value too far out of whack
- return false;
- }
- totaltimeDifferences += timeDiff;
- }
- // It can also fail if the whole thing is too inaccurate.
- if (totaltimeDifferences/secretKnockCount>averageRejectValue){
- return false;
- }
- return true;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement