Boucle d'un programme qui fonctionne qu'une seule fois

Bonjour

Je suis entrain de créer un programme pour un contrôle d’accès avec un badge RFID. Plus en détails, le programme enregistre un badge puis au passage d’un second badge, le programme vérifie s’il s’agit du même et autorise l’accès ou non.

Mon problème est dans la partie du programme où est vérifié si le badge présenté au capteur est bien le même que celui enregistre au début, elle fonctionne qu’une seule fois alors quelle devrait fonctionner en boucle, et je n’arrive pas à trouver pourquoi (la raison est peut être évidente, mais impossible de trouver :cold_sweat: )
Merci d’avance pour ceux qui vont m’aider.

PS: ne faite pas attention aux chiffres ou aux phrases dans certain “Serial.println” c’est pour voir ou est rendu le programme :grin: .

#include <SPI.h> // Bibliothèque pour le capteur RFID
#include <MFRC522.h> // Bibliothèque pour le capteur RFID

String nouveau = "";
String contenu = "";
String codebon = "";
String codebon2 = "";
String codebon3 = "";
String codebon4 = "";

int verif = 0;

#define SDA_PIN 10
#define RST_PIN 9

MFRC522 mrfc(SDA_PIN, RST_PIN);

void setup() {
  Serial.begin(9600);
  SPI.begin();
  mrfc.PCD_Init();

  codebon4 = 1;
}

void loop() {

  nouveaubadge();
  RFID();
}

void nouveaubadge() { 

  if (Serial.available() > 0)
  {
    char Ncode = Serial.read();
    if (Ncode == 'b') {  // Quand le programme lis la lettre "b" dans le moniteur série
      verif = 0;         // Met la variable "verif" à 0
      Serial.println("0");
      Serial.println("Approchez la carte du lecteur");
      delay(3000);
      if (! mrfc.PICC_IsNewCardPresent())               //Si il n'y a pas de carte
      {
        return;                                         //attendre
      }
      if (! mrfc.PICC_ReadCardSerial())                 //Si la carte n'est pas lu dans le moniteur
      {
        return;                                         //attendre
      }
      Serial.print("Code du nouveau badge :");                  //afficher code du badge :

      byte lettre;
      for (byte i = 0; i < mrfc.uid.size; i++) {
        Serial.print(mrfc.uid.uidByte[i] < 0x10 ? "0" : " ");
        Serial.print(mrfc.uid.uidByte[i], HEX);           //Envoie le code HEX du badge utilisé
        nouveau.concat(String(mrfc.uid.uidByte[i] < 0x10 ? "0" : " "));
        nouveau.concat(String(mrfc.uid.uidByte[i], HEX)); //Sauvegarde le code sous "nouveau"
      }
    }

    Serial.println();
    Serial.println("Badge Changé");

    verif = 1;                      // La variable "verif" passe à 1
    Serial.println("1");
    if (verif = 1)                  // Si la variable "verif" est égal à 1 alors la varaible "codebon" = "nouveau"
    {
      codebon = nouveau;
      Serial.println("Codebon");
    }

    codebon2 = codebon;           // La variable "codebon2" est égal à "codebon" pour sauvegarder le code du badge 
    verif = 2;                    // La variable "verif" passe à 2
    Serial.println("2");
  }


  if (! mrfc.PICC_IsNewCardPresent())               //Si il n'y a pas de carte
  {
    return;                                         //attendre
  }
  if (! mrfc.PICC_ReadCardSerial())                 //Si la carte n'est pas lu dans le moniteur
  {
    return;                                         //attendre
  }
  if (verif > 0) {                                  // Effectue ce "if" que si la variable "verif" est supérieur à 0 ce qui permet de faire fonctionner le contrôle d'accès que quand un badge est enregistré

    Serial.print("Code du badge :");                  //afficher code du badge :

    byte lettre;

    for (byte i = 0; i < mrfc.uid.size; i++)            //mrfc.uid.size = Taille du code en HEX
    {
      Serial.print(mrfc.uid.uidByte[i] < 0x10 ? "0" : " ");
      Serial.print(mrfc.uid.uidByte[i], HEX);           //Envoie le code HEX du badge utilisé
      contenu.concat(String(mrfc.uid.uidByte[i] < 0x10 ? "0" : " "));
      contenu.concat(String(mrfc.uid.uidByte[i], HEX)); //Sauvegarde le code sous contenu

    }
    verif = 3;                              // La variable "verif" passe à 3
    Serial.println("3");
    if (verif = 3)                           // Si la variable "verif" est égal à 3 alors la varaible "codebon3" = "contenu"
    {
      codebon3 = contenu;                     
      Serial.println("Codebon3");

    }

    codebon4 = codebon3;                    // La variable "codebon2" est égal à "codebon" pour sauvegarder le code du badge

    Serial.println("on y est presque");
    delay(500);
  }
}


// Problème ici : RFID marche qu'une seule fois
void RFID() {
  if (codebon4 == codebon2)                           // Si la variable "codebon4"  équivaut à "codebon2" alors autorise l'accès sinon non  
  {
    Serial.println("C'est passé !!!!");

    Serial.println();
    Serial.print("Message : ");
    codebon4.toUpperCase();
    if (codebon4.substring(1))    //Si le code est bon
    {
      Serial.println("Accès autorisé");
      delay(50);
    }
    else                                          //Si le code est mauvais
    {
      Serial.println("Accès refusé");             //Afficher Accès refusé
   }
  }
}

il faut mettre l ' appel a la fonction RFID dans la loop boucle , pour executer une action plusieurs fois :confused:

ça s'est pas top...

    if (verif = 1)                  // Si la variable "verif" est égal à 1 alors la varaible "codebon" = "nouveau"

    if (verif = 3)     pour tester une égalité c'est ==

pensez aussi à remettre nouveau à "" avant de le re-remplir avec une nouvelle carte...

il me semble qu'on avait discuté et convenu que passer par des String pour garder un petit tableau d'octets c'est vraiment pas top...

il ya des erreurs dans les conditions :

if (verif = 1)

c' est : if (verif == 1) !

tenez voici un exemple de à quoi ça pourrait ressembler. j’ai tapé cela ici donc je ne sais pas si ça compile, il peut aussi y avoir un bug ou deux…

#include <SPI.h>      // Le SPI pour la Bibliothèque RFID
#include <MFRC522.h>  // https://github.com/miguelbalboa/rfid (Mifare RC522)
#define SDA_PIN 10
#define RST_PIN 9
MFRC522 mrfc(SDA_PIN, RST_PIN);

enum : uint8_t {STANDARD, ADMINISTRATEUR} mode = STANDARD;

const uint32_t timeoutAdmin = 30000UL; // on a 30 secondes pour présenter le badge Admin
uint32_t debutModeAdmin;

struct t_badge {
  uint8_t longueur;
  uint8_t identifiant[10];
};

t_badge badgeAdministrateur = {0, {0}}; // définir ici le badge par défaut éventuellement

void imprimeBadge(t_badge& unBadge)
{
  for (uint8_t i = 0; i < unBadge.longueur; i++) {
    if (unBadge.identifiant[i] <= 0xF) Serial.write('0');
    Serial.print(unBadge.identifiant[i], HEX);
    if (i != unBadge.longueur - 1) Serial.write(':'); // un deux point entre les octets
  }
}


bool lireBadge(t_badge& unBadge)
{
  unBadge.longueur = 0;
  if (! mrfc.PICC_IsNewCardPresent()) return false;
  if (! mrfc.PICC_ReadCardSerial())   return false;
  unBadge.longueur = mrfc.uid.size;
  memcpy(unBadge.identifiant, mrfc.uid.uidByte, mrfc.uid.size); // http://www.cplusplus.com/reference/cstring/memcpy/
  return true;
}

void setup()
{
  Serial.begin(115200);
  SPI.begin();
  mrfc.PCD_Init();
}

void loop()
{
  t_badge leBadge;
  switch (mode) {
    case STANDARD:
      // on regarde si on veut passer admin
      if (Serial.read() == 'b') { // read retourne -1 s'il n'y a rien à lire
        Serial.println(F("MODE ADMIN"));
        Serial.println(F("Approchez la carte Admin du lecteur"));
        debutModeAdmin = millis();
        mode = ADMINISTRATEUR;
        break;
      }

      // on regarde si un badge est présenté
      if (lireBadge(leBadge)) {
        if ((badgeAdministrateur.longueur != 0) && (badgeAdministrateur.longueur == leBadge.longueur)) {
          imprimeBadge(leBadge);
          // est-ce le badge Admin
          if (memcmp(badgeAdministrateur.identifiant, leBadge.identifiant, badgeAdministrateur.longueur) == 0) { // http://www.cplusplus.com/reference/cstring/memcmp/?kw=memcmp
            Serial.println(F("\tAccès autorisé"));
          } else {
            Serial.println(F("\tAccès refusé"));
          }
        }
        break;

      case ADMINISTRATEUR:
        // le temps imparti pour présenter le badge Admin est-il écoulé ?
        if (millis() - debutModeAdmin >= timeoutAdmin) {
          Serial.println(F("Admin: timeout"));
          mode = STANDARD;
          break;
        }

        // a-t-on un badge présenté ?
        if (lireBadge(leBadge)) {
          // on mémorise le badge Admin
          badgeAdministrateur.longueur = leBadge.longueur;
          memcpy(badgeAdministrateur.identifiant, leBadge.identifiant, leBadge.longueur); // http://www.cplusplus.com/reference/cstring/memcpy/
          Serial.print("Badge Admin Changé: ");
          imprimeBadge(badgeAdministrateur);
          Serial.println();
          mode = STANDARD;
        }
        break;
      }
  }
}

L’idée c’est qu’on fait une petite machine à état (cf mon tuto éventuellement) pour définir si on est en mode ‘Administrateur’ ou en mode Standard. En mode admin si on présente une carte elle devient la carte Admin et en mode standard si on présente une carte elle est comparée à la carte Admin.

Il y a un time-out de 30 secondes si on ne présente pas de carte après être passé en mode Admin.

(le moniteur série doit être à 115200 bauds, pas besoin d’aller lentement à 9600…)

J'ai essayé votre programme et il fonctionne parfaitement mais le problème (cette fois si c'est de moi et pas du programme), je ne comprend rien, je veux dire que chaque ligne, je dois savoir ce qu'elle fait. C'était une des raisons pour lequel je prenais des String, c'est plus facile à comprendre pour moi.

J'ai donc deux solutions, soit continuer sur mon programme, comme ça j'ai tout compris et je n'aurai aucun mal à l'expliquer, ou sinon je prend votre programme et je regarde ligne par ligne et je regarde ce qu'elle fait.

Mais je vous remercie de m'avoir aidé.

Bouyou: J'ai essayé votre programme et il fonctionne parfaitement mais le problème (cette fois si c'est de moi et pas du programme), je ne comprend rien, je veux dire que chaque ligne, je dois savoir ce qu'elle fait. C'était une des raisons pour lequel je prenais des String, c'est plus facile à comprendre pour moi.

En fait la solution c ' est de nommer correctement vos variables ou constantes par exemple :

string codebon4 = 1

rien n ' empeche de mettre un type different de string et de conserver le meme nom voir ci dessous :

boolean codebon4 = 1 // le type de variable booleene ne peut valoir que deux choses : vrai ou fausse soit 0 ou 1 .
byte codebon4 = 1 // peut valoir de 0 a 255 ( dans mes souvenirs )

De toute façon , vos programmes ne marcheront pas si vous utilisez des variables de type string uniquement . il faut toujours choisir le bon type et le bon nom de variable en fonction de ce qu ' elle va prendre comme valeurs possible dans votre programme .

Que ce soit avec des String ou pas, ce n'est pas gênant (même si c'est mieux sans). C'est plus la structuration du code qui compte, le votre est un peu "plat de spaghettis"... :slight_smile:

C'est bien de vouloir comprendre. lisez mon tuto sur les machines à état

En gros mon programme est une "machine à état" qui dépend de la variable mode qui peut prendre deux valeurs, soit STANDARD, soit ADMINISTRATEUR

voilà le diagramme des états et des transitions.


Quand je suis en mode Normal, deux transitions sont possibles

  • Je reçois 'b' sur le port série ==> dans ce cas je passe en mode ADMIN
  • Une carte est présentée ==> dans ce cas je regarde si c'est la carte admin et affiche un message en conséquence puis reviens à l'attente du mode NORMAL

Quand je suis en mode ADMIN, deux transitions sont possibles

  • le temps de 30s en mode admin est écoulé ==> dans ce cas je reviens au mode Normal
  • Une carte est présentée ==> dans ce cas je l'enregistre comme nouvelle carte Admin et je reviens au mode Normal

pour mémoriser une carte j'ai créé une structure qui comporte 2 champs: le nombre d'octets de l'ID et la liste de ces octets.

la fonction lireBadge() retourne vrai si on a lu un badge et dans ce cas elle aura mis les informations du badge dans la variable passée en paramètre (le & est un passage par référence donc on écrit directement dans le paramètre de l'appelant).

les fonctions memcpy() et memcmp() (les liens sont fournis) ne font que copier ou comparer deux zones mémoires. On dit combien d'octets on veut copier ou comparer, et ici bien sûr c'est le nombre d'octet nécessaire à l'identifiant.

est-ce que ça vous aide ?

Oui merci beaucoup c'est beaucoup plus clair maintenant. je vais essayer de faire ligne par ligne mais avec votre explication je vais mieux comprendre.

Bon maintenant après avoir passé pas mal de temps sur le programme et cherché sur internet, j'aimerais être sur et j'ai quelque question:

La variable "enum" est bien une variable qui permet de regrouper des "statuts" (STANDARD et ADMINISTRATEUR) ?

Dans "enum : uint8_t {STANDARD, ADMINISTRATEUR} mode = STANDARD;", cette ligne permet bien de définir deux "statuts" et définit que le programme débute en mode "STANDARD" ?

La fonction "struct" permet bien de regrouper des variables ?

Je sais que la fonction "return" permet de terminer la fonction en cours (enfin si j'ai bien compris) et peut aussi renvoyer une valeur, mais je comprend pas "return false" ou "return true", il renvoi "faux" ou "vrai" ??

A quoi sert le "F" dans les serial.println ?

Et enfin pour la fonction "memcpy", c'est celle où je suis le moins sur d'avoir bien compris, dans memcpy(unBadge.identifiant, mrfc.uid.uidByte, mrfc.uid.size); le programme copie dans "unBadge.identifiant", la source est "mrfc.uid.uidByte" et le nombre d'octet à copié est dans "mrfc.uid.size" ?

J'avoue que pour cette ligne là je suis un peu paumé :cold_sweat:

Bon voilà pour l'instant les quelque questions que j'ai sur votre programme.

Une énumération fournit un contexte pour décrire une plage de valeurs qui sont représentées en tant que constantes nommées, également appelées « énumérateurs »

Si on fait enum {STANDARD, ADMINISTRATEUR};on dit au compilo qu’il y a deux mots clés à connaitre et que l’on peut utiliser dans le code. Par défaut le compilateur prend des entiers (2 octets) et le premier de la liste vaut 0, le second vaut 1 etc…

Comme 2 octets c’est beaucoup, on peut donner le type de l’enum.enum : uint8_t {STANDARD, ADMINISTRATEUR}; va utiliser un seul octet.

Enfin on peut donner un nom à ce type énuméré et déclarer des variables qui ne pourront prendre (théoriquement) que des valeurs de l’énumération.

Donc en faisant enum : uint8_t {STANDARD, ADMINISTRATEUR} mode = STANDARD;je dis que je veux une variable qui s’appelle mode, dont le type sous jacent est uint8_t et qui prendra comme valeur soit STANDARD, soit ADMINISTRATEUR

struct n’est pas une fonction, c’est un mot clé qui permet de définir une structure, un nouveau type de données au sein duquel on peut grouper effectivement plusieurs choses.

oui return true ou false c’est pour retourner une valeur de vérité. La fonction renvoie vraie si un badge a été lu et faux sinon. ça permet comme cela de mettre la fonction directement dans un if. c’est ce que je fais avec       if (lireBadge(leBadge)) {Le if va tester cette valeur retournée par la fonction.

le F(“xxx”) dans les print permet d’avoir le le texte en mémoire flash et ne pas le ramener en mémoire SRAM pour l’imprimer. On gagne ainsi de la place mémoire. cf “The F() macro” dans la documentation de PROGMEM

Pour memcpy():
vous avez un tableau (de 10 cases mémoire - 10 octets) qui s’appelle unBadge.identifiant (un des bout de la structure où on a groupé les infos sur un badge) dans lequel on veut copier les données qui ont été lue sur le badge présenté. Ces données lues sont rangées dans un tableau qui s’appelle mrfc.uid.uidByte et on sait qu’on en a mrfc.uid.size.
L’appel de la fonction dit simplement copie ce nombre d’octet depuis le second tableau vers le premier.

on aurait pu sinon écrire à la main:

for (int i=0; i< mrfc.uid.size; i++) {
  unBadge.identifiant[i] = mrfc.uid.uidByte[i];
}

ça aurait fait la même chose.