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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 #include "mbed.h"
00002 #include "MMA8451Q.h"
00003 
00004 #define MMA8451_I2C_ADDRESS (0x1d<<1)
00005 
00006 /* Secret Knock Demo for FRDM-KL25Z by Mac Lobdell */
00007 /* Several bits and pieces taken from code by Steve Hoefer (http://grathio.com) under a creative commons share-alike license */
00008 /* Revison 1.0  
00009 /* Programming not supported yet - only knows "shave and a hair cut, two bits" (or another pre-set sequence if set in secretCode variable initialization) */
00010  
00011 void INT2ISR(void);
00012 void INT1ISR(void);
00013 void d2ISR(void);
00014 int validateKnock(void);
00015 void triggerSuccessfulAction(void);
00016 void triggerFailedAction(void);
00017 int map(int x, int in_min, int in_max, int out_min, int out_max);
00018 
00019 Serial pc(USBTX,USBRX);
00020 MMA8451Q acc(PTE25, PTE24, MMA8451_I2C_ADDRESS);
00021 DigitalOut rled(LED_RED);
00022 DigitalOut gled(LED_GREEN);
00023 DigitalOut bled(LED_BLUE);
00024      
00025 DigitalOut d2(PTD4);
00026 Timer timer;
00027 
00028 const int maximumKnocks = 8;       // Maximum number of knocks to listen for.
00029 int secretCode[maximumKnocks] = {
00030   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.
00031 int secretKnockMax = 6; 
00032 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)
00033 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.)
00034 int knockTime[maximumKnocks];    // When someone knocks this array fills with delays between knocks. (A correct knock looks a lot like the line above).
00035 int nKnockTime[maximumKnocks]; 
00036 int knocking = 0;
00037 int counter;
00038 int knockCount = 0;  //starts at 0 for first knock
00039 int i = 0;
00040 int programButtonPressed = false;
00041 int startTime;
00042 int dummyKnock = 1;
00043 int unlocked = 0;
00044 
00045 int main(void) {
00046 
00047     DigitalIn int1(PTA14);  //data ready interrupt
00048     int1.mode(PullUp);
00049     DigitalIn int2(PTA15);  //tap or Portrat/Landscape interrupt
00050     int2.mode(PullUp);
00051 
00052      /* initialize all variables */   
00053    knocking = 0;
00054    counter = 0;
00055    knockCount = 0;
00056    startTime = 0;
00057    dummyKnock = 1;
00058    unlocked = 0;
00059     for(i = 0; i<maximumKnocks; i++)
00060     {
00061          knockTime[i] = 0;  
00062          nKnockTime[i] = 0;
00063     }
00064     rled = 1; //off
00065     gled = 0; //on  
00066     bled = 1; //off
00067          
00068     //To Do - need to check if programming
00069     //capacitive touch slider swipe?     
00070   if (programButtonPressed==true){  
00071     for (i=0;i<3;i++){
00072       wait_ms(100);
00073       gled = 0;
00074       bled = 0;
00075       wait_ms(100);
00076       gled = 1;
00077       bled = 1;      
00078     }
00079   }
00080 
00081    //enable interrupts
00082     InterruptIn int1i(PTA14);
00083     int1i.fall(&INT1ISR);
00084     InterruptIn int2i(PTA15);
00085     int2i.fall(&INT2ISR);
00086 
00087     while(1)
00088     {
00089          //just hang out and wait for interrupts
00090        if(knocking == 1)
00091        {
00092         counter++;    //increment here, it will start over to 0 if knock happens in ISR
00093         bled = 0;  //blue on
00094         gled = 1;  //green off
00095         wait_ms(10);  //wait 10 ms
00096         if ((counter > 1000) || ((knockCount) >= secretKnockMax ))  //timeout after 100*10ms = 1 sec, or if max knocks ocurred
00097          {
00098            timer.stop();
00099  
00100             //report what happened
00101             if(counter > 100) 
00102             {
00103               pc.printf("timout\n\r");
00104             }else
00105             {
00106               pc.printf("max knocks\n\r");
00107             }
00108             
00109         /* check if secret code */
00110             if (validateKnock() == true){      
00111                 triggerSuccessfulAction(); 
00112                 unlocked = 1;
00113             } 
00114             else {
00115                 triggerFailedAction(); 
00116                 unlocked = 0;
00117             }
00118 
00119         /* re-initialize all variables */
00120            counter = 0;
00121            knocking = 0;
00122            knockCount = 0;
00123            unlocked = 0;          
00124            for(i = 0; i<maximumKnocks; i++)
00125            {
00126              knockTime[i] = 0;  
00127            }
00128           rled = 1; //off
00129           gled = 0; //on  
00130           bled = 1; //off
00131 
00132 
00133          }
00134    
00135        }             
00136     }    
00137     
00138     
00139 }
00140 void d2ISR(void)
00141 {
00142    pc.printf("d2 ISR\n\r");
00143    
00144 }
00145 void INT1ISR(void)
00146 {
00147   /*Acceleration data Ready ISR */
00148   
00149      //   rled = 1.0 - abs(acc.getAccX());
00150      //   gled = 1.0 - abs(acc.getAccY());
00151      //   bled = 1.0 - abs(acc.getAccZ());           
00152 }
00153 
00154 void INT2ISR(void)
00155 {
00156   /* tap occurred - make sure only single taps in one direction are enabled */
00157   
00158   uint8_t dummy;
00159  
00160   dummy = acc.tapSource();  //seems like you need to read the source to clear the interrupt
00161   
00162 //  pc.printf("tap isr\n\r");
00163 
00164  //if(dummyKnock == 1)
00165  //{
00166  //  //always get a bad knock on reset
00167  //   dummyKnock = 0;
00168  //   pc.printf("dummy knock \n");
00169  //}else
00170  {
00171 
00172     
00173   //  pc.printf("Tap \n");            
00174     counter = 0;  //reset the timeout counter
00175 
00176   if(knocking ==0)
00177   {
00178     //this is the first knock
00179     pc.printf("first knock\n\r");   
00180     knockCount = 0;
00181     knocking = 1;
00182     pc.printf("start timer\n\r");   
00183     timer.start();
00184     timer.reset();
00185     startTime = timer.read_ms(); 
00186        
00187   }else if(knocking == 1)
00188   {
00189     //this is not the first knock
00190     pc.printf("not first knock\n\r");
00191     int now;
00192     now = timer.read_ms();
00193     pc.printf("startTime = %d\n\r",startTime);
00194     pc.printf("now = %d\n\r",now);
00195     knockTime[knockCount] = (now - startTime);  
00196     pc.printf("knockTime[knockCount] : %d\n\r",knockTime[knockCount]);
00197     
00198     knockCount++;
00199     pc.printf("knockCount : %d\n\r",knockCount);
00200 
00201     startTime = now;  //start time for next knock period
00202     
00203 
00204     
00205   }    
00206   
00207  }
00208 
00209 }
00210 
00211 
00212 // We got a good knock, so do something!
00213 void triggerSuccessfulAction(){
00214   pc.printf("Success!\n\r");
00215 
00216   rled = 1;  //off  
00217   gled = 0;  //on
00218   bled = 1;  //off 
00219 
00220   for (int i=0;i<16;i++){                    
00221     gled = 0;
00222     wait_ms(100);
00223     gled = 1;
00224     wait_ms(100);
00225   }
00226 
00227 }
00228 
00229 // We didn't like the knock.  Indicate displeasure.
00230 void triggerFailedAction(){
00231   pc.printf("Secret knock failed\n");
00232   rled = 0;  //on  
00233   gled = 1;  //off
00234   bled = 1;  //off 
00235    
00236   for (int i=0;i<16;i++){                    
00237     rled = 0;
00238     wait_ms(100);
00239     rled = 1;
00240     wait_ms(100);
00241   }
00242 }
00243 
00244 // Checks if our knock matches the secret.
00245 // Returns true if it's a good knock, false if it's not.
00246 int validateKnock(){
00247   int i=0;
00248   // Simplest check first: Did we get the right number of knocks?
00249   int currentKnockCount = 0;
00250   int secretKnockCount = 0;
00251   int maxKnockInterval = 0;             // We use this later to normalize the times.
00252   
00253   int codeFound=true;
00254   int totaltimeDifferences=0;
00255   int timeDiff=0;
00256   
00257   pc.printf("validating knock sequence \n\r");
00258 
00259 
00260   for (i=0;i<maximumKnocks;i++){
00261     if (knockTime[i] > 0){
00262       currentKnockCount++;
00263     }
00264     if (secretCode[i] > 0){                     
00265       secretKnockCount++;
00266     }
00267 
00268     if (knockTime[i] > maxKnockInterval){   // Collect normalization data while we're looping.
00269       maxKnockInterval = knockTime[i];
00270     }
00271   }
00272    pc.printf("max knock interval: \n\r",maxKnockInterval);
00273   
00274   // If we're recording a new knock, save the relevant info and get out of here.
00275   /*if (programButtonPressed==true){
00276     for (i=0;i<maximumKnocks;i++){ // Normalize the knock timing
00277       secretCode[i]= map(knockTime[i],0, maxKnockInterval, 0, 100);
00278     }
00279     // And flash the lights in the recorded pattern to let us know it's been programmed.
00280     bled = 0;
00281     rled = 0;
00282     wait_ms(750);
00283 
00284     //Start playing back the knocks
00285     bled = 1;
00286     rled = 1;
00287     wait_ms(40);
00288     for (i = 0; i < maximumKnocks ; i++){
00289       bled = 0;
00290       rled = 0;
00291 
00292       if (programButtonPressed==true){  // Only turn it on if there's a delay
00293         if (secretCode[i] > 0){                                   
00294           wait_ms(map(secretCode[i],0, 100, 0, maxKnockInterval)); // Expand the time back out to what it was.  Roughly. 
00295           bled = 1;
00296           rled = 1;  
00297         }
00298       }
00299       wait_ms(40);
00300       bled = 0;
00301       rled = 0;
00302     }
00303     return false;   // We don't do anything when we are recording a new knock.
00304   }
00305 */
00306     pc.printf("currentKnockCount: %d secretKnockCount %d\n\r",currentKnockCount,secretKnockCount);
00307         
00308   if (currentKnockCount != secretKnockCount){
00309     pc.printf("wrong number of knocks\n\r");
00310     return false;   // Return false if the number of knocks are wrong.
00311   }
00312 
00313   /*  Now we compare the relative intervals of our knocks, not the absolute time between them.
00314    (ie: if you do the same pattern slow or fast it should still work.)
00315    This makes it less picky, which does make it less secure but also makes it
00316    less of a pain to use if you're tempo is a little slow or fast. 
00317    */
00318   for (i=0;i<maximumKnocks;i++){ 
00319     nKnockTime[i] = 0;  //reinitialize
00320   }  
00321   for (i=0;i<maximumKnocks;i++){ // Normalize the times
00322     nKnockTime[i]= map(knockTime[i],0, maxKnockInterval, 0, 100);      
00323     pc.printf("knock time: %d, normalized knock time: %d, secret code time %d \n\r",knockTime[i], nKnockTime[i], secretCode[i]);
00324     timeDiff = abs(nKnockTime[i]-secretCode[i]);
00325     if (timeDiff > rejectValue){ // Individual value too far out of whack
00326       codeFound=false;
00327     }
00328     totaltimeDifferences += timeDiff;
00329   }
00330   // It can also fail if the whole thing is too inaccurate.
00331   if (totaltimeDifferences/secretKnockCount>averageRejectValue){
00332     codeFound = false;
00333   }
00334 
00335   if (codeFound==false){
00336     return false;
00337   } 
00338   else {
00339     return true;
00340   }
00341 
00342 }
00343 
00344 
00345 int map(int x, int in_min, int in_max, int out_min, int out_max)
00346 {
00347   return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
00348 }