Advertisement
Guest User

Mini secret knock gumball machine

a guest
Apr 29th, 2017
99
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 6.63 KB | None | 0 0
  1. #include <Servo.h>
  2.  
  3. /* Mini secret knock gumball machine
  4.  *  by Halifax Makerspace
  5.  *  
  6.  *  Based on the secret knock door unlock code:
  7.    
  8.    By Steve Hoefer http://grathio.com
  9.    Version 0.1.10.20.10
  10.    Licensed under Creative Commons Attribution-Noncommercial-Share Alike 3.0
  11.    http://creativecommons.org/licenses/by-nc-sa/3.0/us/
  12.    (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.)
  13.  
  14.  */
  15.  
  16. // Pin definitions
  17. const int knockSensor = A0;         // Piezo sensor on pin 0.
  18. const int gumServo = 9;           // Gear motor used to turn the lock.
  19. const int redLED = 13;              // Status LED
  20.  
  21. // Tuning constants.  Could be made vars and hoooked to potentiometers for soft configuration, etc.
  22. const int threshold = 70;           // Minimum signal from the piezo to register as a knock
  23. const int rejectValue = 25;        // If an individual knock is off by this percentage of a knock we don't unlock..
  24. const int averageRejectValue = 15; // If the average timing of the knocks is off by this percent we don't unlock.
  25. const int knockFadeTime = 150;     // milliseconds we allow a knock to fade before we listen for another one. (Debounce timer.)
  26. const int lockTurnTime = 650;      // milliseconds that we run the motor to get it to go a half turn.
  27.  
  28. const int maximumKnocks = 20;       // Maximum number of knocks to listen for.
  29. const int knockComplete = 1200;     // Longest time to wait for a knock before we assume that it's finished.
  30.  
  31. // Variables.
  32. 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."
  33. int knockReadings[maximumKnocks];   // When someone knocks this array fills with delays between knocks.
  34. int knockSensorValue = 0;           // Last reading of the knock sensor.
  35. int programButtonPressed = false;   // Flag so we remember the programming button setting at the end of the cycle.
  36.  
  37. Servo myservo;
  38.  
  39. void setup() {
  40.   pinMode(gumServo, OUTPUT);
  41.   pinMode(redLED, OUTPUT);
  42.  
  43.   Serial.begin(9600);                     // Uncomment the Serial.bla lines for debugging.
  44.   Serial.println("Program start.");       // but feel free to comment them out after it's working right.
  45.  
  46.   myservo.attach(gumServo);
  47.   myservo.write(25);
  48. }
  49.  
  50. void loop() {
  51.   // Listen for any knock at all.
  52.   knockSensorValue = analogRead(knockSensor);
  53.  
  54.   if (knockSensorValue >=threshold){
  55.     listenToSecretKnock();
  56.   }
  57. }
  58.  
  59. // Records the timing of knocks.
  60. void listenToSecretKnock(){
  61.   Serial.println("knock starting");  
  62.  
  63.   int i = 0;
  64.   // First lets reset the listening array.
  65.   for (i=0;i<maximumKnocks;i++){
  66.     knockReadings[i]=0;
  67.   }
  68.  
  69.   int currentKnockNumber=0;               // Incrementer for the array.
  70.   int startTime=millis();                 // Reference for when this knock started.
  71.   int now=millis();
  72.  
  73.   digitalWrite(redLED, LOW);            // we blink the LED for a bit as a visual indicator of the knock.
  74.   delay(knockFadeTime);                                 // wait for this peak to fade before we listen to the next one.
  75.   digitalWrite(redLED, HIGH);  
  76.  
  77.   do {
  78.     //listen for the next knock or wait for it to timeout.
  79.     knockSensorValue = analogRead(knockSensor);
  80.     if (knockSensorValue >= threshold){                   //got another knock...
  81.       //record the delay time.
  82.       Serial.print(knockSensorValue);
  83.       Serial.println(" knock.");
  84.       now=millis();
  85.       knockReadings[currentKnockNumber] = now-startTime;
  86.       currentKnockNumber ++;                             //increment the counter
  87.       startTime=now;          
  88.       // and reset our timer for the next knock
  89.       digitalWrite(redLED, LOW);  
  90.       delay(knockFadeTime);                              // again, a little delay to let the knock decay.
  91.       digitalWrite(redLED, HIGH);
  92.     }
  93.  
  94.     now=millis();
  95.    
  96.     //did we timeout or run out of knocks?
  97.   } while ((now-startTime < knockComplete) && (currentKnockNumber < maximumKnocks));
  98.  
  99.   //we've got our knock recorded, lets see if it's valid
  100.   if (validateKnock() == true){
  101.     dispenseGum();
  102.   } else {
  103.     Serial.println("Secret knock failed.");
  104.     for (i=0;i<4;i++){          
  105.       digitalWrite(redLED, HIGH);
  106.       delay(100);
  107.       digitalWrite(redLED, LOW);
  108.       delay(100);
  109.     }
  110.   }
  111. }
  112.  
  113. void dispenseGum(){
  114.   for (int pos = 25; pos <= 115; pos += 5) { // goes from 0 degrees to 180 degrees
  115.     // in steps of 1 degree
  116.     myservo.write(pos);              // tell servo to go to position in variable 'pos'
  117.     delay(15);                       // waits 15ms for the servo to reach the position
  118.   }
  119.   delay(500);
  120.   for (int pos = 115; pos >= 25; pos -= 1) { // goes from 180 degrees to 0 degrees
  121.     myservo.write(pos);              // tell servo to go to position in variable 'pos'
  122.     delay(15);                       // waits 15ms for the servo to reach the position
  123.   }
  124.  
  125. }
  126.  
  127. // Sees if our knock matches the secret.
  128. // returns true if it's a good knock, false if it's not.
  129. boolean validateKnock(){
  130.   int i=0;
  131.  
  132.   // simplest check first: Did we get the right number of knocks?
  133.   int currentKnockCount = 0;
  134.   int secretKnockCount = 0;
  135.   int maxKnockInterval = 0;               // We use this later to normalize the times.
  136.  
  137.   for (i=0;i<maximumKnocks;i++){
  138.     if (knockReadings[i] > 0){
  139.       currentKnockCount++;
  140.     }
  141.     if (secretCode[i] > 0){           //todo: precalculate this.
  142.       secretKnockCount++;
  143.     }
  144.    
  145.     if (knockReadings[i] > maxKnockInterval){   // collect normalization data while we're looping.
  146.       maxKnockInterval = knockReadings[i];
  147.     }
  148.   }
  149.  
  150.   if (currentKnockCount != secretKnockCount){
  151.     return false;
  152.   }
  153.  
  154.   /*  Now we compare the relative intervals of our knocks, not the absolute time between them.
  155.       (ie: if you do the same pattern slow or fast it should still open the door.)
  156.       This makes it less picky, which while making it less secure can also make it
  157.       less of a pain to use if you're tempo is a little slow or fast.
  158.   */
  159.   int totaltimeDifferences=0;
  160.   int timeDiff=0;
  161.   for (i=0;i<maximumKnocks;i++){ // Normalize the times
  162.     knockReadings[i]= map(knockReadings[i],0, maxKnockInterval, 0, 100);      
  163.     timeDiff = abs(knockReadings[i]-secretCode[i]);
  164.     if (timeDiff > rejectValue){ // Individual value too far out of whack
  165.       return false;
  166.     }
  167.     totaltimeDifferences += timeDiff;
  168.   }
  169.   // It can also fail if the whole thing is too inaccurate.
  170.   if (totaltimeDifferences/secretKnockCount>averageRejectValue){
  171.     return false;
  172.   }
  173.  
  174.   return true;
  175.  
  176. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement