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
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 }
Generated on Thu Jul 21 2022 09:52:06 by 1.7.2