Fun application for FRDM-KL25Z. Uses the on-board accelerometer's built-in tap detection to detect a sequence of knocks on the table the board is sitting on. If the knock sequence matches the secret knock, it makes it happy (blinks green). Otherwise it gets angry (blinks red).

Dependencies:   MMA8451Q_ext mbed

Fun application for FRDM-KL25Z. Uses the on-board accelerometer's built-in tap detection to detect a sequence of knocks on the table the board is sitting on. If the knock sequence matches the secret knock, it makes it happy (blinks green). Otherwise it gets angry (blinks red).

Right now it doesn't support recording new secret knocks. It only knows, "shave and a hair cut, two bits". There is commented-out code in there to do the recording, but it hasn't been enabled or tested yet.

main.cpp

Committer:
maclobdell
Date:
2013-03-15
Revision:
0:ac4e452b3199

File content as of revision 0:ac4e452b3199:

#include "mbed.h"
#include "MMA8451Q.h"

#define MMA8451_I2C_ADDRESS (0x1d<<1)

/* Secret Knock Demo for FRDM-KL25Z by Mac Lobdell */
/* Several bits and pieces taken from code by Steve Hoefer (http://grathio.com) under a creative commons share-alike license */
/* Revison 1.0  
/* Programming not supported yet - only knows "shave and a hair cut, two bits" (or another pre-set sequence if set in secretCode variable initialization) */
 
void INT2ISR(void);
void INT1ISR(void);
void d2ISR(void);
int validateKnock(void);
void triggerSuccessfulAction(void);
void triggerFailedAction(void);
int map(int x, int in_min, int in_max, int out_min, int out_max);

Serial pc(USBTX,USBRX);
MMA8451Q acc(PTE25, PTE24, MMA8451_I2C_ADDRESS);
DigitalOut rled(LED_RED);
DigitalOut gled(LED_GREEN);
DigitalOut bled(LED_BLUE);
     
DigitalOut d2(PTD4);
Timer timer;

const int maximumKnocks = 8;       // Maximum number of knocks to listen for.
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." 100=full note, 50=half note, 25=quarter note, etc.
int secretKnockMax = 6; 
const int rejectValue = 30;        // If an individual knock is off by this percentage of a knock we ignore. (30 is pretty lose. 10 is strict)
const int averageRejectValue = 20; // If the average timing of the knocks is off by this percent we ignore. (20 is pretty lose, 10 is strict.)
int knockTime[maximumKnocks];    // When someone knocks this array fills with delays between knocks. (A correct knock looks a lot like the line above).
int nKnockTime[maximumKnocks]; 
int knocking = 0;
int counter;
int knockCount = 0;  //starts at 0 for first knock
int i = 0;
int programButtonPressed = false;
int startTime;
int dummyKnock = 1;
int unlocked = 0;

int main(void) {

    DigitalIn int1(PTA14);  //data ready interrupt
    int1.mode(PullUp);
    DigitalIn int2(PTA15);  //tap or Portrat/Landscape interrupt
    int2.mode(PullUp);

     /* initialize all variables */   
   knocking = 0;
   counter = 0;
   knockCount = 0;
   startTime = 0;
   dummyKnock = 1;
   unlocked = 0;
    for(i = 0; i<maximumKnocks; i++)
    {
         knockTime[i] = 0;  
         nKnockTime[i] = 0;
    }
    rled = 1; //off
    gled = 0; //on  
    bled = 1; //off
         
    //To Do - need to check if programming
    //capacitive touch slider swipe?     
  if (programButtonPressed==true){  
    for (i=0;i<3;i++){
      wait_ms(100);
      gled = 0;
      bled = 0;
      wait_ms(100);
      gled = 1;
      bled = 1;      
    }
  }

   //enable interrupts
    InterruptIn int1i(PTA14);
    int1i.fall(&INT1ISR);
    InterruptIn int2i(PTA15);
    int2i.fall(&INT2ISR);

    while(1)
    {
         //just hang out and wait for interrupts
       if(knocking == 1)
       {
        counter++;    //increment here, it will start over to 0 if knock happens in ISR
        bled = 0;  //blue on
        gled = 1;  //green off
        wait_ms(10);  //wait 10 ms
        if ((counter > 1000) || ((knockCount) >= secretKnockMax ))  //timeout after 100*10ms = 1 sec, or if max knocks ocurred
         {
           timer.stop();
 
            //report what happened
            if(counter > 100) 
            {
              pc.printf("timout\n\r");
            }else
            {
              pc.printf("max knocks\n\r");
            }
            
        /* check if secret code */
            if (validateKnock() == true){      
                triggerSuccessfulAction(); 
                unlocked = 1;
            } 
            else {
                triggerFailedAction(); 
                unlocked = 0;
            }

        /* re-initialize all variables */
           counter = 0;
           knocking = 0;
           knockCount = 0;
           unlocked = 0;          
           for(i = 0; i<maximumKnocks; i++)
           {
             knockTime[i] = 0;  
           }
          rled = 1; //off
          gled = 0; //on  
          bled = 1; //off


         }
   
       }             
    }    
    
    
}
void d2ISR(void)
{
   pc.printf("d2 ISR\n\r");
   
}
void INT1ISR(void)
{
  /*Acceleration data Ready ISR */
  
     //   rled = 1.0 - abs(acc.getAccX());
     //   gled = 1.0 - abs(acc.getAccY());
     //   bled = 1.0 - abs(acc.getAccZ());           
}

void INT2ISR(void)
{
  /* tap occurred - make sure only single taps in one direction are enabled */
  
  uint8_t dummy;
 
  dummy = acc.tapSource();  //seems like you need to read the source to clear the interrupt
  
//  pc.printf("tap isr\n\r");

 //if(dummyKnock == 1)
 //{
 //  //always get a bad knock on reset
 //   dummyKnock = 0;
 //   pc.printf("dummy knock \n");
 //}else
 {

    
  //  pc.printf("Tap \n");            
    counter = 0;  //reset the timeout counter

  if(knocking ==0)
  {
    //this is the first knock
    pc.printf("first knock\n\r");   
    knockCount = 0;
    knocking = 1;
    pc.printf("start timer\n\r");   
    timer.start();
    timer.reset();
    startTime = timer.read_ms(); 
       
  }else if(knocking == 1)
  {
    //this is not the first knock
    pc.printf("not first knock\n\r");
    int now;
    now = timer.read_ms();
    pc.printf("startTime = %d\n\r",startTime);
    pc.printf("now = %d\n\r",now);
    knockTime[knockCount] = (now - startTime);  
    pc.printf("knockTime[knockCount] : %d\n\r",knockTime[knockCount]);
    
    knockCount++;
    pc.printf("knockCount : %d\n\r",knockCount);

    startTime = now;  //start time for next knock period
    

    
  }    
  
 }

}


// We got a good knock, so do something!
void triggerSuccessfulAction(){
  pc.printf("Success!\n\r");

  rled = 1;  //off  
  gled = 0;  //on
  bled = 1;  //off 

  for (int i=0;i<16;i++){                    
    gled = 0;
    wait_ms(100);
    gled = 1;
    wait_ms(100);
  }

}

// We didn't like the knock.  Indicate displeasure.
void triggerFailedAction(){
  pc.printf("Secret knock failed\n");
  rled = 0;  //on  
  gled = 1;  //off
  bled = 1;  //off 
   
  for (int i=0;i<16;i++){                    
    rled = 0;
    wait_ms(100);
    rled = 1;
    wait_ms(100);
  }
}

// Checks if our knock matches the secret.
// Returns true if it's a good knock, false if it's not.
int 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.
  
  int codeFound=true;
  int totaltimeDifferences=0;
  int timeDiff=0;
  
  pc.printf("validating knock sequence \n\r");


  for (i=0;i<maximumKnocks;i++){
    if (knockTime[i] > 0){
      currentKnockCount++;
    }
    if (secretCode[i] > 0){                     
      secretKnockCount++;
    }

    if (knockTime[i] > maxKnockInterval){   // Collect normalization data while we're looping.
      maxKnockInterval = knockTime[i];
    }
  }
   pc.printf("max knock interval: \n\r",maxKnockInterval);
  
  // If we're recording a new knock, save the relevant info and get out of here.
  /*if (programButtonPressed==true){
    for (i=0;i<maximumKnocks;i++){ // Normalize the knock timing
      secretCode[i]= map(knockTime[i],0, maxKnockInterval, 0, 100);
    }
    // And flash the lights in the recorded pattern to let us know it's been programmed.
    bled = 0;
    rled = 0;
    wait_ms(750);

    //Start playing back the knocks
    bled = 1;
    rled = 1;
    wait_ms(40);
    for (i = 0; i < maximumKnocks ; i++){
      bled = 0;
      rled = 0;

      if (programButtonPressed==true){  // Only turn it on if there's a delay
        if (secretCode[i] > 0){                                   
          wait_ms(map(secretCode[i],0, 100, 0, maxKnockInterval)); // Expand the time back out to what it was.  Roughly. 
          bled = 1;
          rled = 1;  
        }
      }
      wait_ms(40);
      bled = 0;
      rled = 0;
    }
    return false;   // We don't do anything when we are recording a new knock.
  }
*/
    pc.printf("currentKnockCount: %d secretKnockCount %d\n\r",currentKnockCount,secretKnockCount);
        
  if (currentKnockCount != secretKnockCount){
    pc.printf("wrong number of knocks\n\r");
    return false;   // Return false if the number of knocks are wrong.
  }

  /*  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 work.)
   This makes it less picky, which does make it less secure but also makes it
   less of a pain to use if you're tempo is a little slow or fast. 
   */
  for (i=0;i<maximumKnocks;i++){ 
    nKnockTime[i] = 0;  //reinitialize
  }  
  for (i=0;i<maximumKnocks;i++){ // Normalize the times
    nKnockTime[i]= map(knockTime[i],0, maxKnockInterval, 0, 100);      
    pc.printf("knock time: %d, normalized knock time: %d, secret code time %d \n\r",knockTime[i], nKnockTime[i], secretCode[i]);
    timeDiff = abs(nKnockTime[i]-secretCode[i]);
    if (timeDiff > rejectValue){ // Individual value too far out of whack
      codeFound=false;
    }
    totaltimeDifferences += timeDiff;
  }
  // It can also fail if the whole thing is too inaccurate.
  if (totaltimeDifferences/secretKnockCount>averageRejectValue){
    codeFound = false;
  }

  if (codeFound==false){
    return false;
  } 
  else {
    return true;
  }

}


int map(int x, int in_min, int in_max, int out_min, int out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}