Guitar Rhythm Taper

THanks again Mee-n-Mac. I studied it a bit more and it is making some sense. One thing though I tried to make the code lin switch above and somehow the arduino is reacting to the term _PULLUP. I wonder it can be because a got a version that is about a year old with the software. Will try and load up a new version when I get to broad band zone.

I guess with the code the two elements that I don’t fully get are the usage of (TapCount-1) and also TapCntCopy. If you feel inspired anything you can say about it would be interesting to me.

My brother and I were working on a gadget that relayed finger/hand movements via the internet across the internet. The idea was to make a touch interface experience over the internet. We got it to work oK but the world of mass production was another story. Anyway, I got a couple those units rigged up somewhere that will have a servo set up to respond to a FSR signal so will test it out… Gotta find it first:-).

Cheers.

BTW Mee-n-Mac can you recommend a simple book for learning more about this program for a lay person.

shakamuni01:
THanks again Mee-n-Mac. I studied it a bit more and it is making some sense. One thing though I tried to make the code lin switch above and somehow the arduino is reacting to the term _PULLUP. I wonder it can be because a got a version that is about a year old with the software. Will try and load up a new version when I get to broad band zone.

That's almost certainly the case. I think "_PULLUP" was added with Arduino 1.0. I would suggest you get the newer version as I'm told a lot of things changed with ver 1.0. For the time being you could use the original, no "_PULLUP" code, to see what happens. It should at least compile.

I’ve assumed that there’s a non-momentary switch connected to the pin above. In one position it would supply a connection to ground and that would put the tapper into playback mode (as it did in your code). In the other position the switch could just be an open (and hence the use of the “_PULLUP” command to enable the internal pullup resistor) or you could wire it to 5V through an external pullup resistor. If you do this there’s no need to enable the internal pullup.

You should also detail how you intend to wire up the FSR and what device it is. I’ve assumed you have someting akin to what’s shown in the FSR tutorial.

http://learn.adafruit.com/force-sensitive-resistor-fsr

http://learn.adafruit.com/system/assets … 1340651420

shakamuni01:
I guess with the code the two elements that I don’t fully get are the usage of (TapCount-1) and also TapCntCopy. If you feel inspired anything you can say about it would be interesting to me.

The first is easy, the latter is somewhat complicated. Let's start with easy ! Each time a tap on the FSR is detected a count of the taps is incremented. That counter is the variable TapCount. Now if you want to record the time intervals, you need to have a beginning tap and a next tap so there's always 1 more tap than there are intervals. The program stores the interval lengths in an array "tapReadings[]". Arrays are accessed using an index which always starts at 0, ie - tapReadings[0] is the first interval stored. tapReadings[1] is the second, etc, etc. You can access the array in any order but just remember the array uses 0, not 1, for the 1'st value in the array.

There’s a trick part where the recording is done in that I use TapCount before it’s been incremented for the detected tap. That is on the 2’nd tap the code does this piece first;

          else
          {
            // record interval btw this tap and prior tap
            tapReadings[TapCount-1] = int(millis() - TapTime);
            TapTime = millis();
            //send interval count to PC
            Serial.print("Recorded interval #   ");
            Serial.println(TapCount);
          }

At this moment TapCount = 1 and so the 1’st interval time to be stored goes into tapReadings[TapCount-1] = tapReadings[0].

Then the TapCount is incremented by the next line, TapCount++.

However your question now has me looking at the usage in the playback loop …

      for (int i=0; i<(TapCount-1); i++)
      {
        servo1.write(strikePos);  // command servo to strike
        digitalWrite(LEDpin, HIGH); // pulse LED on
        delay(strikeTime);        // wait just long enough for servo to get there
        servo1.write(retractPos); // command servo back to zero position
        digitalWrite(LEDpin, LOW); // turn LED back off
        delay(tapReadings[i]);    // pauses between beats
      }

If you tapped 14 times there would be 13 intervals btw the taps. TapCount would be 14 when going to start the playback loop above. You want to playback all the intervals, not missing one but also not going beyond the array end and pulling out some not-real time. The index into the array, i, (tapReadings*) should go from the 1’st to the 13’th interval which means it should go from 0 to 12. So far, so good ?*
The for loop starts with i set to 0 and then does the servo commands. The code then waits for the 1’st interval and then comes back and increments i and then asks if i is less than (TapCount-1). If that’s true then the loop is repeated. So you get a servo strike after the 1’st interval time, then a wait (the 2’nd interval time) for the 2’nd pass of the loop. This continues, a servo strike, a wait, a servo strike, a wait …
When i gets incremented from 12 to 13 at the top of the for loop, you’ll have already waited for the 13’th interval (i was equal to 12), which is all the intervals you have. At the top of the loop i=13 and is no longer < than 13 and so the loop doesn’t run anymore. I thought that was what I wanted.
That’s a mistake ! [EDIT : or maybe not. See the explanation below]
It’s now apparent I need a last servo strike/tap to meter out that last interval. The code above needs to use TapCount, not TapCount-1 or use <= instead of < (and make sure some goofy time, beyond what was stored, is never used). Which one makes more sense to you ? I guess if I think of the servo strikes as taps then I should run to TapCount. That would be more intuitive. The loop plays back the same number of “taps”, waiting the recorded time btw the taps. I’ll fix that now.
[EDIT : Or maybe I should leave it as is ? Right now the playback repeats itself endlessly … or until the user shuts off the Arduino or goes to record mode. So when playing endlessly the 1’st servo strike of the next set is the last strike of the prior set and so the last interval does have a strike at it’s start and one after it’s timed out. Let’s use the example above. When i gets incremented to 13 the for loop doesn’t execute. Instead the while(blahblahblah) line does. If the switch is still set to playback and there’s still data (TapCount > 0) then the for loop is started all over again, with i reset to 0. The servo strike will happen in this second set and it will happen after the last interval from the 1’set (plus a few usecs which you’ll never notice). So if you want to playback loop to run over and over, with no wait between sets, the code is pretty good as is. It’ll only be wrong on the very last set played. If you want only 1 playback at a time then it’ll have to change and we’ll have to change other stuff as well (to stop the endless looping). To be perfect the code should add 1 more servo strike after that last wait if and only if on playback of the last set before going to record mode.]
TapCntCopy is a copy of TapCount (doh). It’s something of a kludge inorder to get by 2 problems. First problem was the switch you’ll use to go from playback to record will no doubt bounce. That is, when you go from one position to the next it’ll actually chatter btw the 2 positions for some period of time. There are ways to debounce a switch, in hardware and/or in software, and I do a simple version of the latter when going from record to playback mode. There’s a wait and a reread of the switch to ensure that the switch has stopped chattering before going into playback. The reason for that was that I didn’t want to stop and start and stop and start the servo commands due to switch bounce.
The second problem was that a chatter would, as I had in version 0.1, erase all the stored intervals times immediately upon entering record mode. I could have done a similar wait type debouncing as above but I also wanted to allow the user to go to record mode (by mistake perhaps) and not have the prior data erased immediately. So I made the erasure wait until the 1’st new tap was detected. Alas I had been using TapCount = 0 to know when there were no taps and I was setting TapCount = 0 when the code left playback mode on the way to record mode. I couldn’t figure out any better way so I left all the TapCount zero stuff as is and invented an new variable, the copy. The copy doesn’t get reset to 0 until the first tap, along with the rest of the tap data. If the user decides to return to playback w/o having recorded any new data, TapCntCopy is still valid and used to restore TapCount to it’s prior value. The code knows there’s no new data because TapCount = 0 going into playback mode, not what would usually occur. No doubt there’s a slicker way to do this but I didn’t happen upon it at the time I was looking at this.
As for learning how to code for the Arduino … I guess I’d look through the tutorial page and study the reference page. No doubt there’s some good “Learning Arduino for Beginners” site but I just am not aware of it. Though I am not a coding person, I do have years of reading code and debugging same and had some small experience writing code (assembly and C) before I picked up an Arduino (this year). So I had a good understanding of the basics ahead of time.

Chipping away in my brain to try and make new compartments to store new concepts here. Yeah it overall makes sense. I can see now how far off I was thinking I was quite close. There issue around what is that pause between the rhythm loops has been in the back of my mind but I was just wanting to see it partially work and thought I would address it. However now that you bring it up I had an idea about this. It would be critical to have a way to tell the code how much to pause before restarting the loop, as if it restarts it loop immediately after the last servo tap, it will not be in hip like its musicians.

So I was thinking what I could do is to tell the user to do one last tap to create an increment where it waits. Ie. the last not may be a full note, half note, or a quarter note. However for this last tap will not happen but will just be a delay. Another option is just to have another button that can be pushed that can be the rhythm interval delay button, but I think I like the first version better. Hmmmmmm I am tempted to ask you how this code will go but it may be good practice for me to try and figure out. Perhaps I could run it by you when I figure it out, or more important can’t:-).

I am about to install the newest version of Ardiuno program on my comp. I will see how the pullup function flies. If it works and I don’t need to stick a pull-up resistor on my FSR, I know I will not be complaining about it.

OK time to build this thing now. I am much better designing in the spacial world than the cyberworld.

shakamuni01:
There issue around what is that pause between the rhythm loops has been in the back of my mind but I was just wanting to see it partially work and thought I would address it. However now that you bring it up I had an idea about this. It would be critical to have a way to tell the code how much to pause before restarting the loop, as if it restarts it loop immediately after the last servo tap, it will not be in hip like its musicians.

So I was thinking what I could do is to tell the user to do one last tap to create an increment where it waits. Ie. the last not may be a full note, half note, or a quarter note. However for this last tap will not happen but will just be a delay.

Well, that's what the code will do at present. Just make the last tap happen a relatively long time after it's predecessor and that'll be the wait. Alternately it could just be some fixed in-between time, a constant you could fiddle with to get a time that seems correct. In this case we'll need to add that last tap to the playback loop, the last one that's presently missing.

I’m not sure which would be more natural to use.

shakamuni01:
I am about to install the newest version of Ardiuno program on my comp. I will see how the pullup function flies. If it works and I don’t need to stick a pull-up resistor on my FSR, I know I will not complaining about it.

I'd hadn't thought about using the internal pullup for the FSR voltage divider, it should work but it might not be optimal in that a tap gives you the biggest change in voltage when using it. I don't know what the value of the internal pullup is, it's just described as "weak" and I've heard values anywhere between 10k and 50k assigned to it.

If I might make a suggestion … I’d code up a simple test program, one that only reads the taps and reports (“print”) the value of the analog reading to the serial monitor. Setup the serial monitor and analog pin just as in the tapper program. Set some easy threshold for the tap detection and whenever a tap is detected have a loop spit out the analog readings as quick as can be done for perhaps 200 msec and that way you can “see” what a tap looks like and the difference btw a soft tap and a hard one. This might be good info to have to set a threshold for the “real” program.

Also I use a constant, “minTapInterval”, that waits after a tap (specifically what I think will be the leading edge of a tap) is detected before looking for the next tap. Right now it’s a complete guess at 100 msec. Knowing that a real tap lasts longer or shorter than “minTapInterval” would tune this constant to a better number and prevent false taps (constant too short) as well as prevent missing taps (constant too long).

{An easy threshold might be only 10 lsbs different from whatever the non-tap reading is. How would you know what the non-tap reading is ? In this “tap test” code also put a print to the serial monitor every 1 sec or so, even when there’s no tap detected. This way you’ll also get some idea of how much noise is inherent in reading the FSR voltage and how different the threshold needs to be so you don’t falsely detect non-taps due to noise}

And while it’s in my mind … it occurs to me that you could also record how hard the taps are and then command the servo to replicate the relative strengths as well. Perhaps after the original idea is all working perfectly. :twisted:

Well did got my gadget built and tried it out. I just used 2 FSR’s one for the program switch and one for the tap sensor.

So I have messed around with it for about 4 hours and slowly sluething my way through it… hopefully :slight_smile:

It seems like the code kept missing the fsr signals quite frequntly. I remember reading about this with digital equipment where I think a cap was used to hold the charge a bit longer so the when the loop check that analog input the charge would still be there.

Also I added a line

if (i == TapCount) i = 0;

In mode 0 because it seemed like the code would replay the array once and quit so I was hopping with it reached the last tapcount it would reset itself.

I also added some monitoring code that would print via the monitor. Which ofcourse I would take out when done.

I guess I could try and use a piezo mic instead which is what this guy had did for a secret knock code. At one point I had tried to cannabolize his code because most of what I wanted was there but it was too complex and by the time I cut it up to try and make it do what I wanted it didn’t work.

Anyway here is the code. I welcome any feedback. Thanks

#include <Servo.h>

Servo servo1;                    // create first servo object to control first servo

// declare the constants
const int FSR = 0;               // FSR on pin 0
const int programSwitch = 2;     // Record playback switch on pin 2
const int servoPin = 6;          // servo is on pin 6
const int LEDpin = 13;           // use onboard LED on pin 13
const int MaxNumIntervals = 20;  // the max number of intervals to be recorded
const int Tapthresh = 1;       // threshold for FSR tap detection
const int strikePos = 170;       // servo command for strike position
const int retractPos = 0;        // servo command for retract position
const unsigned long strikeTime = 100;      // time, in msec, btw the servo striking and retracting
const unsigned long dbDelay = 150;         // debounce wait time in msecs
const unsigned long minTapInterval = 100;  // min time between taps in msecs

// declare the variables
int TapCount = 0;                // Incrementer for the array
int TapCntCopy = 0;              // Copy of tap count
int mode = 0;                    // mode is record (1) or playback (0)
unsigned long TapTime=millis();  // Reference for when this tap started.
unsigned long tapReadings[MaxNumIntervals]; // set aside memory for interval times

void setup()
{

  pinMode(programSwitch, INPUT); // define record playback pin as an input
  pinMode(LEDpin, OUTPUT);       // define LED pin as an output
  digitalWrite(LEDpin, LOW);     // set the LED off
  servo1.attach(servoPin);       // attaches the first servo
  Serial.begin(9600);            // init the serial port to 9600 baud, 8N1
}


void loop()
{
  if(digitalRead(programSwitch)==false)  // initial check of record playback mode switch
  {
    delay(dbDelay);                      // wait a bit and check switch again
    // read sw again, also verify data exists via tap count not = 0
    while((digitalRead(programSwitch)==false) && (TapCount > 0))
    {
      // now in playback mode
      mode = 0; // set mode = playback
      
            Serial.print("Mode 0 FSR is ");        //survey code next six lines
            Serial.println(analogRead(FSR));
            Serial.print("Mode 0 programSwitch is ");
            Serial.println(analogRead(programSwitch));
            Serial.print("Mode 0 TapCount is ");
            Serial.println(TapCount);
            delay (100);
            
      // command the servo to strike and retract using recorded interval times
      // the beat goes on forever or until commanded into record mode
      for (int i=0; i<(TapCount-1); i++)  
      {
        servo1.write(strikePos);  // command servo to strike
        delay(strikeTime);        // wait just long enough for servo to get there
        servo1.write(retractPos); // command servo back to zero position
        delay(tapReadings[i]);    // pauses between beats
        if (i == TapCount) i = 0;
      }

    }
  }
  else
  {
    // now in record mode
    mode = 1;
    digitalWrite(LEDpin, HIGH);
    Serial.println("Start tapping");
    // zero out tap count but leave other data intact until 1'st tap is detected
    TapCount = 0;
    // now run the tap detection routine over and over and oever
    while(mode == 1) //now stuck in loop below until mode changes to 0
    {
      
            Serial.print("Mode 1 FSR is ");      //survey code next six lines
            Serial.println(analogRead(FSR));
            Serial.print("Mode 1 programSwitch is ");
            Serial.println(analogRead(programSwitch));
            Serial.print("Mode 1 tapcount is ");
            Serial.println(TapCount);
            delay (100);
      
      if(TapCount <= MaxNumIntervals)
      {
        if(analogRead(FSR) > Tapthresh)
        {
          // a tap has been detected !
          digitalWrite(LEDpin, LOW);  // blink LED off to show tap detected
          if(TapCount == 0) // 1st tap detected
          {
            TapTime = millis();
            Serial.println("Detected 1'st tap");
            
            //now reset the entire possible listening array
            for (int i=0; i<MaxNumIntervals; i++)
            {
              tapReadings[i]=0;
            }
            TapCntCopy = 0;  // also reset the count copy
          }
          else
          {
            // record interval btw this tap and prior tap
            tapReadings[TapCount-1] = int(millis() - TapTime);
            TapTime = millis();
            //send interval count to PC
            Serial.print("Recorded interval #   ");
            Serial.println(TapCount);
          }
          TapCount++;   // tap count = 21 after 20 intervals
          TapCntCopy = TapCount; // make a copy for restore if needed 
          
          // now wait for tap to be released, FSR to go back to normal
          delay(minTapInterval);
          digitalWrite(LEDpin, HIGH); // turn LED back on
        }
      }
      else
      {
        //have recorded max number of intervals
        digitalWrite(LEDpin, LOW); //tell user all done recording
        Serial.println("Recorded max number of taps");
      }
      if(digitalRead(programSwitch)==false) //read switch to see if user commands playback mode
      {
        mode = 0; //go to playback mode
        digitalWrite(LEDpin, LOW); //tell user all done recording
        if(TapCount == 0) // user has decided to keep prior data
        {
          TapCount = TapCntCopy;  // restore tap count to prior value
        }
        Serial.println("Finished recording tap data");
      }
    }
  }
}

Oh yeah BTW if anybody is interested in the secret knock sketch here it is and link about it is here http://grathio.com/2009/11/secret_knock … door_lock/

/* Detects patterns of knocks and triggers a motor to unlock
   it if the pattern is correct.
   
   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.)
   
   Analog Pin 0: Piezo speaker (connected to ground with 1M pulldown resistor)
   Digital Pin 2: Switch to enter a new code.  Short this to enter programming mode.
   Digital Pin 3: DC gear reduction motor attached to the lock. (Or a motor controller or a solenoid or other unlocking mechanisim.)
   Digital Pin 4: Red LED. 
   Digital Pin 5: Green LED. 
   
   Update: Nov 09 09: Fixed red/green LED error in the comments. Code is unchanged. 
   Update: Nov 20 09: Updated handling of programming button to make it more intuitive, give better feedback.
   Update: Jan 20 10: Removed the "pinMode(knockSensor, OUTPUT);" line since it makes no sense and doesn't do anything.
 */
 
// Pin definitions
const int knockSensor = 0;         // Piezo sensor on pin 0.
const int programSwitch = 2;       // If this is high we program a new code.
const int lockMotor = 3;           // Gear motor used to turn the lock.
const int redLED = 4;              // Status LED
const int greenLED = 5;            // Status LED
 
// Tuning constants.  Could be made vars and hoooked to potentiometers for soft configuration, etc.
const int threshold = 3;           // 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.

void setup() {
  pinMode(lockMotor, OUTPUT);
  pinMode(redLED, OUTPUT);
  pinMode(greenLED, OUTPUT);
  pinMode(programSwitch, INPUT);
  
  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.
  
  digitalWrite(greenLED, HIGH);      // Green LED on, everything is go.
}

void loop() {
  // Listen for any knock at all.
  knockSensorValue = analogRead(knockSensor);
  
  if (digitalRead(programSwitch)==HIGH){  // is the program button pressed?
    programButtonPressed = true;          // Yes, so lets save that state
    digitalWrite(redLED, HIGH);           // and turn on the red light too so we know we're programming.
  } else {
    programButtonPressed = false;
    digitalWrite(redLED, LOW);
  }
  
  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(greenLED, LOW);      			// we blink the LED for a bit as a visual indicator of the knock.
  if (programButtonPressed==true){
     digitalWrite(redLED, LOW);                         // and the red one too if we're programming a new knock.
  }
  delay(knockFadeTime);                       	        // wait for this peak to fade before we listen to the next one.
  digitalWrite(greenLED, HIGH);  
  if (programButtonPressed==true){
     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.println("knock.");
      now=millis();
      knockReadings[currentKnockNumber] = now-startTime;
      currentKnockNumber ++;                             //increment the counter
      startTime=now;          
      // and reset our timer for the next knock
      digitalWrite(greenLED, LOW);  
      if (programButtonPressed==true){
        digitalWrite(redLED, LOW);                       // and the red one too if we're programming a new knock.
      }
      delay(knockFadeTime);                              // again, a little delay to let the knock decay.
      digitalWrite(greenLED, HIGH);
      if (programButtonPressed==true){
        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 (programButtonPressed==false){             // only if we're not in progrmaing mode.
    if (validateKnock() == true){
      triggerDoorUnlock(); 
    } else {
      Serial.println("Secret knock failed.");
      digitalWrite(greenLED, LOW);  		// We didn't unlock, so blink the red LED as visual feedback.
      for (i=0;i<4;i++){					
        digitalWrite(redLED, HIGH);
        delay(100);
        digitalWrite(redLED, LOW);
        delay(100);
      }
      digitalWrite(greenLED, HIGH);
    }
  } else { // if we're in programming mode we still validate the lock, we just don't do anything with the lock
    validateKnock();
    // and we blink the green and red alternately to show that program is complete.
    Serial.println("New lock stored.");
    digitalWrite(redLED, LOW);
    digitalWrite(greenLED, HIGH);
    for (i=0;i<3;i++){
      delay(100);
      digitalWrite(redLED, HIGH);
      digitalWrite(greenLED, LOW);
      delay(100);
      digitalWrite(redLED, LOW);
      digitalWrite(greenLED, HIGH);      
    }
  }
}


// Runs the motor (or whatever) to unlock the door.
void triggerDoorUnlock(){
  Serial.println("Door unlocked!");
  int i=0;
  
  // turn the motor on for a bit.
  digitalWrite(lockMotor, HIGH);
  digitalWrite(greenLED, HIGH);            // And the green LED too.
  
  delay (lockTurnTime);                    // Wait a bit.
  
  digitalWrite(lockMotor, LOW);            // Turn the motor off.
  
  // Blink the green LED a few times for more visual feedback.
  for (i=0; i < 5; i++){   
      digitalWrite(greenLED, LOW);
      delay(100);
      digitalWrite(greenLED, HIGH);
      delay(100);
  }
   
}

// Sees if our knock matches the secret.
// returns true if it's a good knock, false if it's not.
// todo: break it into smaller functions for readability.
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 we're recording a new knock, save the info and get out of here.
  if (programButtonPressed==true){
      for (i=0;i<maximumKnocks;i++){ // normalize the times
        secretCode[i]= map(knockReadings[i],0, maxKnockInterval, 0, 100); 
      }
      // And flash the lights in the recorded pattern to let us know it's been programmed.
      digitalWrite(greenLED, LOW);
      digitalWrite(redLED, LOW);
      delay(1000);
      digitalWrite(greenLED, HIGH);
      digitalWrite(redLED, HIGH);
      delay(50);
      for (i = 0; i < maximumKnocks ; i++){
        digitalWrite(greenLED, LOW);
        digitalWrite(redLED, LOW);  
        // only turn it on if there's a delay
        if (secretCode[i] > 0){                                   
          delay( map(secretCode[i],0, 100, 0, maxKnockInterval)); // Expand the time back out to what it was.  Roughly. 
          digitalWrite(greenLED, HIGH);
          digitalWrite(redLED, HIGH);
        }
        delay(50);
      }
	  return false; 	// We don't unlock the door when we are recording a new knock.
  }
  
  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;
  
}

Been busy watching MNF and my Pats destroy the Texans so I’ve not read your 2 posts above but …

I’ve been assuming the switch from record to playback to record, etc, etc would be done with a 2 position, non-momentary switch. The big idea being that once the mechanical switch was thrown into 1 position or the other, it would, eventually settle and stay in that position … allowing it’s position to be read at any point in time after the switching action took place.

If you’re using an FSR to switch btw modes (record/playback) that’s akin to a momentary push button switch. That can be done (incorporated in code) but it will require a different coding than done presently. You shouldn’t be surprised than nothing works now.

Perhaps this is a good time to define all the user interfaces to the Arduino. What switches are expected to do what. And the wiring to all the “parts”. I inferred a lot from the code you originally posted. Now it needs to be put into concrete.

FWIW I have some guess as to what you’re doing with the servo. I’m guided by an article I read somewhere (I can’t find it now, damn !) about how someone gutted a toy monkey-playing-drums and incorporated an Arduino plus servos to do the drumming. But that’s just my guess as to your intent …

Give me some time to read your posts above.

Ok I got code running out of my nose, but I restructured it a bit because as you said Mac-n-mee that you did not do Arduino coding and it didn’t seem to be reacting too well to it. It seems a bit more responsive with this code, but somehow it only seems to play the array once before it goes back into record mode.

#include <Servo.h>

Servo servo1;                    // create first servo object to control first servo

// declare the constants
const int FSR = 0;               // FSR on pin 0
const int programSwitch = 2;     // Record playback switch on pin 2
const int servoPin = 6;          // servo is on pin 6
const int LEDpin = 13;           // use onboard LED on pin 13
const int MaxNumIntervals = 20;  // the max number of intervals to be recorded
const int Tapthresh = 1;       // threshold for FSR tap detection
const int strikePos = 170;       // servo command for strike position
const int retractPos = 0;        // servo command for retract position
const unsigned long strikeTime = 100;      // time, in msec, btw the servo striking and retracting
const unsigned long dbDelay = 150;         // debounce wait time in msecs
const unsigned long minTapInterval = 100;  // min time between taps in msecs

// declare the variables
int TapCount = 0;                // Incrementer for the array
int TapCntCopy = 0;              // Copy of tap count
int mode = 0;                    // mode is record (1) or playback (0)
unsigned long TapTime=millis();  // Reference for when this tap started.
unsigned long tapReadings[MaxNumIntervals]; // set aside memory for interval times

void setup()
{

  pinMode(programSwitch, INPUT); // define record playback pin as an input
  pinMode(LEDpin, OUTPUT);       // define LED pin as an output
  digitalWrite(LEDpin, LOW);     // set the LED off
  servo1.attach(servoPin);       // attaches the first servo
  Serial.begin(9600);            // init the serial port to 9600 baud, 8N1
}


void loop()

{

  while((digitalRead(programSwitch)==false) && (TapCount = 0))
  
  {
    delay(dbDelay);                      // wait a bit and check switch again
    // read sw again, also verify data exists via tap count not = 0
  } 
    
  while(digitalRead(programSwitch)==true) 
  
  {
    // now in record mode
    digitalWrite(LEDpin, HIGH);
    Serial.println("Start tapping");
    // zero out tap count but leave other data intact until 1'st tap is detected
    TapCount = 0;
    // now run the tap detection routine over and over and oever
    
    while(digitalRead(programSwitch)==true) //now stuck in loop below until mode changes to 0
    
    {     
            Serial.print("Record mode FSR is ");      //survey code next six lines
            Serial.println(analogRead(FSR));
            Serial.print("Record mode programSwitch is ");
            Serial.println(analogRead(programSwitch));
            Serial.print("Record mode tapcount is ");
            Serial.println(TapCount);
            delay (500);
      
      if(TapCount <= MaxNumIntervals)
      {
        if(analogRead(FSR) > Tapthresh)
        {
          // a tap has been detected !
          digitalWrite(LEDpin, LOW);  // blink LED off to show tap detected
          if(TapCount == 0) // 1st tap detected
          {
            TapTime = millis();
            Serial.println("Detected 1'st tap");
            
            //now reset the entire possible listening array
            for (int i=0; i<MaxNumIntervals; i++)
            {
              tapReadings[i]=0;
            }
            TapCntCopy = 0;  // also reset the count copy
          }
          else
          {
            // record interval btw this tap and prior tap
            tapReadings[TapCount-1] = int(millis() - TapTime);
            TapTime = millis();
            //send interval count to PC
            Serial.print("Recorded interval #   ");
            Serial.println(TapCount);
          }
          TapCount++;   // tap count = 21 after 20 intervals
          TapCntCopy = TapCount; // make a copy for restore if needed 
          
          // now wait for tap to be released, FSR to go back to normal
          delay(minTapInterval);
          digitalWrite(LEDpin, HIGH); // turn LED back on
        }
      }
      else
      {
        //have recorded max number of intervals
        digitalWrite(LEDpin, LOW); //tell user all done recording
        Serial.println("Recorded max number of taps");
      }
      
      
      if(digitalRead(programSwitch)==false) //read switch to see if user commands playback mode
      {
        goto Playback;  
        digitalWrite(LEDpin, LOW); //tell user all done recording
        if(TapCount == 0) // user has decided to keep prior data
        {
          TapCount = TapCntCopy;  // restore tap count to prior value
        }
        Serial.println("Finished recording tap data");
      }
    }
  }
  
  Playback:
    while((digitalRead(programSwitch)==false) && (TapCount > 0))

    {
            Serial.print("playback FSR is ");        //survey code next six lines
            Serial.println(analogRead(FSR));
            Serial.print("playback programSwitch is ");
            Serial.println(analogRead(programSwitch));
            Serial.print("playback TapCount is ");
            Serial.println(TapCount);
            delay (500);
            
      // command the servo to strike and retract using recorded interval times
      // the beat goes on forever or until commanded into record mode
      for (int i=0; i<(TapCount-1); i++)  
      {
        servo1.write(strikePos);  // command servo to strike
        delay(strikeTime);        // wait just long enough for servo to get there
        servo1.write(retractPos); // command servo back to zero position
        delay(tapReadings[i]);    // pauses between beats
        if (i == TapCount) i = 0;
      }

    }
  }

shakamuni01:
Well did got my gadget built and tried it out. I just used 2 FSR’s one for the program switch and one for the tap sensor.

I looked at your code and I don't see how you intend to use an FSR for the program switch. I also don't know why you put a goto where you did, it would seem to bypass some code needed to keep things working. But let's start with using an FSR as a switch. How do you envision this working ? How do you have it wired up ? How does the code "know" when someone is pressing on the program switch FSR ... or not ? I ask because this (below) is clearly wrong (for reading the FSR).
digitalRead(programSwitch)

From the guide …

http://www.sparkfun.com/datasheets/Sens … rguide.pdf

I noticed that Figure 9 shows the typical configuration used to “read” the FSR. What’s interesting about it is the steepness of the curve(s), from no force to just a little force. I don’t have a gut feel for how much force is applied when you tap with your finger but if it’s enough to run from 0V to > 3.5V you might be able to get away with using a digital input to detect a tap rather than having to read an analog voltage and apply some threshold test. This would solve a problem in trying to use an FSR as the “program switch” (record/playback mode switch). I won’t go into that now but if a tap can be read via a digital pin then you can use the external interrupt pins (2, 3). That would be a very simple thing to do !

(click on to enlarge)

So what I now really really recommend is to capture the taps via an analog read and spit the data out over the serial line to the serial monitor, where the data can be captured and stored. Just yesterday another poster had a problem trying to do something very similar (capture A/D samples, send to serial port) and so here’s some code to read the FSR voltage divider and spit it out.

// declare constants
const unsigned long s_period = 2000; // this is the sample period in usec
int analogPin = 0;

// declare variables
unsigned long t_now = 0;    // the latest time from micros call
unsigned long t_prior = 0;  // the last time a sample was taken
int val = 0;                // A2D reading

void setup(){
  Serial.begin(38400);      // vary the baud rate
  t_prior = micros();       // init time of last sample
}

void loop(){
  t_now = micros();                  // poll the time at the top of the loop
  // get a sample when a sample period has passed since last sample
  if(t_now - t_prior >= s_period){
    t_prior = t_now;                 // remember time
    val = analogRead(analogPin);     // sample
    Serial.println(val);             // output data
  }
}

Start with a 100k resistor (for Rm above) as shown in Fig9 (ignore the op-amp, that’s already “inside” your Arduino), connect to A0 pin and see what you get for voltages vs tap strength. You can also try with resistors in the 20k to 50k range and see what you get. Those values will better mimic what you’d see using an internal pullup on a digital input.

Just for giggles you might try this sketch. Connect your FSRs to btw pins 2 and ground and 3 and ground. Run the code and open the serial monitor window, set baud to 38.4k. Then tap away. Every 5 secs a “running” message should show up just so you know it’s running. If a tap can be detected using the internal pullups you’ll see a time printout along with an “A” or “B” depending on which FSR you whacked.

// set pin numbers
const int FSRAPin = 2;                   // the number of the A targets input pin
const int FSRBPin = 3;                   // the number of the B targets input pin

// initialize the constants
const unsigned long s_period = 5000;     // loop time in msec

// initialize global variables
unsigned long t_now = 0;                 // variable to hold the start time
unsigned long t_prior = 0;               // variable to hold the buzzer on time


void setup(){
  // initialize the input pins with internal pullups
  pinMode(FSRAPin, INPUT_PULLUP);
  pinMode(FSRBPin, INPUT_PULLUP);
  Serial.begin(38400);                    // set baud rate to 38.4k
  // setup ext pins as interrupts
  attachInterrupt(0, ISR_A, FALLING);     // detect leading, falling edge of tap
  attachInterrupt(1, ISR_B, FALLING);     // detect leading, falling edge of tap
} 

void loop(){
  t_now = millis();                        // poll the time at the top of the loop
  if(t_now - t_prior >= s_period){
    t_prior = t_now;                       // remember time
    Serial.println("Running");             // send the I'm alive message
  }
}

void ISR_A(){
  Serial.print("A ");
  Serial.println(millis());
}

void ISR_B(){
  Serial.print("B ");
  Serial.println(millis());
}

Now if that doesn’t work and you have some 10k to 20k resistors lying about then you can give this sketch a try. You’ll have to connect the FSRs and these external pull-downs as shown in Fig 9. That is the FSR between 5V and the pin (2 or 3) and the resistor between the pin and ground. This will make a rising voltage when tapped and that’s a little easier for the digital pin to “detect”.

// set pin numbers
const int FSRAPin = 2;                   // the number of the A targets input pin
const int FSRBPin = 3;                   // the number of the B targets input pin

// initialize the constants
const unsigned long s_period = 5000;     // loop time in msec

// initialize global variables
unsigned long t_now = 0;                 // variable to hold the start time
unsigned long t_prior = 0;               // variable to hold the buzzer on time


void setup(){
  // initialize the input pins as inputs
  pinMode(FSRAPin, INPUT);
  pinMode(FSRBPin, INPUT);
  Serial.begin(38400);                    // set baud rate to 38.4k
  // setup ext pins as interrupts
  attachInterrupt(0, ISR_A, RISING);      // detect leading, rising edge of tap
  attachInterrupt(1, ISR_B, RISING);      // detect leading, rising edge of tap
} 

void loop(){
  t_now = millis();                        // poll the time at the top of the loop
  if(t_now - t_prior >= s_period){
    t_prior = t_now;                       // remember time
    Serial.println("Running");             // send the I'm alive message
  }
}

void ISR_A(){
  Serial.print("A ");
  Serial.println(millis());
}

void ISR_B(){
  Serial.print("B ");
  Serial.println(millis());
}

Yeah, sorry about that I was just using the FSR as a switch because I didn’t have any other switches around. But no I would not use the switch as a programming switch ultimately. For now I just constantly hold it down when I am tapping a pattern.

Mee_n_Mac:

shakamuni01:
Well did got my gadget built and tried it out. I just used 2 FSR’s one for the program switch and one for the tap sensor.

I looked at your code and I don't see how you intend to use an FSR for the program switch. I also don't know why you put a goto where you did, it would seem to bypass some code needed to keep things working. But let's start with using an FSR as a switch. How do you envision this working ? How do you have it wired up ? How does the code "know" when someone is pressing on the program switch FSR ... or not ? I ask because this (below) is clearly wrong (for reading the FSR).
digitalRead(programSwitch)

I will give your fsr test a try. I am assuming by what you are saying the digital reading is a lot faster performed than the analog reading. Again I do intend to make the program switch something mechanical and not an fsr.

shakamuni01:
I will give your fsr test a try. I am assuming by what you are saying the digital reading is a lot faster performed than the analog reading. Again I do intend to make the program switch something mechanical and not an fsr.

A reading of a digital pin does take less time that doing an analog read and threshold test but that’s not my reason for the tests I recommended. I was trying to follow up on what I thought was your idea to use the FSR as some sort of momentary switch. That idea might work, might not. The only way to tell is to do those experiments. I would but I don’t have an FSR.

If you have no intention of using an FSR in that way, and you’re not interested in the result, then don’t. The code is much easier when you use a non-momentary switch to determine playback mode vs record mode.

I’ve yet to look at the tap detect code, probably won’t get a chance until after Xmas.

EDIT : Hmmm, I thought I saw a post with some other “tap detect” code (to unlock a door). Now it’s gone. I musta been drinking too much watching today’s game. :o :mrgreen:

That was I think this post MacnMee. I did post something about perhaps canbolizing this one but after tinkering with it for a couple more hours I felt it would just confuse matters. Currently playing with the code, really just learning about. Trying to make a no frills version of it just to see it work in a way I can understand it and then expand into something I ultimately want. Have a nice X mas buddy. Will keep you posted if I generate anything viable.

shakamuni01:
Oh yeah BTW if anybody is interested in the secret knock sketch here it is and link about it is here http://grathio.com/2009/11/secret_knock … door_lock/

/* Detects patterns of knocks and triggers a motor to unlock

it if the pattern is correct.

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.)

Analog Pin 0: Piezo speaker (connected to ground with 1M pulldown resistor)
Digital Pin 2: Switch to enter a new code. Short this to enter programming mode.
Digital Pin 3: DC gear reduction motor attached to the lock. (Or a motor controller or a solenoid or other unlocking mechanisim.)
Digital Pin 4: Red LED.
Digital Pin 5: Green LED.

Update: Nov 09 09: Fixed red/green LED error in the comments. Code is unchanged.
Update: Nov 20 09: Updated handling of programming button to make it more intuitive, give better feedback.
Update: Jan 20 10: Removed the “pinMode(knockSensor, OUTPUT);” line since it makes no sense and doesn’t do anything.
*/

// Pin definitions
const int knockSensor = 0; // Piezo sensor on pin 0.
const int programSwitch = 2; // If this is high we program a new code.
const int lockMotor = 3; // Gear motor used to turn the lock.
const int redLED = 4; // Status LED
const int greenLED = 5; // Status LED

// Tuning constants. Could be made vars and hoooked to potentiometers for soft configuration, etc.
const int threshold = 3; // 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.

void setup() {
pinMode(lockMotor, OUTPUT);
pinMode(redLED, OUTPUT);
pinMode(greenLED, OUTPUT);
pinMode(programSwitch, INPUT);

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.

digitalWrite(greenLED, HIGH); // Green LED on, everything is go.
}

void loop() {
// Listen for any knock at all.
knockSensorValue = analogRead(knockSensor);

if (digitalRead(programSwitch)==HIGH){ // is the program button pressed?
programButtonPressed = true; // Yes, so lets save that state
digitalWrite(redLED, HIGH); // and turn on the red light too so we know we’re programming.
} else {
programButtonPressed = false;
digitalWrite(redLED, LOW);
}

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(greenLED, LOW); // we blink the LED for a bit as a visual indicator of the knock.
if (programButtonPressed==true){
digitalWrite(redLED, LOW); // and the red one too if we’re programming a new knock.
}
delay(knockFadeTime); // wait for this peak to fade before we listen to the next one.
digitalWrite(greenLED, HIGH);
if (programButtonPressed==true){
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.println(“knock.”);
now=millis();
knockReadings[currentKnockNumber] = now-startTime;
currentKnockNumber ++; //increment the counter
startTime=now;
// and reset our timer for the next knock
digitalWrite(greenLED, LOW);
if (programButtonPressed==true){
digitalWrite(redLED, LOW); // and the red one too if we’re programming a new knock.
}
delay(knockFadeTime); // again, a little delay to let the knock decay.
digitalWrite(greenLED, HIGH);
if (programButtonPressed==true){
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 (programButtonPressed==false){ // only if we’re not in progrmaing mode.
if (validateKnock() == true){
triggerDoorUnlock();
} else {
Serial.println(“Secret knock failed.”);
digitalWrite(greenLED, LOW); // We didn’t unlock, so blink the red LED as visual feedback.
for (i=0;i<4;i++){
digitalWrite(redLED, HIGH);
delay(100);
digitalWrite(redLED, LOW);
delay(100);
}
digitalWrite(greenLED, HIGH);
}
} else { // if we’re in programming mode we still validate the lock, we just don’t do anything with the lock
validateKnock();
// and we blink the green and red alternately to show that program is complete.
Serial.println(“New lock stored.”);
digitalWrite(redLED, LOW);
digitalWrite(greenLED, HIGH);
for (i=0;i<3;i++){
delay(100);
digitalWrite(redLED, HIGH);
digitalWrite(greenLED, LOW);
delay(100);
digitalWrite(redLED, LOW);
digitalWrite(greenLED, HIGH);
}
}
}

// Runs the motor (or whatever) to unlock the door.
void triggerDoorUnlock(){
Serial.println(“Door unlocked!”);
int i=0;

// turn the motor on for a bit.
digitalWrite(lockMotor, HIGH);
digitalWrite(greenLED, HIGH); // And the green LED too.

delay (lockTurnTime); // Wait a bit.

digitalWrite(lockMotor, LOW); // Turn the motor off.

// Blink the green LED a few times for more visual feedback.
for (i=0; i < 5; i++){
digitalWrite(greenLED, LOW);
delay(100);
digitalWrite(greenLED, HIGH);
delay(100);
}

}

// Sees if our knock matches the secret.
// returns true if it’s a good knock, false if it’s not.
// todo: break it into smaller functions for readability.
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 we’re recording a new knock, save the info and get out of here.
if (programButtonPressed==true){
for (i=0;i<maximumKnocks;i++){ // normalize the times
secretCode[i]= map(knockReadings[i],0, maxKnockInterval, 0, 100);
}
// And flash the lights in the recorded pattern to let us know it’s been programmed.
digitalWrite(greenLED, LOW);
digitalWrite(redLED, LOW);
delay(1000);
digitalWrite(greenLED, HIGH);
digitalWrite(redLED, HIGH);
delay(50);
for (i = 0; i < maximumKnocks ; i++){
digitalWrite(greenLED, LOW);
digitalWrite(redLED, LOW);
// only turn it on if there’s a delay
if (secretCode[i] > 0){
delay( map(secretCode[i],0, 100, 0, maxKnockInterval)); // Expand the time back out to what it was. Roughly.
digitalWrite(greenLED, HIGH);
digitalWrite(redLED, HIGH);
}
delay(50);
}
return false; // We don’t unlock the door when we are recording a new knock.
}

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;

}

Actually I do have a general question about the code up above. He has a loop section and then he has two sections which are

void listenToSecretKnock()

validateKnock();

void triggerDoorUnlock()

Somehow without using the goto function the program jumps to these sections. How does this work. Does it jump to this section, execute whatever and then jump back to the loop? Also are the () after the label necessary?

Well I am at a true loss here. I have been screwing around with the code quite a bit hoping to make some sense of it but just getting illogical statements coming out of it. I tried to simplify the code to the basic premisis that when the program button is not pushed all it does is try to replay the array. When it is pressed it only stays in record mode as long at it is pressed.

Here is the code

#include <Servo.h>

Servo servo1;                    // create first servo object to control first servo

// declare the constants
const int FSR = 0;                         // FSR on pin 0
const int programSwitch = 2;               // Record playback switch on pin 2
const int servoPin = 6;                    // servo is on pin 6
const int LEDpin = 13;                     // use onboard LED on pin 13
const int MaxNumIntervals = 20;            // the max number of intervals to be recorded
const int Tapthresh = 5;                  // threshold for FSR tap detection
const int strikePos = 170;                 // servo command for strike position
const int retractPos = 0;                  // servo command for retract position
const unsigned long strikeTime = 100;      // time, in msec, btw the servo striking and retracting
const unsigned long dbDelay = 150;         // debounce wait time in msecs
const unsigned long minTapInterval = 100;  // min time between taps in msecs

// declare the variables
int TapCount = 0;                           // Incrementer for the array
int TapCntCopy = 0;                         // Copy of tap count
int mode = 0;                               // mode is record (1) or playback (0)
unsigned long TapTime=millis();             // Reference for when this tap started.
unsigned long tapReadings[MaxNumIntervals]; // set aside memory for interval times

void setup()
{
  pinMode(programSwitch, INPUT); // define record playback pin as an input
  pinMode(LEDpin, OUTPUT);       // define LED pin as an output
  digitalWrite(LEDpin, LOW);     // set the LED off
  servo1.attach(servoPin);       // attaches the first servo
  Serial.begin(9600);            // init the serial port to 9600 baud, 8N1
}

void loop()
{

  while(digitalRead(programSwitch)==false)  // initial check of record playback mode switch
  { 
   for (int i=0; i<(TapCount-1); i++)
      {
        servo1.write(strikePos);  // command servo to strike
        delay(strikeTime);        // wait just long enough for servo to get there
        servo1.write(retractPos); // command servo back to zero position
        delay(tapReadings[i]);    // pauses between beats
        if(digitalRead(programSwitch)==true)                  //  This will break it out of the repeat play cycle with the 'for' function    
        {goto Mode1;}  
        if (i>=TapCount)                                      // this allows the array to play itself through again and again when it get to the end.
           {i=0;}
        
        
        Serial.print("loop FSR is ");        //survey code next six lines which will ultimately be deleted
        Serial.println(analogRead(FSR));
        Serial.print("loop programSwitch is ");
        Serial.println(analogRead(programSwitch));
        Serial.print("loop TapCount is ");
        Serial.println(TapCount);
        delay (100);      

       
      }
    
   }

    
    // now run the tap detection routine over and over and oever
    
    Mode1:
    while((digitalRead(programSwitch)==true))
    
    {
      
    digitalWrite(LEDpin, HIGH);
    Serial.println("Start tapping");
    
    TapCount=0;        //Resets the count of taps for a new combo
      
       Serial.print("Mode 1 FSR is ");                     //survey code next six lines to be deleted
            Serial.println(analogRead(FSR));
            Serial.print("Mode 1 programSwitch is ");
            Serial.println(analogRead(programSwitch));
            Serial.print("Mode 1 tapcount is ");
            Serial.println(TapCount);
            delay (100);
      
      
       if(digitalRead(programSwitch)==false) //read switch to see if user commands playback mode
      {    
        Serial.println("Finished recording tap data");
      }
      
      
      if(TapCount <= MaxNumIntervals)             
      {
        if(analogRead(FSR) > Tapthresh)            // 1st tap has been detected !
        {
          digitalWrite(LEDpin, LOW);               // blink LED off to show tap detected
          TapCount=TapCount++;                     // This makes the value of Tapcount equal 1
          if(TapCount == 1)                        // 1st tap detected
          {
            TapTime = millis();
            Serial.println("Detected 1'st tap");
            
            //Now reset the entire possible listening array
            for (int i=0; i<MaxNumIntervals; i++)
            {
              tapReadings[i]=0;
            }
          }
          else                                 // this else refers to is tap count is more than 0 which would be past the first tap.  
                                               //This is so the program doesn't count the interval time before and taps have started.
          {
            // record interval btw this tap and prior tap and the active recording starts here.
            tapReadings[TapCount-1] = int(millis() - TapTime);
            TapTime = millis();          //Shifts the timing for the last tap made
            //send interval count to PC
            Serial.print("Recorded interval #   ");
            Serial.println(TapCount);
          }
          TapCount++;   // tap count = 21 after 20 intervals.  Due to an interval being the space between two points of tap.
          
          // now wait for tap to be released, FSR to go back to normal
          delay(minTapInterval);
          digitalWrite(LEDpin, HIGH);                   // turn LED back on
        }
      }
      else                                             //This 'else' refers to it being beyond the max intervals.
      {
        //have recorded max number of intervals
        digitalWrite(LEDpin, LOW);                     //tell user all done recording
        Serial.println("Recorded max number of taps");

      }
    }
  }

So I run the monitor and the following report happens. In thoery if the program button is 0 it should not ever be in

Mode 1 but from the Monitor printout(ctrl-shift-M) it is.

Start tapping

Mode 1 FSR is 0

Mode 1 programSwitch is 0

Mode 1 tapcount is 0

Finished recording tap data

Start tapping

Mode 1 FSR is 0

Mode 1 programSwitch is 0

Mode 1 tapcount is 0

Any ideas.

Losing faith :-(.

Well this code here version 7 seems more responsive but a long way to go. I gotta figure out how to reset the tap count when it goes from playback mode into record mode each time. I also changed the parameters from true and false to Low and high. Somehow it seemed to respond better to that. Don’t know why.

#include <Servo.h>

Servo servo1;                    // create first servo object to control first servo

// declare the constants
const int FSR = 0;                         // FSR on pin 0
const int programSwitch = 2;               // Record playback switch on pin 2
const int servoPin = 6;                    // servo is on pin 6
const int LEDpin = 13;                     // use onboard LED on pin 13
const int MaxNumIntervals = 20;            // the max number of intervals to be recorded
const int Tapthresh = 5;                  // threshold for FSR tap detection
const int strikePos = 170;                 // servo command for strike position
const int retractPos = 0;                  // servo command for retract position
const unsigned long strikeTime = 250;      // time, in msec, btw the servo striking and retracting
const unsigned long minTapInterval = 100;  // min time between taps in msecs

// declare the variables
int TapCount = 0;                           // Incrementer for the array
int TapCntCopy = 0;                         // Copy of tap count
int mode = 0;                               // mode is record (1) or playback (0)
unsigned long TapTime=millis();             // Reference for when this tap started.
unsigned long tapReadings[MaxNumIntervals]; // set aside memory for interval times

void setup()
{
  pinMode(programSwitch, INPUT); // define record playback pin as an input
  pinMode(LEDpin, OUTPUT);       // define LED pin as an output
  digitalWrite(LEDpin, LOW);     // set the LED off
  servo1.attach(servoPin);       // attaches the first servo
  Serial.begin(9600);            // init the serial port to 9600 baud, 8N1
}


void loop()

{
  while(digitalRead(programSwitch)==LOW)  // initial check of record playback mode switch
  {
   for (int i=0; i<=(TapCount-1); i++)
      {
        servo1.write(strikePos);  // command servo to strike
        delay(strikeTime);        // wait just long enough for servo to get there
        servo1.write(retractPos); // command servo back to zero position
        delay(tapReadings[i]);    // pauses between beats
        
        if (i>=TapCount)          // this allows the array to play itself through again and again when it get to the end.
              {i=0;}
        if(digitalRead(programSwitch)==HIGH)    //This function will pull it out of the rythm playing loop
              {    
              goto Recordmode;                    // jump out of for loop to Recordmode  
              TapCount=0;                         //Tapcount gets reset to 0 as it enters record mode.
              }       
            Serial.print("loop FSR is ");       //survey code next six lines
            Serial.println(analogRead(FSR));
            Serial.print("loop programSwitch is ");
            Serial.println(analogRead(programSwitch));
            Serial.print("loop TapCount is ");
            Serial.println(TapCount);
            Serial.print("loop i is ");
            Serial.println(i);
            delay (500);
        }
   }

    
    // now run the tap detection routine over and over and oever
    
    Recordmode:
    while((digitalRead(programSwitch)==HIGH))   
    {      
    digitalWrite(LEDpin, HIGH);
    Serial.println("Start tapping");
    Serial.print("RecordMode 1 FSR is ");                     //survey code next six lines 
    Serial.println(analogRead(FSR));
    Serial.print("RecordMode 1 programSwitch is ");
    Serial.println(analogRead(programSwitch));
    Serial.print("RecordMode 1 tapcount is ");
    Serial.println(TapCount);
    delay (500);
      
      
      if(digitalRead(programSwitch)==LOW) //read switch to see if user commands playback mode
        {    
        Serial.println("Finished recording tap data");
        }      
      
      if(TapCount <= MaxNumIntervals)
        {
        if(analogRead(FSR) > Tapthresh)                // a tap has been detected !
            {
             digitalWrite(LEDpin, LOW);                // blink LED off to show tap detected
             if(TapCount == 0)                         // 1st tap detected
               {
                TapTime = millis();
                Serial.println("Detected 1'st tap");            
                for (int i=0; i<MaxNumIntervals; i++) 
                {tapReadings[i]=0;}                    //Now reset the entire possible listening array
               }
          else                                         // this 'else' refers to is TapCount is more than 0 which would be anything after the first tap.  
                                                       //This is so the program doesn't count the interval time before and taps have started.
               {
                tapReadings[TapCount-1] = int(millis() - TapTime);     // record interval btw this tap and prior tap and the active recording starts here.
                TapTime = millis();
                Serial.print("Recorded interval #   ");                //send interval count to PC
                Serial.println(TapCount);
               }
             TapCount++;                            // tap count = 21 after 20 intervals.  Due to an interval being the space between two points of tap.
             delay(minTapInterval);                 // now wait for tap to be released, FSR to go back to normal
             digitalWrite(LEDpin, HIGH);            // turn LED back on
            }
         }
      else                                          //This 'else' refers to it being beyond the max intervals.
      {
        digitalWrite(LEDpin, LOW);                  //tell user all done recording
        Serial.println("Recorded max number of taps");
      }
    }
  }

shakamuni01:
Actually I do have a general question about the code up above. He has a loop section and then he has two sections which are

void listenToSecretKnock()

validateKnock();

void triggerDoorUnlock()

Somehow without using the goto function the program jumps to these sections. How does this work. Does it jump to this section, execute whatever and then jump back to the loop? Also are the () after the label necessary?

Those are separate functions. They are little programs of their own that get executed whenever they are called. The are called by putting their name in the main loop (or anywhere else). Look at the main loop you posted.

void loop() {
    knockSensorValue = analogRead(knockSensor);
 
  if (digitalRead(programSwitch)==HIGH){  
    programButtonPressed = true;         
    digitalWrite(redLED, HIGH);           
  } else {
    programButtonPressed = false;
    digitalWrite(redLED, LOW);
  }
 
  if (knockSensorValue >=threshold){
    listenToSecretKnock();             // this is where the call happens
  }
}

I’ve stripped out all the comments except for mine to show where the listenToSecretKnock() function is called. You should read this.

http://arduino.cc/en/Reference/FunctionDeclaration

http://arduino.cc/en/Tutorial/Foundations