Commander servo + led + buzzer avec RFID

Bonsoir à tous !
Le projet : une maquette d'ouverture de barrière avec une carte RFID.
le principe : en passant la carte (la bonne !) devant le lecteur un servomoteur se met en marche,
et simultanément une led clignote et un buzzer bip-bip...
Le matériel : arduino Uno, module RFID RC522, servo SG90, led et buzzer actif.
Le code bidouillé en deux temps :
_ d'abord reconnaissance de l'UID de la carte, ça fonctionne...
_ ensuite fonctionnement simultané servo+led+buzzer, avec millis ça roule...
_ enfin je rassemble ces deux parties et le mariage est manqué; en effet après avoir passé la carte
devant le lecteur(reconnue), servo+led+buzzer se mettent en marche mais se bloquent dès que j'éloigne
la carte du lecteur.
Or dans la "vraie vie" pour faire ouvrir une barrière on présente le badge prévu quelques secondes et après la barrière continue à se lever...
Quoi donc empêche ce beau système de fonctionner correctement ?
Merci d'avance pour vos conseils éclairés... qui m'éclaireront!

[code]

//2020.04.27

#include <SPI.h>
#include <MFRC522.h>
#define SS_PIN 10
#define RST_PIN 9
MFRC522 mfrc522(SS_PIN, RST_PIN);

#include<Servo.h>
Servo moteur;

int angleprecedent, anglenouveau = 0;
long tempsactuel;
long tempsprecedentmoteur = 0;
int a = 1;


long tempsprecedentled = 0;
long tempsprecedentbuzzer = 0;
int led = 3;
int buzzer = 2;
byte etatled = LOW;
byte etatbuzzer = LOW;

void setup()
{
  Serial.begin(9600);
  SPI.begin();
  mfrc522.PCD_Init();   
   
  Serial.println("Passer la carte devant le lecteur...");
  Serial.println();
    
  moteur.attach(6);
  moteur.write(0);
  pinMode(buzzer, OUTPUT);
  pinMode(led, OUTPUT);
}
void loop()
{
  if ( ! mfrc522.PICC_IsNewCardPresent())
  {
    return;
  }
  if ( ! mfrc522.PICC_ReadCardSerial())
  {
    return;
  }
  String UID= "";
  for (byte i = 0; i < mfrc522.uid.size; i++)
  {
    if(mfrc522.uid.uidByte[i]<0x10)
    {
    String(mfrc522.uid.uidByte[i])=" 0";
    }
    else
    {String(mfrc522.uid.uidByte[i])=" ";
    }
    UID =UID + (String(mfrc522.uid.uidByte[i], HEX));  
    }
  Serial.println();
  Serial.print("L'UID est :");
  UID.toUpperCase();
  Serial.print(UID);
  Serial.print("  ");
  if (UID == "4A9C65A3") //UID de la carte à utiliser(sans espaces)
  {
    Serial.println("Carte acceptée");
    Serial.println();
    Moteur();
    Led();
    Buzzer();    
    }         
   if (UID != "4A9C65A3")  {
    Serial.println("Carte refusée");    
  }     
  }
  


  

void Moteur() 
{
  anglenouveau = angleprecedent +a;
  Serial.println(anglenouveau);
  
  tempsactuel = millis();
  if ( tempsactuel  - tempsprecedentmoteur > 50)
  {
    tempsprecedentmoteur = tempsactuel;
    angleprecedent = anglenouveau;
    moteur.write(anglenouveau);
  }
  if (anglenouveau == 150) {
    a = -a;delay(3000); 
  }
  if (anglenouveau <= 0) {    
   moteur.detach();
   }
  

  
}

void Led() {
  tempsactuel = millis();
  if ((tempsactuel - tempsprecedentled) > 500)
  {   
    etatled = !etatled;
    digitalWrite(led, etatled);
    tempsprecedentled = tempsactuel;
  }
  }
  


void Buzzer()
{
  tempsactuel = millis();
  if ((tempsactuel - tempsprecedentbuzzer) > 500)
  {   
    etatbuzzer = !etatbuzzer;
    digitalWrite(buzzer, etatbuzzer);
    tempsprecedentbuzzer = tempsactuel;
  }
  }

[/code]

Parce que vous ne notez pas le fait que la carte est acceptée et que vous êtes en phase de déplacement / blink / buzz. Dans ce cas il ne faut plus tester la carte dans la loop.

Si vous voulez garder le fonctionnement avec la loop et gestion du timing, rajoutez un booléen qui dit dans quel mode vous êtes: attente de carte ou mouvement. En fonction de ce booléen, soit vous testez la carte, soit vous bougez.

(sinon Attention, tout ce qui a trait au temps (millis) doit être en unsigned long, pas juste en long)

De manière plus générale, c'est typiquement une définition de programme qui se prête bien à la programmation par machine à états (cf mon tuto éventuellement) si vous voulez structurer le code

Bonjour J.M.L et à tous,

Merci pour cette réponse mais il va falloir que je démêle tout ça...( peut-être une prolongation du confinement me serait alors profitable !!!)
En tout cas chapeau pour le tuto qui me semble très pédagogique, que je n'ai fait que survoler pour le moment, mais que je regarderai de près car les principes énoncés doivent être applicables à de nombreuses situations...
En attendant de l'aide, j'avais trouvé une bidouille sûrement pas du tout orthodoxe, mais qui a fait le boulot; j'ai modifié l'appel des void en ajoutant une void Retour qui se mord la queue (?), comme ça :

void Retour()
{Moteur();
Led();
Buzzer();
Retour();}

Et à ma grande surprise ça marche ! Cordialement

Salut

ça ne va pas marcher longtemps... chaque appel empile un contexte sur la pile, au bout d'un moment vous n'aurez plus de mémoire . S'il y a peu d'appels, alors ça passe mais si ça dure un peu trop...

sinon in "void" ça ne veut rien dire, on parle de fonctions

Bien noté : unsigned long pour les millis (post précédent) et fonction pour void...
Pour la mémoire pas de problème, c'est un montage complètement expérimental, mais c'est toujours utile de savoir qu'on peut la saturer même si je ne comprends pas le pourquoi du comment !
Merci pour les remarques...

c'est toujours utile de savoir qu'on peut la saturer même si je ne comprends pas le pourquoi du comment

Ce que vous avez fait s'appelle une "récursion", c'est une fonction qui s'appelle elle même.

Pour faire simple, un programme déroule tranquillement ses instructions les unes après les autres.


Si à un endroit - ici après instruction 3 vous appelez une fonction, vous dites au code de se dérouter et d'aller chercher ses prochaines instructions à un autre endroit en mémoire (là ou le code de la fonction est stocké).

Une fois que vous êtes arrivé à la fin de la fonction (instruction C), vous souhaitez revenir à l'endroit de départ pour pouvoir continuer la séquence.

C'est là où vous devez vous demandez: "mais dites moi donc, comme je peux appeler la fonction de n'importe où, comment mon Arduino sait qu'il faut précisément revenir à cet endroit et pas ailleurs ?"

et la réponse (en simplifiant), c'est comme le petit Poucet :slight_smile: : Parce que le compilateur a laissé des petits cailloux pour retrouver son chemin --> avant de se dérouter il a stocké en mémoire, sur la pile, l'adresse de retour. A la fin de la fonction le compilateur regarde ce qu'il y a sur la pile et retrouve son chemin.

Mais comme dans l'histoire du petit Poucet, si vous allez loin (de fonctions en fonctions) au bout d'un moment vous n'avez plus de cailloux blancs (de place sur la pile) et si vous mettez de la mie de pain, on sait ce qu'il arrive :slight_smile:

bref - à chaque appel de fonction on stocke temporairement sur la pile l'adresse de retour (et un peu d'autre infos - les paramètres, de la place pour ce que la fonction retourne, éventuellement les valeurs des registres de travail, ...) et donc on remplit la mémoire. Cette mémoire se libère d'elle même quand on remonte la trace des fonctions, mais si vous faites une récursion, à chaque fois que la fonction s'appelle, vous en remettez une couche. Si au bout de 10 couches la fonction ne s'appelle plus alors on dépile et tout va bien, mais si vous faites ça 1000 fois dans une machine qui n'a que 2ko de SRAM ça plante...

j'espère que ça démystifie un peu le truc..

Bonjour,
Merci pour cette explication claire et... imagée !
Cordialement.