/* Ce programme détecte le rythme des cognées et active un moteur qui déverrouille la serrure si le rythme est correcte. Par Vincent Gingras Version 02 Pour votre information: Analog Pin 0 : Piezo speaker (connecté au ground avec une résistance de 1M en pull-down) Digital Pin 2: Switch pour entrer un nouveau code. Digital Pin 3: Moteur DC attaché à la serrure. Digital Pin 4: DEL rouge. Digital Pin 5: DEL verte. */ // Définitions des Pins. const int knockSensor = 0; // Capteur piezo à la pin 0. const int programSwitch = 2; // Si la valeur est à «HIGH», on programme un nouveau code. const int lockMotor = 3; // Moteur utilisé pour faire déverrouiller la serrure. const int redLED = 4; // Statut de la DEL rouge. const int greenLED = 5; // Statut de la DEL verte. // Ajustement des constantes. const int threshold = 2; // Signal minimum venant du piezo pour constater qu'il s'agit d'une cognée. const int rejectValue = 25; // Si quelqu'un cogne avec un décalage de ce pourcentage, on ne débarre pas. const int averageRejectValue = 15; // Si la moyenne de temps des cognées est décalé de ce pourcentage, on ne débarre pas. const int knockFadeTime = 150; // Le temps en millisecondes que l'on accorde à la cognée pour s'affaiblir avant d'en entendre une autre. const int lockTurnTime = 4000; // Le temps en milisecondes que l'on fait tourner le moteur pour le faire tourner d'un demi-tour. const int maxUsers = 4; // 4 Utilisateurs possibles. const int maximumKnocks = 20; // Nombre maximum de cognées. const int knockComplete = 1200; // Temps le plus long entre les cognées avant d'assumer que la séquence est terminée. // Définition des variables. int secretCode[maximumKnocks] [maxUsers] = { 50, 25, 25, 50, 100, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Setup initiale: "Shave and a Hair Cut, deux bits." int knockReadings[maximumKnocks]; // Lorsque quelqu'un cogne, ce tableau se rempli de délai entre chaque cognée. int knockSensorValue = 0; // Dernière lecture du capteur de cognée. int programButtonPressed = false; int time = 0; int userNumber = 0; // Nombre d'utilisateurs. int i = 0; int j = 0; boolean readyToRecord = false; void setup() { pinMode(lockMotor, OUTPUT); pinMode(redLED, OUTPUT); pinMode(greenLED, OUTPUT); pinMode(programSwitch, INPUT); Serial.begin(9600); // Initialise le port série à 9600 baud rate. Serial.println("Debut du programme."); // Écrit sur la ligne série que le programme débute. digitalWrite(greenLED, HIGH); // Del verte allumée, on continue... } void loop() { // Écoute pour toutes cognées. knockSensorValue = analogRead(knockSensor); if (digitalRead(programSwitch)== HIGH){ // Est-ce que le bouton programmation est enfoncée? programButtonPressed = true; // Oui, alors enregistrons cet état. userNumber = 0; unsigned long time2=millis(); // On prend le temps. delay (20); while ((millis() - time2) < 4000){ // On donne 5 secondes à l'utilisateur pour faire le bon nombres de pesées sur le bouton pour choisir l'utilisateur. if ((digitalRead(programSwitch)== HIGH) && (programButtonPressed == false)) { programButtonPressed = true; delay(20); userNumber = (userNumber+1) % maxUsers; // On ajoute 1 chaque fois que le bouton est pesée et lorsque l'utilisateur pèse plus de quatre fois, // on recommence le userNumber à 0. (la cinquième pesée sur le bouton programmation représente le chiffre 5 // et puisqu'il n'y a pas 5 usagers, on reset à 0. } if (digitalRead(programSwitch)== LOW){ programButtonPressed = false; delay(20); } } digitalWrite (redLED, LOW); for (int k =0;k<(userNumber+1);k++){ digitalWrite(greenLED, LOW); delay(100); digitalWrite(greenLED, HIGH); delay(400); } readyToRecord = true; digitalWrite(redLED, HIGH); // Allumons aussi la DEL rouge pour nous laisser savoir que l'on programme. } else { programButtonPressed = false; digitalWrite(redLED, LOW); } if (knockSensorValue >=threshold){ listenToSecretKnock(); } } // Enregistre le rythme des cognées. void listenToSecretKnock(){ Serial.println("Sequence demarree"); // Premièrement, on reset le tableau. for (i=0;i=threshold){ //Nous avons une autre cognée... //Enregistre le délai. Serial.println("Toc!"); now=millis(); knockReadings[currentKnockNumber] = now-startTime; currentKnockNumber ++; //Incrémentation du compteur. startTime=now; // reset le timer pour la prochaine cognée digitalWrite(greenLED, LOW); if (readyToRecord==true){ digitalWrite(redLED, LOW); // La DEL rouge aussi lorsque nous sommes en mode programmation. } delay(knockFadeTime); // Attendons que la cognée s'affaiblisse pour ensuite en écouter une autre. digitalWrite(greenLED, HIGH); if (readyToRecord==true){ digitalWrite(redLED, HIGH); } } now=millis(); //Sommes nous en timeout ou sommes nous a court de cognées?? } while ((now-startTime < knockComplete) && (currentKnockNumber < maximumKnocks)); //Nous avons notre séquence d'enregistré, vérifions si elle est valide. if (readyToRecord==false){ // Seulement si nous ne sommes pas en mode programmation. boolean atLeastOneCorrect = false; for (int l=0;l 0){ currentKnockCount++; } if (secretCode[i] [userToCompare] > 0){ // Il faut pré-calculer cela. secretKnockCount++; } if (knockReadings[i] > maxKnockInterval){ // Collecte les données normalisées en continue. maxKnockInterval = knockReadings[i] ; } } // Si nous enregistrons une nouvelle séquence, on sauvegarde les informations et on sors du bloc. if (readyToRecord==true){ for (i=0;i 0){ delay( map(secretCode[i] [userNumber],0, 100, 0, maxKnockInterval)); // Remet le temps à ce qu'il était. digitalWrite(greenLED, HIGH); digitalWrite(redLED, HIGH); } delay(50); } return false; // On ne déverrouille pas la porte lorsque nous programmons. } if (currentKnockCount != secretKnockCount){ return false; } /* Maintenant, nous comparons le temps relatifs entre chaque cognée et non le temps absolue entre chaque. (ie: Si le "pattern" est fait lentement ou rapidement, la séquence sera quand même bonne.) Cela rend le tout plus simple, mais moins sécuritaire. */ int totaltimeDifferences=0; int timeDiff=0; for (i=0;i rejectValue){ return false; } totaltimeDifferences += timeDiff; } // Le tout peut aussi échoué si le tout est imprécis. if (totaltimeDifferences/secretKnockCount>averageRejectValue){ return false; } return true; }