Exiger une action sur la durée

Bonjour,

J'ai créé un petit mécanisme tout simple : le fait d'éclairer une photorésistance avec une lampe torche stoppe l'alimentation d'un électro-aimant et permet d'ouvrir une porte.
Voilà mon code :

const int sensorPin = A0;
int sensorValue = 0;
int unlockValue = 800;

void setup() {
  Serial.begin(9600);
  pinMode(2, OUTPUT);
  digitalWrite(2, HIGH);
}

void loop() {
  sensorValue = analogRead(sensorPin);
  Serial.print("Sensor value =");
  Serial.print(sensorValue);

  if(sensorValue > unlockValue){
    digitalWrite(2, LOW);
    Serial.print("Puzzle Solved !");
  }    
}

J'aimerais complexifier un peu la chose : il faudrait éclairer ma photo résistance pendant disons 5s avant que ma condition ne soit validée. Si elle n'est éclairée que pendant 4s, rien ne se passe, le compteur est réinitialisé et il faudra l'éclairer à nouveau pendant 5s pour pouvoir valider l'ouverture de la porte.

Je pense avoir compris que la fonction millis sera au menu... à mon grand dam !
Je n'arrive pas à comprendre comment celle-ci fonctionne. Et pas la peine de me renvoyer vers les sites habituels qui expliquent ça, je les ai tous lus plusieurs fois et je ne pipe toujours rien quant à son fonctionnement ! (à moins que vous n'ayez connaissance d'un excellent vulgarisateur qui saurait me faire comprendre...)

Pourtant, je pense avoir compris grosso modo ce qu'il faudrait faire J'avais pensé à quelque chose : quand je compare ma valeur mesurée à ma valeur cible, si c'est bon, je ne met pas encore mon pin 2 en LOW mais je déclenche un chrono. Tant que la valeur lue par la photo résistance est au dessus de la valeur cible, le chrono continue, mais si on arrête d'éclairer, il faudrait réinitialiser le chrono.
Une fois que c'est fait, je n'ai plus qu'a créer une condition if qui compare mon chrono à un chrono cible et qui, s'il le dépasse, active mon Pin 2.

Ou alors, si je galère trop avec millis j'ai peut-etre une solution plus simple (mais moins propre).
Je compare le sensorValue avec le unlockValue, si c'est bon, je déclenche un delay(5000), à la fin du delai, je redemande une comparaison de mes Values et si la valeur lue est toujours au dessus, alors seulement là je valide. Vous pensez que c'est possible ?

Dernière petite question, j'ai lu des trucs sur le débordement du millis tous les 50 jours environ, est-ce que , sachant que mon Arduino sera débranché tous les jours, je serai concerné par ce problème ?

Tu es sur la bonne voie. Il doit y avoir plusieurs manières de faire ce que tu veux faire, en voici une :

Tu lis la valeur de la PR (photo résistance) et dès qu'elle dépasse le seuil tu lances le chrono et tu mets un flag à true. Tant que le temps de mesure n'est pas passé, tu lis le niveau de la PR. S'il passe sous le seuil (peut-être mettre un peu d'hysteresis ici, c'est à dire comparer au seuil diminué d'une petite marge) tu mets le flag à false et tu sors du while (break).
Ensuite, après le while, tu testes le flag pour savoir s'il faut ouvrir ou non.

Pour la limite de durée de millis, il faudrait que tu lances le chrono juste avant cette limite pour que ça pose un problème. Si tu débranches l'arduino tous les jours, millis repartira de zéro à l'allumage donc pas de souci.

Alors, j’ai suivi tes indications et j’ai fait ça :

const int sensorPin = A0;
int sensorValue = 0;
int unlockValue = 800;
boolean flag = false;
unsigned long chrono = millis();

void setup() {
  Serial.begin(9600);
  pinMode(2, OUTPUT);
  digitalWrite(2, HIGH);
}

void loop() {
  sensorValue = analogRead(sensorPin);
  Serial.print("Sensor value =");
  Serial.println(sensorValue);

  if(sensorValue >= unlockValue){
    chrono = millis();
    flag = true;
  }    
  while(chrono < 10000){
      sensorValue = analogRead(sensorPin);
      Serial.print("Sensor value =");
      Serial.println(sensorValue);
      if (sensorValue < unlockValue){
        flag = false;
        break;
      }
  }
  if(chrono >= 10000 && flag = true){
      digitalWrite(2, LOW);
      Serial.println("Puzzle Solved");
  }
}

Par contre, j’ai un message d’erreur qui me dit " if(chrono >= 10000 && flag = true){
exit status 1
lvalue required as left operand of assignment"

Tu sais pourquoi ? Sinon à part ça, ça te semble bon ?

FoNeTiK47:
if(chrono >= 10000 && flag = true){
exit status 1
lvalue required as left operand of assignment"

A cause de =
le symbole = c’est une affectation
utilise == qui est l’opérateur de comparaison d’égalité

Facepalm Mais quel con !
Merci Jambe ^^

Par contre mon programme ne fonctionne toujours pas.
Pendant les 10 premières secondes où l'Arduino est sous tension, on ne peut pas débloquer l'aimant. Au bout de 10s, l'aimant est relaché immédiatement quand on éclaire la PR.

Le problème semble venir du fait que le décompte de la variable "chrono" se lance dès le début alors que je voudrais qu'elle ne se lance que quand la condition "sensorValue >= unlockValue" est vérifiée.
Vous voyez comment je peux faire ?

bonjour,

while(chrono < 10000){
      sensorValue = analogRead(sensorPin);
      Serial.print("Sensor value =");
      Serial.println(sensorValue);
      if (sensorValue < unlockValue){
        flag = false;
        break;
      }
  }

Cette partie là pose un problème car tu scrute la valeur de chrono en permanence pour savoir à quelle moment sortir. hors cette valeur n’ai pas modifié dans ton while.
Il n’y a donc aucune chance pour que tu sortes de ta boucle en dehors du break (valeur de sensorValue).

Tant que le temps de mesure n’est pas passé,

Ça se traduit, tu devrais l’avoir vu dans les tutos que tu as les, par

while (millis() - chrono < duree)

Ensuite, tu ne testes que le flag pas le chrono pour ouvrir la porte

Si vous n’aimez pas millis() et que vous n’envisagez pas d’extension, vous pouvez:
si la photoresistance est éclairée
attendre 5000 ms (avec delay)
si elle est toujours éclairée, ouvrir la porte.

delay est bloquant: le processeur scrute l’horloge, et daigne faire autre chose une fois le delai écoulé (alors que l’appel à millis() n’est pas bloquant: le processeur est libre de faire ce que vous voulez et de scruter de temps en temps)

Est ce que votre automate peut marcher de jour?

Sauf qu'en utilisant delay() la source de lumière peut disparaitre quelques secondes puis revenir avant l'expiration du délai et cela sera considéré comme bon alors qu'avec millis() on peut détecter la coupure.

Oui, mais est ce probable?
par ailleurs je trouve votre temporisarion bien longue, même pour un être humain patient).

On ne se base que sur l'énoncé du problème ::slight_smile: . Pour le reste, seule la personne est apte à déterminer ce qu'il veut obtenir. Il pourra toujours modifier si besoin par le suite. :wink:

Dans ma réponse, justifiée par le fait que, nulle part il n'a été spécifié que l'éclairage devait être continu (et certaines lampes torches doivent avoir une PWM....) , je mettais bien une condition sur le désamour de millis() -qui lit un compteur extrêmement précis, démarrant à la mise sous tension, sans en perturber le fonctionnement-. Je ne prenais pas position sur ce désamour (je préfère millis() delay, qui ne fait rien d'autre que scruter millis() et comparer le résultat à l'échéance, ne laissant aucun cycle à l'utilisateur)

Pourtant l'énoncé au #1 est assez claire

J'aimerais complexifier un peu la chose : il faudrait éclairer ma photo résistance pendant disons 5s avant que ma condition ne soit validée. Si elle n'est éclairée que pendant 4s, rien ne se passe, le compteur est réinitialisé et il faudra l'éclairer à nouveau pendant 5s pour pouvoir valider l'ouverture de la porte.

Alors là, si on utilise une torche à LED commandée par des impulsions , fussent-telles très rapides (une façon simple de gérer des LEDs , avec un fort rendement, sans dissiper de la puissance dans une résistance), c'est ... impossible : supposez qu'elles aient un rapport cyclique de 50%, il faudrait attendre 10s et "bien" échantillonner pour décider de la fermeture...

Pour la fonction millis(), c'est vraiment important de passez un peu de temps dessus. Tant que tes programmes ne font pas deux choses en même temps, ça passe, mais tu risques de vite te limiter pour ton projet.

Je vais donc essayer de t'expliquer le plus simplement possible. :wink:

Imagine que tu veux te cuire un œuf dur et que tu ne disposes que de ta montre pour te repérer dans le temps.
Pour obtenir un œuf dur, tu doit le plonger 9 minutes dans de l'eau bouillante.

Au moment où tu plongés l'œuf, tu ne va pas mettre les aiguilles de ta montre sur 12h00. Tu n'agis pas sur le temps mais tu t'en sers comme référence. Tu va donc mémoriser l'heure sur ta montre puis attendre que les 9 minutes passent.
Pendant ce temps, rien ne t'empêche de faire autre chose. Il faut juste que tu jetés un œil de temps en temps sur ta montre pour voir si tu n'as pas dépassé les 9 minutes nécessaires à la cuisson, soit 9 minutes après avoir plongé l'œuf.

Dans ton arduino, le temps qui passe se traduit par une fonction millis() qui te retourne un nombre sur 4 octets.
La valeur de millis() est tout simplement le nombre de millisecondes écoulées depuis le dernier reset du microcontroleur.

Au moment où tu alimentes l'arduino, un reset du microcontroleur est effectué et le compteur de millisecondes (obtenu avec la fonction millis()) commence à s'incrémenter. C'est le temps qui passe... Tu ne peux pas agir dessus. Mais tu peux l'utiliser comme référence de temps.

La fonction millis() te retourne une valeur que tu dois stocker dans une variable de type unsigned long. Par unsigned (non signé), on indique que cette variable ne peut être que nulle ou positive (et le 'long' et un type de variable stockée sur 4 octets) .
La valeur retournée par millis() va donc s'incrémenter jusqu'à faire le tour du compteur (donc de 0 à 4,294,967,295), ce qui représente une période de 50 jours environ avant de reprendre à 0.

Revenons à notre œuf... ;D

Pour attendre 9 minutes (soit 9601000 millisecondes), tu vas donc mémoriser la référence de temps dans une variable au moment où tu plonges ton œuf.

chrono = millis() ;

La variable chrono (qui est forcément un unsigned long) contient maintenant le temps de début de ta cuisson.

Pour savoir quand tu dois retirer ton œuf, tu dois donc attendre que la différence entre le temps actuel (que tu lis sur ta montre) et le temps mémorisé dans chrono soit supérieur, ou égal à 9601000.

temps actuel - chrono >= temps de cuisson d'un œuf dur.

Tu remarqueras qu'on ne peut pas se limiter qu'au signe égal (==) car si tu fais autre chose en même temps, il est fort probable que tu ne sois pas le nez sur ta montre pour arrêter la cuisson pile poil au moment voulu.

La condition qui va donc valider la cuisson de ton œuf est donc:

Si (temps actuel - chrono) est supérieur ou égal à 9 minutes, alors mon œuf est cuit, je stoppe la cuisson.
Sinon je fais autre chose et je reviendrai plus tard pour vérifier à nouveau le temps de cuisson.

Si tu n'as qu'une petite casserole et que tu veux te faire un autre œuf, tu ne dois pas oublier de mémoriser le nouveau temps de référence du second œuf au moment où tu le plonges dans l'eau. Et si tu veux gagner du temps, tu le fais juste après avoir retiré le premier œuf.

Maintenant si tu veux te la jouer grand chef cuisinier, tu peux lancer plusieurs cuissons en même temps. Tu dois juste mémoriser tout tes débuts de cuisson et vérifier en boucle les moments où tu dois intervenir dans chaque casseroles pour ajouter un ingrédient ou arrêter la cuisson.

Tu remarqueras que le temps passé à vérifier les temps de cuisson est très rapide, ce qui te laisse du temps pour te préparer un café (en séparant les étapes si nécessaire) .

Mais si tu veux être précis, il vaut mieux éviter de boire l'apéro en même temps avec tes potes, ou aller piquer un roupillon, car tu risques de ne pas être dispo pour vérifier tes temps de cuisson. 8)

Ça y est ! Avec la bonne formulation pour millis ça fonctionne !
Zlika, un IMMENSE merci pour tes explications. Tu m'as fait comprendre que ce qui me bloquait depuis le début : je pensais que chrono = millis() voulait dire "lance un 2ème chrono qui s'appelle" chrono" ) alors qu'en fait, il s'agit juste d'une mémorisation du temps écoulé au moment où on le demande. Ça change tout !

(en effet, rester pendant 10s à éclairer la PR, c'est tout sauf ludique ! J'avais mis 10s le temps de réaliser des tests. En vrai, elle sera réglée sur 3s.)
Et oui, l'idée d'utiliser delay en acceptant le fait (peu probable) que des personnes n'éclairent plus de 2 à 2,5 secondes puis reeclairent à la 3ème seconde, m'avait traversé l'esprit.
Merci à tous pour votre aide. Bonne soirée.