Go Down

Topic: Gestion code secret utilisateur et administrateur avec un Keypad et EEPROM (Read 164 times) previous topic - next topic

J-M-L

Il faut mettre la console série à 115200 bauds, ça marche avec un clavier 4x4 mais facilement adaptable



Code: [Select]
// ----------------------------------------------------------------------
// DEMONSTRATION DE KEYPAD + EEPROM
// AUTEUR: J-M-L POUR LE FORUM ARDUINO https://forum.arduino.cc/index.php?topic=667881.msg4497540#msg4497540
// version 1.0: 2020/03/01
//
// GESTION D'UN CODE UTILISATEUR ET ADMINISTRATEUR AVEC UN KEYPAD
// L'ENTREE DU BON MOT DE PASSE UTILISATEUR EST DETECTEE
// L'ENTREE DU MOT DE PASSE ADMIN LANCE UN MENU SIMPLE QUI PERMET DE SAISIR
// DE NOUVEAUX MOTS DE PASSE QUI SERONT SAUVÉS EN EEPROM
//
// BSD license, all text above must be included in any redistribution
// ----------------------------------------------------------------------

const char marqueurDeFin = '#';
const char* codeUtilisateurParDefaut = "123";
const char* codeAdministrateurParDefaut = "BAD"; // en tapant #BAD# (ou juste BAD# en fait mais le premier # commmence la saisie) on peut changer le mot de passe
const uint8_t longeurMaxCodeSecret = 10;

// Ce qu'on veut mémoriser en EEPROM se trouve dans cette structure
struct __attribute__ ((packed)) _params {
  char codeUtilisateur[longeurMaxCodeSecret + 1]; // +1 pour le \0
  char codeAdministrateur[longeurMaxCodeSecret + 1]; // +1 pour le \0
} mesInformations;


// ------------------ GESTION KEYPAD ------------------
#include <Keypad.h> // https://playground.arduino.cc/Code/Keypad/

const byte ROWS = 4; //4 rangées
const byte COLS = 4; //4 colonnes

char keys[ROWS][COLS] = {   //les symboles, déclarés par leur code ASCII dans le tableau à 2 dimension
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte rowPins[ROWS] = {7, 6, 5, 4}; // On connecte ROW0, ROW1, ROW2 and ROW3 à ces pins
byte colPins[COLS] = {11, 10, 9, 8}; // On connecte  COL0, COL1, COL2 and COL3 à ces pins

Keypad membraneKeypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS);  //On initialise une instance de la classe Keypad

const byte tailleMessageMax = 100;
char code[tailleMessageMax + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'

boolean codeDisponible()
{
  static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
  boolean codeEnCours = true;

  char c = membraneKeypad.getKey();
  if (c != NO_KEY) {
    if (c == marqueurDeFin) {
      code[indexMessage] = '\0'; // on termine la c-string
      indexMessage = 0; // on se remet au début pour la prochaine fois
      codeEnCours = false;
    } else if (indexMessage <= tailleMessageMax - 1) code[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
  }
  return !codeEnCours;
}

// ------------------ GESTION MENU ADMIN ------------------

void imprimeMenu()
{
  imprimeInformations();
  Serial.println(F("\n******* MENU ADMINISTRATEUR *******"));
  Serial.println(F("1\tCHANGER CODE ADMINISTRATEUR"));
  Serial.println(F("2\tCHANGER CODE UTILISATEUR"));
  Serial.write(marqueurDeFin);  Serial.println(F("\tRETOUR"));
}

bool codeAdministrateur()
{
  bool nouveauCode = false;
  bool attenteCommande = true;
  uint32_t chrono = millis(); // si on ne fait rien pendant 10 secondes, on sort du mode
  Serial.println(F("\n*** CHANGER CODE ADMINISTRATEUR ***"));
  Serial.print(F("Entrez le nouveau code suivi de ")); Serial.println(marqueurDeFin);
  Serial.write(marqueurDeFin);  Serial.println(F("\tRETOUR"));

  while (attenteCommande && (millis() - chrono <= 10000UL)) {
    if (codeDisponible()) {
      if (*code) { // si le code n'est pas vide (idem que if (code[0] != '\0') {...}
        memset(mesInformations.codeAdministrateur, '\0', sizeof(mesInformations.codeAdministrateur));
        strncpy(mesInformations.codeAdministrateur, code, longeurMaxCodeSecret);
        sauveInformations();
        Serial.print(F("Nouveau code administrateur:"));
        Serial.println(mesInformations.codeAdministrateur);
        attenteCommande = false;
        nouveauCode = true;
      }
    }
  }
  return nouveauCode;
}


bool codeUtilisateur()
{
  bool nouveauCode = false;
  bool attenteCommande = true;
  uint32_t chrono = millis(); // si on ne fait rien pendant 10 secondes, on sort du mode
  Serial.println(F("\n**** CHANGER CODE UTILISATEUR *****"));
  Serial.print(F("Entrez le nouveau code suivi de ")); Serial.println(marqueurDeFin);
  Serial.write(marqueurDeFin);  Serial.println(F("\tRETOUR"));

  while (attenteCommande && (millis() - chrono <= 10000UL)) {
    if (codeDisponible()) {
      if (*code) { // si le code n'est pas vide (idem que if (code[0] != '\0') {...}
        memset(mesInformations.codeUtilisateur, '\0', sizeof(mesInformations.codeUtilisateur));
        strncpy(mesInformations.codeUtilisateur, code, longeurMaxCodeSecret);
        sauveInformations();
        Serial.print(F("Nouveau code utilisateur:"));
        Serial.println(mesInformations.codeUtilisateur);
        attenteCommande = false;
        nouveauCode = true;
      }
    }
  }
  return nouveauCode;
}

void administration()
{
  bool attenteCommande = true;
  uint32_t chrono = millis(); // si on ne fait rien pendant 10 secondes, on sort du mode admin

  imprimeMenu();
  while (attenteCommande && (millis() - chrono <= 10000UL)) {
    char c = membraneKeypad.getKey();
    if (c != NO_KEY) {
      chrono = millis(); // on a eu de l'activité
      switch (c) {
        case '1':
          if (codeAdministrateur()) { // le code a été changé
            chrono = millis(); // on a eu de l'activité
            imprimeMenu();
          }
          break;
        case '2':
          if (codeUtilisateur()) { // le code a été changé
            chrono = millis(); // on a eu de l'activité
            imprimeMenu();
          }

          break;
        case marqueurDeFin: // sortie du menu
          attenteCommande = false;
          break;
      }
    }
  }
  Serial.println(F("*** FIN SESSION ADMINISTRATEUR ****\n"));
}

// ------------------ GESTION EEPROM ------------------

#include <EEPROM.h>

const uint32_t motSentinelle = 0xDEADBEEF;
const uint16_t adresseSentinelle = 0x00;
const uint16_t adresseDesInformations = adresseSentinelle + sizeof(motSentinelle);

void imprimeInformations()
{
  Serial.println(F("\n************* PARAMETRES *************"));
  Serial.print(F("Code Secret =\t")); Serial.println(mesInformations.codeUtilisateur);
  Serial.print(F("Code Admin =\t")); Serial.println(mesInformations.codeAdministrateur);
}

void resetInformations()
{
  uint32_t mauvaisMotSentinelle = 0xBADBAD;
  EEPROM.put(adresseSentinelle, mauvaisMotSentinelle);
}

void sauveInformations()
{
  EEPROM.put(adresseSentinelle, motSentinelle);
  EEPROM.put(adresseDesInformations, mesInformations);
}

void etablirInformations()
{
  uint32_t tmpKey;

  EEPROM.get(adresseSentinelle, tmpKey);
  if (tmpKey == motSentinelle) {
    EEPROM.get(adresseDesInformations, mesInformations);    // L'EEPROM était déjà initialisée, OK pour lire
  } else {
    // premier usage sur cet Arduino on met le mot de passe par défaut
    memset(mesInformations.codeUtilisateur, '\0', sizeof(mesInformations.codeUtilisateur));
    strncpy(mesInformations.codeUtilisateur, codeUtilisateurParDefaut, longeurMaxCodeSecret);
    // ainsi que le code Admin
    memset(mesInformations.codeAdministrateur, '\0', sizeof(mesInformations.codeAdministrateur));
    strncpy(mesInformations.codeAdministrateur, codeAdministrateurParDefaut, longeurMaxCodeSecret);
    sauveInformations();
  }
}

// ------------------ PROGRAMME PRINCIPAL ------------------

void setup()
{
  Serial.begin(115200);
  // resetInformations(); // à décommenter si vous souhaitez ne pas lire l'EEPROM (à recommenter ensuite)
  etablirInformations(); // initialise la structure avec soit le mot de passe par défaut, soit celui qui était déjà en EEPROM si dispo
  Serial.write(marqueurDeFin);  Serial.println(F(" pour confirmer"));
}

void loop()
{
  if (codeDisponible()) { // on a tapé un #
    if (!strcmp(code, mesInformations.codeAdministrateur)) {
      administration();
    }
    else if (!strcmp(code, mesInformations.codeUtilisateur)) {
      Serial.print(code);
      Serial.println(F("\tCode correct"));
    }
    else if (*code) { // si le code n'est pas vide (idem que if (code[0] != '\0') {...}
      Serial.print(code);
      Serial.println(F("\tCode Inconnu"));
    }
  }
  // ici on peut faire autre chose
}
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

J-M-L

le fonctionnement est simplisme:

on tape un code qui se termine par '#'

si c'est le bon code utilisateur, on affiche que le message est reconnu, il vous suffit de piloter votre relais à cet endroit par exemple

si c'est un mauvais code, il y a un message d'erreur

si c'est le code admin, un menu s'affiche et on peut changer soit le mot de passe admin, soit le mot de passe utilisateur. les nouveaux codes sont alors mémorisés en EEPROM donc même en cas de coupure de courant les mots de passes sont sauvegardés.

il y a pour les développeurs une fonction  resetInformations(); à décommenter dans le setup() qui sert à invalider ce qu'il y a en mémoire et donc de rétablir les mots de passe par défaut (123 et BAD)

il faut avoir la console à 115200 bauds, il sera facile de mettre un LCD à la place cependant
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

Go Up