Désactivation digicode 30s après son emploi

Bonjour tout le monde! C’est la première fois que j’écris sur ce forum mais là je suis vraiment en grosse difficulté :confused: J’ai comme projet de faire un prototype de distributeur à codes de mini-bouteilles de gel hydroalcoolique, qui pourrait par exemple être mis dans une entreprise pour les employés. Il fonctionnerait de la sorte: un keypad permet d’entrer son code (dans mon programme j’en ai référencé 3, c’est juste un prototype), si le code est bon, un moteur pas à pas de met en route, fait tourner une vis sans fin et la bouteille tombe. Cependant, vue que cela serait mis à disposition de beaucoup de personnes, il faut éviter les abus. C’est pourquoi je voudrais que quand la personne utilise son code, sa bouteille tombe mais ensuite elle doit attendre 24h avant de pouvoir le réutiliser! En fait, son code doit se “désactiver” pendant 24h, dans mon projet actuel je voudrais symboliser ces 24h par 30s. J’ai tout essayé, delay(), qui ne peut pas fonctionner car il fait une pause dans tout mon programme mais je ne peux pas car les autres “employés” dont le code n’a pas encore été utilisé dans les 30 secondes doivent encore pouvoir se servir… Pour le millis(), je n’ai également rien trouvé de concluent…

Dans le code dont je vais vous faire part, mon programme fait déjà: accepte les 3 codes définis et écrit sur mon lcd “Code:”, “Code:****” lorsqu’on a tapé et ensuite “Code bon” ou “Mauvais code” avant que le lcd se clear et reviennent à “Code:”.

Je vous remercie d’avance pour l’aide que vous m’apporterez :-*

  #define ligne_0 2   //ici on définit sur quelle pin est branchée chaque ligne
  #define ligne_1 3
  #define ligne_2 4
  #define ligne_3 5

  #define colonne_0 8  // pareil mais pour les colonnes
  #define colonne_1 9
  #define colonne_2 10
  #define colonne_3 11

// section lcd 

  #include <Wire.h>
  #include <LiquidCrystal_I2C.h>
  LiquidCrystal_I2C lcd(0x27,16,2);
  
void setup()
{
 Serial.begin(9600);

pinMode(ligne_0,OUTPUT);  //on déclare que les lignes sont des sorties
pinMode(ligne_1,OUTPUT);
pinMode(ligne_2,OUTPUT);
pinMode(ligne_3,OUTPUT);

pinMode(colonne_0,INPUT_PULLUP); //on déclare que les colonnes sont des entrées avec résistance de pull-up
pinMode(colonne_1,INPUT_PULLUP);
pinMode(colonne_2,INPUT_PULLUP);
pinMode(colonne_3,INPUT_PULLUP); 

lcd.init();
lcd.backlight();
lcd.setCursor(0,0);
lcd.print("Code:");
}

void loop()
{
 int taille_codes = 4; 
 char code_employe1[taille_codes] = {'1','2','3','4'};   //tableau qui contient les caracts du code secret
 char code_employe2[taille_codes] = {'A','B','C','D'};
 char code_employe3[taille_codes] = {'*','2','3','4'};
 
 char code_tape[taille_codes] = {0};                      //tableau qui contient les caracts du code tapé
 
 for (int nb_caract=0; nb_caract < taille_codes; nb_caract++)
 {
  code_tape [nb_caract] = bouton_ok();
  lcd.setCursor(nb_caract +5,0);
  lcd.print ('*');
 }
boolean code_bon = true; 

 for (int caseX = 0; caseX < taille_codes; caseX++)       //on va comparer les cases jumelles des 2 tableaux et voir si les mêmes caracts s'y trouvent
 {
  if (code_employe1[caseX] != code_tape[caseX] && code_employe2[caseX] != code_tape[caseX] && code_employe3[caseX] != code_tape[caseX])
  {
    code_bon = false;
    break;
  }
 }

 if (code_bon == true)
 {
  lcd.setCursor (4,1); 
  lcd.print("Code bon");
  delay(1000);
  lcd.clear();
  lcd.print("Code:");
 }
 else
 {
  lcd.setCursor (2,1);
  lcd.print("Mauvais code");
  delay(1000);
  lcd.clear();
  lcd.print("Code:");
 }
}

char bouton_ok() 
{
  char bouton = NULL;
  do 
  {
    bouton = bouton_appuye ();
  }
  while (bouton == NULL);
  delay (100);

  while (bouton_appuye()!= NULL);
  delay (100);
  return bouton; 
}
  char bouton_appuye()
{
   const char boutons [4][4] = {'1','2','3','A',
                               '4','5','6','B',
                               '7','8','9','C',
                               '*','0','#','D'};

     for (int ligne=0;ligne<4;ligne++)
     { 
       digitalWrite(ligne_0, ligne == 0 ? LOW : HIGH);
       digitalWrite(ligne_1, ligne == 1 ? LOW : HIGH);
       digitalWrite(ligne_2, ligne == 2 ? LOW : HIGH);
       digitalWrite(ligne_3, ligne == 3 ? LOW : HIGH);

       int etat_colonne_0 = digitalRead(colonne_0);
       int etat_colonne_1 = digitalRead(colonne_1);
       int etat_colonne_2 = digitalRead(colonne_2);
       int etat_colonne_3 = digitalRead(colonne_3);

        if (etat_colonne_0 == LOW)
       { 
         return boutons [ligne][0];
       }
        if (etat_colonne_1 == LOW)
       { 
         return boutons [ligne][1];
       }
        if (etat_colonne_2 == LOW)
       { 
         return boutons [ligne][2];
       }
       if (etat_colonne_3 == LOW)
       { 
         return boutons [ligne][3];
       }
    }

    return NULL;
}

il suffit d'associer à un code connu le moment de la dernière distribution.

Une structure serait adaptée:

struct t_personne {
  const char* nom;
  const char* code;
  uint32_t momentDerniereDistribution;  // temps unix par exemple 0 = le "début des temps"
};

// définition des personnes connues
t_personne employes[] = {
  {"Julie", "1234AB", 0},
  {"Paul", "4537C", 0},
  {"Marie", "ABCD12", 0},
  {"Jean", "789843ABC", 0},
};

const byte nombreEmployes = sizeof employes / sizeof employes[0];

Lorsque un code est rentré, vous vérifiez s'il existe et vous comparez la date de dernière distribution avec l'heure courante et si c'est plus de 24 après, c'est bon, vous distribuez le produit et notez l'heure pour ce code dans momentDerniereDistribution, sinon vous appelez la police :slight_smile:

Pour gérer un flux asynchrone genre keypad (ou port série) vous pouvez jeter un oeil à mon petit tuto sur le sujet

Pour gérer le temps, une RTC (DS3231 par exemple) serait bien pratique. la bibliothèque adafruit/RTClib vous sera utile.

24H est trop restrictif.
Je dirais plutôt : distribution autorisée si la dernière distribution a été fait le jour d'avant.

@Morgane15
Tu fais des comparaisons de chaînes de caractères, caractères par caractère.
Avec des C strings (terminées par ZÉRO, donc il faut un octet de plus) et strcmp() ce serait plus facile et lisible.

Il existe une librairie nommée keypad qui t'aurait épargné la gestion du clavier, mais bon c'est déjà fait.
Je n'ai pas regardé dans les détails mais je me pose quand même la question des anti-rebonds ...

Pour un démonstrateur et puisque tu prends un temps de blocage assez court, tu peux faire une fausse gestion d'heure en utilisant millis() comme heure locale.
Lorsque l'employé prend son flacon tu enregistres la valeur courante de millis() dans l'élément momentDerniereDistribution de la structure proposée par J-M-L.
Lorsqu'il représente son code, tu fais la différence entre millis() et la valeur sauvegardée pour voir si la durée de blocage est dépassée.

24H est trop restrictif.

On peut ruser : considérer que la distribution est autorisée si 12H se sont écoulées depuis la dernière.
Les employés travaillent rarement plus de 12H. Et dans ce cas millis() est utilisable.

Sauf s'il y a des gens qui travaillent en 3/8 ...

hbachetti:
Les employés travaillent rarement plus de 12H. Et dans ce cas millis() est utilisable.

millis() peut représenter 50 jours... je ne comprends pas l'histoire de 12h

Exemple : si un employé est arrivé à 8H30 hier et arrive à 8H aujourd'hui, avec 24H de délai cela ne marche pas.
Avec 12 ou 18 c'est OK.

Je ne comprend pas pourquoi ça ne marche pas ? (Si l’arduino n’a pas été redémarré bien sûr)

Si un employé est arrivé à 8H30 hier, pour avoir sa distribution aujourd'hui, il devra entrer son code aussi à 8H30, donc 24H après, pas avant.

ah OK - je ne comprenais pas pourquoi 12 ou 18. Effectivement un petite tolérance sera nécessaire si la personne arrive tous les matins à la même heure.

20h 22h ou 23h30 pourrait aussi marcher alors, ce n'est pas lié à millis()

Ce n'est pas lié à millis().
20h 22h ou 23h30 pourraient marcher. Si l'heure d'arrivée est variable il faut adopter une tolérance égale à la plage variable. Exemple: 22H si la plage est de 2H.

Si le secteur est fiable, millis() + données en RAM suffiront. Sinon, il faudra RTC + données en EEPROM.

L'intérêt de millis() est qu'aucune mise à l'heure n'est nécessaire.

(j'espère que vous n'aurez pas ce message 2x, j'ai fait une erreur de manipulation je pense que je l'ai supprimé donc je recommence)
Tout d'abord merci à tous pour l'aide que vous m'apportez, c'est vraiment gentil ! J'essaie de comprendre la première solution mais c'est vraiment pas facile, je n'ai que les bases en Arduino... Donc petit à petit je regarde ce qu'est le "Struct", "const char^*", "uint32_t" etc pour espérer comprendre tout ça. Mais même sans encore comprendre, une question me vient, je vais devoir mettre cela dans le void loop, setup ou à part? Et du coup je dois supprimer toutes mes lignes de codes qui créent les tableaux avec les codes d'accès dans mon void loop, mais si ces codes sont supprimés, comment je peux encore noter mon "Code bon" ou "Mauvais code" car c'est directement lié à la lecture de ceux-ci ?

Morgane15:
Mais même sans encore comprendre, une question me vient, je vais devoir mettre cela dans le void loop, setup ou à part?

Une structure c’est un moyen de regrouper des variables dans un nouveau type auquel on donne un nom. Ici j’ai créé le type t_personne qui regroupe 3 attributs, un nom et un code qui sont des pointeurs vers du texte constant et un entier momentDerniereDistribution que l’on peut modifier.

Une fois le type défini, on crée un tableau de ce Type pour déclarer tous les employés et on le remplit.

Tenez pour vous mettre le pied à l’étrier, exécutez ce code:

struct t_personne {
  const char* nom;
  const char* code;
  uint32_t momentDerniereDistribution;  // temps unix par exemple 0 = le "début des temps"
};

// définition des personnes connues
t_personne employes[] = {
  {"Julie", "1234AB", 0},
  {"Paul", "4537C", 10},
  {"Marie", "ABCD12", 20},
  {"Jean", "789843ABC", 30},
};

// on compte combien on connait de personnes (ici ce sera 4:)
const byte nombreEmployes = sizeof employes / sizeof employes[0];


void setup() {
  Serial.begin(115200);
  for (byte i = 0; i < nombreEmployes; i++) {
    Serial.print("employes["); 
    Serial.print(i);    // le N° de l'employé dans la liste
    Serial.print("] = "); 
    Serial.print( employes[i].nom); // le nom de l'employé
    Serial.print(" (code: ");
    Serial.print( employes[i].code); // le code de l'employé
    Serial.print(") utilisé la dernière fois à millis= "); 
    Serial.println( employes[i].momentDerniereDistribution);
  }
}

void loop() {}

Dans le moniteur série (ouvert à 115200 bauds) vous devriez voir

[color=purple]
employes[0] = Julie (code: 1234AB) utilisé la dernière fois à millis= 0
employes[1] = Paul (code: 4537C) utilisé la dernière fois à millis= 10
employes[2] = Marie (code: ABCD12) utilisé la dernière fois à millis= 20
employes[3] = Jean (code: 789843ABC) utilisé la dernière fois à millis= 30
[/color]

les 0/10/20/30 que vous voyez sont bien les valeurs rangées dans le tableau lors de sa déclaration.

ça devrait vous donner une idée de comment accéder aux différents employés déclarés dans le tableau et les attributs comme leur nom, code etc…

si plus tard dans la loop(), lors de la distribution vous faites employes[0].momentDerniereDistribution = millis();ça va mettre à jour le champ momentDerniereDistribution pour Julie (qui est dans la première case du tableau - les indices commençant à 0)

Merci beaucoup pour ces explications complètes @J-M-L! Je viens d'apprendre plein de choses :slight_smile: J'ai maintenant bien compris cette structure. J'ai juste modifié cette ligne:

const byte nombreEmployes = sizeof employes / sizeof employes[0];

J'ai juste mis que la variable nombreEmployes valait 3 car je ne vais pas en faire plus, mais c'est un petit détail :stuck_out_tongue:

Par contre j'ai encore quelques points flous, par exemple dans cette partie de code:

t_personne employes[] = {

  {"Julie", "1234", 0},

  {"Paul", "ABCD", 10},

  {"Marie", "*234", 20},

}

Je ne comprends pas bien le fonctionnement de la dernière valeur de millis(), si j'ai bien compris je dois mettre dans le void loop

 employes[0].momentDerniereDistribution = millis();

En fait, quand je vais taper mon digicode au clavier, s'il correspond bien à celui défini pour l'employé de la case 0, je dois appeler cette ligne de code qui va aller réinitialiser la valeur dans le tableau du void setup et j'aurai donc

t_personne employes[] = {

  {"Julie", "1234", "valeur du nouveau millis(), par exemple 10000"},

  {"Paul", "ABCD", 10},

  {"Marie", "*234", 20},

}

Ensuite je vais devoir trouver une formule qui dit que j'aurai besoin de cette nouvelle valeur de millis() + 30s si je veux pouvoir refaire le code?

J'ai juste mis que la variable nombreEmployes valait 3 car je ne vais pas en faire plus, mais c'est un petit détail :stuck_out_tongue:

Pourquoi ? Tu ne fais pas confiance au compilateur pour faire correctement une division ?

 employes[0].momentDerniereDistribution = millis();

ou employes[1] ou employes[2] en fonction du code reconnu.

Ensuite je vais devoir trouver une formule qui dit que j'aurai besoin de cette nouvelle valeur de millis() + 30s si je veux pouvoir refaire le code?

millis() devra être supérieur à employes.momentDerniereDistribution + 30000.

Si si je lui fais pleinement confiance et d'ailleurs j'ai rechangé pour mettre la ligne de code originale pour faire vraiment sérieux :slight_smile:
Je vais adapter mon programme du coup, merci pour cette réponse !

C'est vraiment gentil de prendre votre temps pour aider des inconnu(e)s, merci mille fois

Bonjour, j’ai donc essayé cette solution maintenant que j’ai bien compris l’idée mais j’ai un dernier souci. Je dois donc comparer les codes que j’ai défini dans la structure au code qui est tapé sur mon keypad. J’ai donc voulu faire ça:

struct codesaisi {
  const char* code;
};

Pour définir ma structure contenant le code qu’on tape au keypad
Ensuite j’ai fait cela pour créer un tableau qui va contenir les 4 caractères du code tapé au keypad et ensuite le mettre dans ma structure:

int taille_codes = 4; 
char code_tape[taille_codes] = {0}; 
for (int nb_caract=0; nb_caract < taille_codes; nb_caract++)
    {
    code_tape [nb_caract] = bouton_ok();
    }
 codesaisi codedonne[]= { {code_tape[]}} ;

Et ensuite j’ai terminé par comparer ces 2 codes

boolean code_bon = false; 


 for (int j = 0; j < nombreEmployes; j++)      
 {
  if (employes[j].code = codedonne.code)
  {
    code_bon = true;
   }
 }

Cependant ca ne fonctionne pas, il ne veut pas accepter cette ligne de code

codesaisi codedonne[]= { {code_tape[]}} ;

Et ce que cela vient du fait que je suis obligée de mettre une valeur bien définie dans ma strucuture et pas une variable? Si c’est ça, comment je peux faire pour reprendre le code tapé et en créer quelque chose qui est accepté par ma structure?

en C ou C++ on ne peut pas affecter un tableau à un autre tableau, il faut copier les éléments un par un

un test sur un type de base se fait avec == pas =, donc ça ce n'est pas bon  if (employes[j].code = codedonne.code)déjà à cause du = au lieu de == mais pour comparer 2 chaînes de caractères (terminées par un caractère nul) il faut utiliser la fonction strcmp()

(re)lisez mon gérer un flux asynchrone genre keypad (ou port série) et regardez le dernier code publié qui traite d'une saisie sur le keypad et de comparaison. c'est comme cela que vous devriez procéder

J'ai relu votre article et effectivement je n'ai plus pensé à cette partie d'explication ! Merci pour ces superbes explications :slight_smile: donc si j'ai bien tout compris je vais devoir faire

If (! Strcmp(employes[0], code_tape))

Et ensuite pareil avec employé 1,2,3 ? Parce qu'après il faut que je ne bloque que le bon utilisateur, pas tous les autres donc il faut que je reconnaisse lequel est désigné.
Mais je ne suis pas certaine car je ne prends qu'une partie de ma structure et je ne sais pas si je peux me permettre de faire cela ?

Il faut bien sûr faire une boucle et il faut aussi prendre le champ code dans la structure pour la comparaison

for (byte i = 0; i < nombreEmployes; i++) { 
  if (! strcmp(employes[i].code, code_tape)) {
    // trouvé le bon
    ...
  }
}

attention aux majuscules, le langage y est sensible