Gérer un lave linge en co-propriété - Réalisation du parfait NOOB

Non pas besoin, 'login' est ici un mot clé, comme 'update' ou tout autre mode clé que tu veux utiliser pour faire comprendre à ton serveur qu'il faut afficher une page web ou du texte lorsqu'il le reçoit.

ok.

oui je l'ai vu, je l'ai même mis dans mon précédent message, avec en comparaison ce qui est dans notre sketch. (message 160)

J'ai vérifié, on les a déjà, lignes 55 et 56

const char* http_username = "admin";
const char* http_password = "123";

Il faut créer une page login.htm pour connecter avec cette partie du sketch?
Il faut que je remonte le comparer avec la suggestion d'Henri pour la boite de dialogue en JS... mais ça me parait bien court pour être du même genre..

Je crois que ça crée la page automatiquement, mais teste le, c'est plus simple. Ça fait longtemps que je ne l'ai pas utilisé...

oui ça y est, j'ai réussi à obtenir une boite de dialogue... par contre, il faut que je trouve le moyen de mettre un timeout sur les cookies ou autre... car une fois identifié, ça ne te le redemande plus... et du coup c'est moins pratique pour faire des essais...

Il doit y avoir l'option de reseter les cookies du navigateur... mais j'aimerais éviter... :grimacing:

J'ai vu ca sur le github, je vais faire des essais...

Je vais aussi implémenter un server.on qui ferme les connexions inutilisées pour eviter que le serveur ne plante...
Je me souviens que yohann avait parlé de crashs occasionnels... ça pourrait être une raison, surtout si un petit malin à scanné le réseau... et rajoute maintenant des crédits, en plus de son admin sur place..

Exit pour l'idée d'utiliser ws.cleanupClients();
Il semble que ce soit dans le cas de l'utilisation de WebSockets... ce qui ne semble pas être le cas ici...
Il sera toujours possible d'y revenir plus tard si besoin...

Dans mon souvenir, ça me demandait une authentification à chaque fois.
Un tuto ici où il ajoute un bouton 'logout'

avec le server.on :

  server.on("/logout", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(401);
  });

Salut @lesept!
Merci ce lien :+1::sunglasses: très bonne idée un bouton logout!
J'y regarde dès que possible🤓 et je vous pour incorporer ça!

Là je suis en train de faire le tour du sujet de façon globale, htm, CSS, JS, HTcontrol, HTTPS, synchro du temps en tps, etc... Organisation des divers pages etc..

Je préfère attaquer cette partie de façon globale et réfléchie.. plutôt que de foncer tête baissée pour devoir ensuite faire marche arrière...

Mais oui le bouton logout c'est définitivement à rajouter!:beers:

J'ai fait aussi un peu d'organisation et de nettoyage de printemps dans le code car c'était raid pour s'y retrouver à force..

Poste le de temps en temps. Pour l'alléger, tu dois créer des fonctions et mettre celles qui sont bien validées et qui ne changeront pas dans un fichier à part (fichier '.h'). tu peux aussi organiser en plusieurs fichiers .h par thème.

oui oui c'était prévu, juste pas eu le temps de le faire aujourd'hui.

oui je dois faire le tour de la vidéo à ce sujet, au message #77..

Sans quoi pour le sketch, j'en suis là:
(Il faut me le dire si vous préféré que j'envoie le fichier ino )

/**
   ----------------------------------------------------------------------------
   ESP32 Lave_Linge_Federes
   ----------------------------------------------------------------------------
   https://forum.arduino.cc/t/gerer-un-lave-linge-en-co-propriete-realisation-du-parfait-noob/908070
   ----------------------------------------------------------------------------
*/

#include "AsyncTCP.h"
#include "ESPAsyncWebServer.h"
#include "SPIFFS.h"
#include "WiFi.h"
#include "time.h"
#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include <MFRC522.h>
#include <Preferences.h>
#include <Tone32.h>


// ----------------------------------------------------------------------------
// Definition of macros
// ----------------------------------------------------------------------------

#define nbUtilisateurs 31
#define MAXNAME 16
#define SS_PIN 4
#define RST_PIN 5
#define BUZZER_PIN 17
#define BUZZER_CHANNEL 0


// ----------------------------------------------------------------------------
// Definition of global constants
// ----------------------------------------------------------------------------

MFRC522 mfrc522(SS_PIN, RST_PIN);        // Create MFRC522 instance.
MFRC522::MIFARE_Key key;
LiquidCrystal_I2C lcd(0x27, 20, 4);

AsyncWebServer server(80);
AsyncEventSource events("/events"); // event source (Server-Sent events)

int freq = 2000;
int channel = 0;
int resolution = 8;
int led_rouge = 14;
int led_verte = 15;
int relais220v = 16;
const int version = 2110141030;

byte serNumLu[4] = {0};
struct Params {
  char nom[MAXNAME];
  uint32_t uID;
  /*Pour info
    Droits : regroupe plusieurs informations booléennes sur 8 bits
    numérotation des bits : 0b12345678
    bit 1: non utilisé
    bit 2: non utilisé
    bit 3: non utilisé
    bit 4: non utilisé
    bit 5: non utilisé
    bit 6: non utilisé
    bit 7: 1 = présent, 0 = absent
    bit 8: 1 = master,  0 = pas master
  */
  uint8_t droits;
  int nbUtils;
  int credit;
};

struct Params Utilisateur[nbUtilisateurs];
Preferences sauvegardeStudios;
int tagok = 255;

const char* ssid     = "Freebox-123456";
const char* password = "123456";
const char* http_username = "admin";
const char* http_password = "123";

const char* ntpServer1 = "fr.pool.ntp.org";
const char* ntpServer2 = "pool.ntp.org";
const long  gmtOffset_sec = 3600;
const int   daylightOffset_sec = 3600;



// ----------------------------------------------------------------------------
// SPIFFS initialisation
// ----------------------------------------------------------------------------
void initSPIFFS() {
  if (!SPIFFS.begin()) {
    Serial.println("Cannot mount SPIFFS Volume...");
    lcd.setCursor(0, 3);
    lcd.print("       ERR. SPIFFS!!");
    buzNon();
    delay(2000);
    lcd.clear();
    return;
  }
  lcd.setCursor(0, 3);
  lcd.print("         SPIFFS OK..");
  buzKey();
  delay(1000);
}


// ----------------------------------------------------------------------------
// WiFi Connexion réseau
// ----------------------------------------------------------------------------
void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  lcd.setCursor(0, 3);
  lcd.print("  Init. connection..");
  buzKey();
  while (WiFi.status() != WL_CONNECTED) {
    Serial.println("Connecting to WiFi..");
    digitalWrite(led_verte, HIGH);
    delay(500);
    digitalWrite(led_verte, LOW);
    digitalWrite(led_rouge, HIGH);
    delay(500);
    digitalWrite(led_rouge, LOW);
  }
  Serial.println(WiFi.localIP());
}

//disconnect WiFi when no longer needed
void WifiOff() {
  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);
}


// ----------------------------------------------------------------------------
// NPT Time sync.
// ----------------------------------------------------------------------------

// NTP time Init and get the time
//**********************************************************
void initNPTtime() {
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1);
  printLocalTime();
}

void printLocalTime() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }
  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
  Serial.print("Hour: ");
  Serial.println(&timeinfo, "%H");
  Serial.print("Hour (12 hour format): ");
  Serial.println(&timeinfo, "%I");
  Serial.println("Time variables");
  char timeHour[3];
  strftime(timeHour, 3, "%H", &timeinfo);
  Serial.println(timeHour);
  char timeWeekDay[10];
  strftime(timeWeekDay, 10, "%A", &timeinfo);
  Serial.println(timeWeekDay);
  Serial.println();
}

// ----------------------------------------------------------------------------
// Bibliothèque des sons du buzzer
// ----------------------------------------------------------------------------
//buzKey Bip
void buzKey () {
  tone(BUZZER_PIN, NOTE_D4, 100, BUZZER_CHANNEL);
  noTone(BUZZER_PIN, BUZZER_CHANNEL);
  delay (1000);
}
//buzOui Ding-Dong
void buzOui () {
  tone(BUZZER_PIN, NOTE_E6, 200, BUZZER_CHANNEL);
  noTone(BUZZER_PIN, BUZZER_CHANNEL);
  delay (70);
  tone(BUZZER_PIN, NOTE_C6, 500, BUZZER_CHANNEL);
  noTone(BUZZER_PIN, BUZZER_CHANNEL);
  delay (1000);
}
//buzNon Han-Han
void buzNon () {
  tone(BUZZER_PIN, NOTE_B1, 200, BUZZER_CHANNEL);
  noTone(BUZZER_PIN, BUZZER_CHANNEL);
  delay (70);
  tone(BUZZER_PIN, NOTE_B1, 500, BUZZER_CHANNEL);
  noTone(BUZZER_PIN, BUZZER_CHANNEL);
  delay (1000);
}
//buzStart 1234
void buzStart () {
  tone(BUZZER_PIN, NOTE_A4, 150, BUZZER_CHANNEL);
  tone(BUZZER_PIN, NOTE_D5, 200, BUZZER_CHANNEL);
  tone(BUZZER_PIN, NOTE_FS5, 200, BUZZER_CHANNEL);
  tone(BUZZER_PIN, NOTE_A5, 100, BUZZER_CHANNEL);
  noTone(BUZZER_PIN, BUZZER_CHANNEL);
  delay (90);
  tone(BUZZER_PIN, NOTE_FS5, 200, BUZZER_CHANNEL);
  tone(BUZZER_PIN, NOTE_A5, 900, BUZZER_CHANNEL);
  noTone(BUZZER_PIN, BUZZER_CHANNEL);
  delay(1000);
}
//buzStop 4321
void buzStop() {
  tone(BUZZER_PIN, NOTE_A5, 150, BUZZER_CHANNEL);
  tone(BUZZER_PIN, NOTE_FS5, 200, BUZZER_CHANNEL);
  tone(BUZZER_PIN, NOTE_D5, 200, BUZZER_CHANNEL);
  tone(BUZZER_PIN, NOTE_A4, 900, BUZZER_CHANNEL);
  noTone(BUZZER_PIN, BUZZER_CHANNEL);
  delay(3000);
}


// ----------------------------------------------------------------------------
// Manipulations en mémoire Flash
// ----------------------------------------------------------------------------

// initialise mem. flash et informe si erreur
//**********************************************************
void initStorage () {
  Serial.println("Initialisation de sauveguardesStudios en mémoire flash");
  if (sauvegardeStudios.begin("nomParam", false) == false) {
    Serial.println("ERROR OPENING DATA STORAGE");
    lcd.backlight();
    lcd.setCursor(0, 3);
    lcd.print("DATA STORAGE ERROR!!");
    buzNon();
    delay(2000);
    return;
  }
  Serial.println("sauveguardeStudios initialisé");
}

// vérifie si sauvegarde présente en mem. flash
//**********************************************************
void testStudio (int i) {
  Serial.println("Test testStudio() de sauvegardeStudios en mémoire flash.");
  initStorage ();
  //eraseAllFlash();    //utiliser pour effacer sauvegardeStudios et tester testStudios
  if (sauvegardeStudios.getType("Stu0UID") == PT_INVALID) {
    Serial.print("PAS DE SAUVEGARDE TROUVEE: création sauvegarde depuis initUsers()");
    lcd.setCursor(0, 3);
    lcd.print("    MISSING BACKUP!!");
    buzNon();
    delay(2000);
    initUsers();                                              //charger la table par défaut
    for (int i = 0; i < nbUtilisateurs; i++) sauveStudio (i); //créer une sauvegarde
    lcd.setCursor(0, 3);
    lcd.print("default table loaded");
    buzOui();
    delay(2000);
  }
  Serial.println("Sauveguarde données trouvée.");
  lcd.setCursor(0, 3);
  lcd.print("      backup found..");
  buzKey();
  delay(1000);
  sauvegardeStudios.end();
}

// fait une sauvegarde en mémoire flash
//**********************************************************
void sauveStudio (int i) {
  Serial.println("Sauveguarde sauveStudio() de sauvegardeStudios en mémoire flash.");
  initStorage ();
  char nomParam[15];
  // nom
  sprintf(nomParam, "Stu%dNm", i);                            //compose correct string into key nomParam, based on i
  sauvegardeStudios.putString(nomParam, Utilisateur[i].nom);  //store the name of Utilisateur[i].nom into sauvegardeStudios
  // uID
  sprintf(nomParam, "Stu%dUID", i);
  sauvegardeStudios.putUInt(nomParam, Utilisateur[i].uID);
  // droits
  sprintf(nomParam, "Stu%dEx", i);
  sauvegardeStudios.putUInt(nomParam, Utilisateur[i].droits);
  // nbUtils
  sprintf(nomParam, "Stu%dnbU", i);
  sauvegardeStudios.putInt(nomParam, Utilisateur[i].nbUtils);
  // credits
  sprintf(nomParam, "Stu%dCre", i);
  sauvegardeStudios.putInt(nomParam, Utilisateur[i].credit);
  sauvegardeStudios.end();
}

// réstauration de la sauvegarde en mem. flash
//**********************************************************
void restoreStudio (int i) {
  Serial.println("Réstauration restoreStudio() de sauvegardeStudios en mémoire flash.");
  initStorage ();
  char nomParam[15];
  // nom
  sprintf(nomParam, "Stu%dNm", i);                                    //compose correct string into key nomParam, based on i
  sauvegardeStudios.getString(nomParam, Utilisateur[i].nom, MAXNAME); //load name to Utilisateur[i].nom, from sauvegardeStudios using key nomParam
  //uID
  sprintf(nomParam, "Stu%dUID", i);
  Utilisateur[i].uID = sauvegardeStudios.getUInt(nomParam);
  // droits
  sprintf(nomParam, "Stu%dEx", i);
  Utilisateur[i].droits = sauvegardeStudios.getUInt(nomParam);
  // nbUtils
  sprintf(nomParam, "Stu%dnbU", i);
  Utilisateur[i].nbUtils = sauvegardeStudios.getInt(nomParam);
  // credit
  sprintf(nomParam, "Stu%dCre", i);
  Utilisateur[i].credit = sauvegardeStudios.getInt(nomParam);
  sauvegardeStudios.end();
}

// ATTENTION EFFACE TOUTE LA MEMOIRE FLASH!!!
//**********************************************************
void eraseAllFlash () {
  Serial.println("!! INITIALIZE ALL FLASH MEMORY !!");
  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print("! ! ! ! ! ! ! ! ! !");
  lcd.setCursor(0, 2);
  lcd.print("INITIALIZ.FLASH MEM");
  lcd.setCursor(0, 3);
  lcd.print("! ! ! ! ! ! ! ! ! !");
  buzNon();
  delay(3000);
  lcd.clear();
  initStorage ();
  sauvegardeStudios.clear();        //utiliser pour effacer sauvegardeStudios
  Serial.println("All flash memory now initialized...!");
  sauvegardeStudios.end();
}


// ----------------------------------------------------------------------------
// AsyncWebServer
// ----------------------------------------------------------------------------

//flag to use from web update to reboot the ESP
//**********************************************************
bool shouldReboot = false;

// Handle Unknown Request
//**********************************************************
void onRequest(AsyncWebServerRequest * request) {
  request->send(404);
}


// ----------------------------------------------------------------------------
// Pages du WebServer
// ----------------------------------------------------------------------------

// Envoi de la page html RECAP au server
//**********************************************************
void pageRecap () {
  char path[] = "/recap.htm";
  // Delete the file if it exists
  if (SPIFFS.exists(path)) SPIFFS.remove(path);
  // Prepare the file's content
  File file = SPIFFS.open(path, FILE_WRITE);
  if (!file) Serial.printf("\n\n\nProbleme ouverture fichier recap.htm !!!\n\n\n");
  // Copy the content of begin.htm to index.htm
  File fileHead = SPIFFS.open("/begin.htm", FILE_READ);
  while (fileHead.available()) file.write(fileHead.read());
  fileHead.close();
  //
  file.println("<table>");
  file.printf("<tr><th>Appt.</th><th>Nom</th><th>Badge</th><th>Credits</th><th>Consommes</th><th>Droits</th>");
  for (int i = 0; i < nbUtilisateurs; i++) {
    file.printf("<tr><th>%d</th><th>%s</th><th>%X</th><th>%d</th><th>%d</th><th>%d</th></tr>", i, Utilisateur[i].nom, Utilisateur[i].uID, Utilisateur[i].credit, Utilisateur[i].nbUtils, Utilisateur[i].droits);
  }
  file.println("</table>");
  file.printf("  <p><h2><a class=\"button ramm\" href=\"/master\">Retour</a></h2>");
  File fileEnd = SPIFFS.open("/End.htm", FILE_READ);
  while (fileEnd.available()) file.write(fileEnd.read());
  fileEnd.close();
  file.close();
  // Verify file content
  File file2 = SPIFFS.open(path, FILE_READ);
  while (file2.available()) Serial.write(file2.read());
  file2.close();
}

// Envoi de la page html INDEX au server
//**********************************************************
void index () {
  char path[] = "/index.htm";
  // Delete the file if it exists
  if (SPIFFS.exists(path)) SPIFFS.remove(path);
  // Prepare the file's content
  File file = SPIFFS.open(path, FILE_WRITE);
  if (!file) Serial.printf("\n\n\nProbleme ouverture fichier index.htm !!!\n\n\n");
  // Copy the content of begin.htm to index.htm
  File fileHead = SPIFFS.open("/begin.htm", FILE_READ);
  while (fileHead.available()) file.write(fileHead.read());
  fileHead.close();

  //Button "master"
  file.printf("  <p><h2><a class=\"button mas\" href=\"/master\">MASTER</a></h2>");

  //Buttons "studio/x"
  for (int i = 1; i < nbUtilisateurs; i++)
    file.printf("  <p><h2><a class=\"button stu\" href=\"/studio?Id=%d\">CARTE %d</a></h2>", i, i);

  //Button "reset"
  file.printf("  <p><h2><a class=\"button res\" href=\"/res\">Reset</a></h2>");
  File fileEnd = SPIFFS.open("/End.htm", FILE_READ);
  while (fileEnd.available()) file.write(fileEnd.read());
  fileEnd.close();
  file.close();
}

// Envoi de la page html MASTER au server
//**********************************************************
void masterhtm () {
  char path[] = "/master.htm";
  // Delete the file if it exists
  if (SPIFFS.exists(path)) SPIFFS.remove(path);
  // Prepare the file's content
  File file = SPIFFS.open(path, FILE_WRITE);
  if (!file) Serial.printf("\n\n\nProbleme ouverture fichier master.htm !!!\n\n\n");
  // Copy the content of begin.htm to master.htm
  File fileHead = SPIFFS.open("/begin.htm", FILE_READ);
  while (fileHead.available()) file.write(fileHead.read());
  fileHead.close();

  // Boutons de le page Master :
  //Buttons "Sauvegarde"
  file.printf("  <p><h2><a class=\"button stu\" href=\"/sauv\">Sauvegarde</a></h2>");

  //Buttons "Restitution des données"
  file.printf("  <p><h2><a class=\"button stu\" href=\"/resti\">Restitution des donnees</a></h2>");

  //Buttons "Restitution des DONNEES DE DEFAUT !!!"
  file.printf("  <p><h2><a class=\"button stu\" href=\"/restidefault\">Restitution des DONNEES DE DEFAUT !!!</a></h2>");

  //Buttons "historique"
  file.printf("  <p><h2><a class=\"button stu\" href=\"/recap\">Historique</a></h2>");

  //Buttons "Retour au menu d'accueil"
  file.printf("  <p><h2><a class=\"button ram\" href=\"/\">Retour au menu accueil</a></h2>");

  // Append the content of End.htm to status.htm
  File fileEnd = SPIFFS.open("/End.htm", FILE_READ);
  while (fileEnd.available()) file.write(fileEnd.read());
  fileEnd.close();
  file.close();
}

// Envoi de la page html STUDIO au server
//**********************************************************
void pageStudio (int numStudio) {
  char path[] = "/studio.htm";
  // Delete the file if it exists
  if (SPIFFS.exists(path)) SPIFFS.remove(path);
  // Prepare the file's content
  File file = SPIFFS.open(path, FILE_WRITE);
  if (!file) Serial.printf("\n\n\nProbleme ouverture fichier studio.htm !!!\n\n\n");
  // Copy the content of begin.htm to index.htm
  File fileHead = SPIFFS.open("/begin.htm", FILE_READ);
  while (fileHead.available()) file.write(fileHead.read());
  fileHead.close();
  //
  //Button "remettre 1 crédit"
  file.printf("  <p><h2><a class=\"button res\" href=\"/plus1?Id=%d\">Ajouter 1 crédit</a></h2>", numStudio);

  //Button "remettre 5 crédits"
  file.printf("  <p><h2><a class=\"button res\" href=\"/plus5?Id=%d\">Ajouter 5 crédits</a></h2>", numStudio);

  //Button "remettre 10 crédits"
  file.printf("  <p><h2><a class=\"button res\" href=\"/plus10?Id=%d\">+Ajouter 10 crédits</a></h2>", numStudio);

  //Button "remettre à 0 crédit"
  file.printf("  <p><h2><a class=\"button res\" href=\"/raz?Id=%d\">Remise a 0</a></h2>", numStudio);

  //Button "changement de carte cdc"
  file.printf("  <p><h2><a class=\"button stu\" href=\"/cdc?Id=%d\">Changement de carte</a></h2>", numStudio);

  //Button "désactiver"
  file.printf("  <p><h2><a class=\"button mas\" href=\"/des?Id=%d\">Désactiver le badge</a></h2>", numStudio);

  //Buttons "retour au menu"
  file.printf("  <p><h2><a class=\"button ram\" href=\"/\">Retour au menu accueil</a></h2>");

  //historique
  File fileEnd = SPIFFS.open("/End.htm", FILE_READ);
  while (fileEnd.available()) file.write(fileEnd.read());
  fileEnd.close();
  file.close();

  // Verify file content
  file = SPIFFS.open(path, FILE_READ);
  while (file.available()) Serial.write(file.read());
  file.close();
}


// ----------------------------------------------------------------------------
// Manipulations liées aux badges
// ----------------------------------------------------------------------------

// Action lors du passage de badges connus
//**********************************************************
void user (int numero) {
  Serial.print("Badge reconnu");
  Serial.println(numero);
  digitalWrite(led_verte, HIGH);
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Badge reconnu");
  lcd.setCursor(4, 2);
  lcd.print("ID numero");
  lcd.setCursor(14, 2);
  lcd.print(numero);
  buzKey();
  delay (3000);
  lcd.clear();
  digitalWrite(led_verte, LOW);
  //affichage moniteur nbre de crédits restant
  //delay (500);
  //lcd.clear();
  if (Utilisateur[numero].credit >= 1) {
    lcd.setCursor(0, 0);
    lcd.print("Nettoyage autorise! ");
    lcd.setCursor(0, 1);
    lcd.print(" 1 credit utilise,  ");
    lcd.setCursor(0, 2);
    lcd.print(" il vous reste:     ");
    lcd.setCursor(3, 3);
    lcd.print(Utilisateur[numero].credit - 1);
    ++Utilisateur[numero].nbUtils ;                //ligne de commande pour ajouter 1 au nombre d 'utilsations
    --Utilisateur[numero].credit;                  //ligne de commande pour soustraire 1 au nombre de crédits
    for (int i = 0; i < nbUtilisateurs; i++) sauveStudio (i);
    lcd.setCursor(8, 3);
    lcd.print("credit(s)");
    buzOui();
    Serial.print (Utilisateur[numero].nbUtils);    //affiche moniteur le nombre d'utilisations
    Serial.print (" utilisations,credit  :");      //affiche moniteur utilisation,crédit :
    Serial.println(Utilisateur[numero].credit);
    digitalWrite(led_verte, HIGH);
    delay(3000);
    lcd.clear();
    lcd.setCursor(0, 1);
    lcd.print("  Debut du cycle!");
    buzStart();
    digitalWrite(led_verte, LOW);
    digitalWrite(led_rouge, HIGH);
    digitalWrite(relais220v, HIGH);                //Activation du lave linge sur le relais
    //    delay (1000);                            //utiliser si latching ou tempo
    //    digitalWrite(relais220v, LOW);
    delay(3000);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Lavage en cours");
    lcd.setCursor(0, 2);
    lcd.print(" (Cycle de 2 hrs10)");
    delay (10000); //durée du cycle du lave linge
    //    digitalWrite(relais220v, HIGH);         //utiliser si latching ou tempo
    //    delay (1000);
    digitalWrite(relais220v, LOW);
    digitalWrite(led_rouge, LOW);
    buzKey();
    lcd.clear();
    lcd.setCursor(0, 1);
    lcd.print("    FIN DU CYCLE");
    lcd.setCursor(0, 3);
    lcd.print("   Mise en sommeil..");
    buzStop();
    delay(5000);
    lcd.clear();
    lcd.noBacklight();
  }
  else  {  // actions si plus de crédit
    lcd.clear();
    digitalWrite(led_rouge, LOW);
    digitalWrite(led_rouge, HIGH);
    lcd.setCursor(0, 0);
    lcd.print("   CREDIT DEPASSE");
    buzNon();
    delay(1500);
    lcd.setCursor(0, 1);
    lcd.print("   POUR RECHARGER");
    lcd.setCursor(0, 2);
    lcd.print("     contactez");
    lcd.setCursor(0, 3);
    lcd.print(" votre administreur");
    delay(7000);
    digitalWrite(led_rouge, LOW);
    lcd.clear();
    lcd.noBacklight();
  }
}

// Action lors du passage du badge master
//**********************************************************
void master () {
  Serial.print("Badge Master");
  lcd.backlight();
  lcd.setCursor(0, 1);
  lcd.print("   ACCES AUTORISE");
  lcd.setCursor(0, 2);
  lcd.print("   !SALUT Master!");
  buzStart();
  delay(3000);
  lcd.clear();
  user(tagok);
}

// Action lors du passage du badge inconnu
//**********************************************************
void inconnu () {
  Serial.println("Badge non reconnu");
  digitalWrite(led_rouge, HIGH);
  lcd.clear();
  lcd.backlight();
  lcd.setCursor(0, 1);
  lcd.print("    ACCES REFUSE");
  buzNon();
  delay(3000);
  digitalWrite(led_rouge, LOW);
  lcd.clear();
  lcd.noBacklight();
}


// ----------------------------------------------------------------------------
// Table de réinitialisation par défaut
// ----------------------------------------------------------------------------

void initUsers() {
  //ini le tableau des badges
  //PAR DEFAUT si première utilisation ou perte de sauvegarde
  //ou demandé depuis la page web 'master'
  // Master (badge numéro 0 == master)
  // Utilisateur 0
  strcpy(Utilisateur[0].nom, "MASTER");
  Utilisateur[0].uID = 0x03E816E3;
  Utilisateur[0].droits = 0b00000011; // finit par 11 == badge présent, et master
  Utilisateur[0].nbUtils = 0;
  Utilisateur[0].credit = 10000;

  // Utilisateur 1
  strcpy(Utilisateur[1].nom, "Carte 1");
  Utilisateur[1].uID = 0x17E98F9C;
  Utilisateur[1].droits = 0b00000010; // finit par 10 == locataire présent, pas master
  Utilisateur[1].nbUtils = 0;
  Utilisateur[1].credit = 0;

  // Utilisateur 2
  strcpy(Utilisateur[2].nom, "Carte 2");
  Utilisateur[2].uID = 0x68307CCA;
  Utilisateur[2].droits = 0b00000010;
  Utilisateur[2].nbUtils = 0;
  Utilisateur[2].credit = 0;

  // Utilisateur 3
  strcpy(Utilisateur[3].nom, "Carte 3");
  Utilisateur[3].uID = 0x036799C4;
  Utilisateur[3].droits = 0b00000010;
  Utilisateur[3].nbUtils = 0;
  Utilisateur[3].credit = 0;

  // Utilisateur 4
  strcpy(Utilisateur[4].nom, "Carte 4");
  Utilisateur[4].uID = 0x0367CB75;
  Utilisateur[4].droits = 0b00000010;
  Utilisateur[4].nbUtils = 0;
  Utilisateur[4].credit = 0;

  // Utilisateur 5
  strcpy(Utilisateur[5].nom, "Carte 5");
  Utilisateur[5].uID = 0x02AD7AD1;
  Utilisateur[5].droits = 0b00000010;
  Utilisateur[5].nbUtils = 0;
  Utilisateur[5].credit = 0;

  // Utilisateur 6
  strcpy(Utilisateur[6].nom, "Carte 6");
  Utilisateur[6].uID = 0x3D3314CB;
  Utilisateur[6].droits = 0b00000000; // finit par 00 : a quitté l'immeuble, pas master
  Utilisateur[6].nbUtils = 0;
  Utilisateur[6].credit = 0;

  // Utilisateur 7
  strcpy(Utilisateur[7].nom, "Carte 7");
  Utilisateur[7].uID = 0x3D366CAB;
  Utilisateur[7].droits = 0b00000000;
  Utilisateur[7].nbUtils = 0;
  Utilisateur[7].credit = 0;

  // Utilisateur 8
  strcpy(Utilisateur[8].nom, "Carte 8");
  Utilisateur[8].uID = 0x3D35D6A4;
  Utilisateur[8].droits = 0b00000000;
  Utilisateur[8].nbUtils = 0;
  Utilisateur[8].credit = 0;

  // Utilisateur 9
  strcpy(Utilisateur[9].nom, "Carte 9");
  Utilisateur[9].uID = 0x3D37F42F;
  Utilisateur[9].droits = 0b00000000;
  Utilisateur[9].nbUtils = 0;
  Utilisateur[9].credit = 0;

  // Utilisateur 10
  strcpy(Utilisateur[10].nom, "Carte 10");
  Utilisateur[10].uID = 0x3D377B03;
  Utilisateur[10].droits = 0b00000000;
  Utilisateur[10].nbUtils = 0;
  Utilisateur[10].credit = 0;
  // Utilisateur 11
  strcpy(Utilisateur[11].nom, "Carte 11");
  Utilisateur[11].uID = 0x3D3836FC;
  Utilisateur[11].droits = 0b00000000;
  Utilisateur[11].nbUtils = 0;
  Utilisateur[11].credit = 0;

  // Utilisateur 12
  strcpy(Utilisateur[12].nom, "Carte 12");
  Utilisateur[12].uID = 0x3D33AC27;
  Utilisateur[12].droits = 0b00000000;
  Utilisateur[12].nbUtils = 0;
  Utilisateur[12].credit = 0;

  // Utilisateur 13
  strcpy(Utilisateur[13].nom, "Carte 13");
  Utilisateur[13].uID = 0x3D37B82A;
  Utilisateur[13].droits = 0b00000000;
  Utilisateur[13].nbUtils = 0;
  Utilisateur[13].credit = 0;

  // Utilisateur 14
  strcpy(Utilisateur[14].nom, "Carte 14");
  Utilisateur[14].uID = 0x3D37EE90;
  Utilisateur[14].droits = 0b00000000;
  Utilisateur[14].nbUtils = 0;
  Utilisateur[14].credit = 0;

  // Utilisateur 15
  strcpy(Utilisateur[15].nom, "Carte 15");
  Utilisateur[15].uID = 0x3D33B88D;
  Utilisateur[15].droits = 0b00000000;
  Utilisateur[15].nbUtils = 0;
  Utilisateur[15].credit = 0;

  // Utilisateur 16
  strcpy(Utilisateur[16].nom, "Carte 16");
  Utilisateur[16].uID = 0x3D3808B6;
  Utilisateur[16].droits = 0b00000000;
  Utilisateur[16].nbUtils = 0;
  Utilisateur[16].credit = 0;

  // Utilisateur 17

  strcpy(Utilisateur[17].nom, "Carte 17");
  Utilisateur[17].uID = 0x3D362D56;
  Utilisateur[17].droits = 0b00000000;
  Utilisateur[17].nbUtils = 0;
  Utilisateur[17].credit = 0;

  // Utilisateur 18
  strcpy(Utilisateur[18].nom, "Carte 18");
  Utilisateur[18].uID = 0x3D33E527;
  Utilisateur[18].droits = 0b00000000;
  Utilisateur[18].nbUtils = 0;
  Utilisateur[18].credit = 0;

  // Utilisateur 19
  strcpy(Utilisateur[19].nom, "Carte 19");
  Utilisateur[19].uID = 0x3D33BBA4;
  Utilisateur[19].droits = 0b00000000;
  Utilisateur[19].nbUtils = 0;
  Utilisateur[19].credit = 0;

  // Utilisateur 20
  strcpy(Utilisateur[20].nom, "Carte 20");
  Utilisateur[20].uID = 0x2D76B657;
  Utilisateur[20].droits = 0b00000000;
  Utilisateur[20].nbUtils = 0;
  Utilisateur[20].credit = 0;

  // Utilisateur 21
  strcpy(Utilisateur[21].nom, "Carte 21");
  Utilisateur[21].uID = 0x24698C21;
  Utilisateur[21].droits = 0b00000000;
  Utilisateur[21].nbUtils = 0;
  Utilisateur[21].credit = 0;

  // Utilisateur 22
  strcpy(Utilisateur[22].nom, "Carte 22");
  Utilisateur[22].uID = 0x67851024;
  Utilisateur[22].droits = 0b00000000;
  Utilisateur[22].nbUtils = 0;
  Utilisateur[22].credit = 0;

  // Utilisateur 23
  strcpy(Utilisateur[23].nom, "Carte 23");
  Utilisateur[23].uID = 0xB5370EE8;
  Utilisateur[23].droits = 0b00000000;
  Utilisateur[23].nbUtils = 0;
  Utilisateur[23].credit = 0;

  // Utilisateur 24
  strcpy(Utilisateur[24].nom, "Carte 24");
  Utilisateur[24].uID = 0xB51DC87;
  Utilisateur[24].droits = 0b00000000;
  Utilisateur[24].nbUtils = 0;
  Utilisateur[24].credit = 0;

  // Utilisateur 25
  strcpy(Utilisateur[25].nom, "Carte 25");
  Utilisateur[25].uID = 0x3D33B88D;
  Utilisateur[25].droits = 0b00000000;
  Utilisateur[25].nbUtils = 0;
  Utilisateur[25].credit = 0;

  // Utilisateur 26
  strcpy(Utilisateur[26].nom, "ADMIN 1");
  Utilisateur[26].uID = 0x0367DC11;
  Utilisateur[26].droits = 0b00000011; ; // finit par 11 == badge présent, et master
  Utilisateur[26].nbUtils = 0;
  Utilisateur[26].credit = 10000;

  // Utilisateur 27

  strcpy(Utilisateur[27].nom, "ADMIN 2");
  Utilisateur[27].uID = 0x3D3B0883;
  Utilisateur[27].droits = 0b00000011; ; // finit par 11 == badge présent, et master
  Utilisateur[27].nbUtils = 0;
  Utilisateur[27].credit = 10000;

  // Utilisateur 28
  strcpy(Utilisateur[28].nom, "ADMIN 3");
  Utilisateur[28].uID = 0xB51B2B7;
  Utilisateur[28].droits = 0b00000000;
  Utilisateur[28].nbUtils = 0;
  Utilisateur[28].credit = 0;

  // Utilisateur 29
  strcpy(Utilisateur[29].nom, "ADMIN 4");
  Utilisateur[29].uID = 0xB5B8BB27;
  Utilisateur[29].droits = 0b00000000;
  Utilisateur[29].nbUtils = 0;
  Utilisateur[29].credit = 0;

  strcpy(Utilisateur[30].nom, "ADMIN 5");
  Utilisateur[29].uID = 0xB5CC6647;
  Utilisateur[29].droits = 0b00000000;
  Utilisateur[29].nbUtils = 0;
  Utilisateur[29].credit = 0;
}


// ----------------------------------------------------------------------------
// Initialization
// ----------------------------------------------------------------------------

void setup() {
  Serial.begin(115200);
  pinMode(led_rouge, OUTPUT);
  pinMode(led_verte, OUTPUT);
  pinMode(relais220v, OUTPUT);
  lcd.begin();   // Init le lcd
  SPI.begin();        // Init SPI bus
  mfrc522.PCD_Init();     // Init MFRC522 card
  buzStart();
  lcd.print("--Laundry");
  lcd.setCursor(0, 1);
  lcd.print("        Controller--");
  lcd.setCursor(0, 3);
  lcd.print("      ver.");
  lcd.setCursor(10, 3);
  lcd.print(version);
  delay(2000);

  initSPIFFS();
  initWiFi();
  initNPTtime();

  lcd.setCursor(0, 3);
  lcd.print("    Init. services..");
  buzKey();


  // AsyncWebServer flags
  //**********************************************************

  // attach AsyncEventSource
  server.addHandler(&events);

  // respond to GET requests on URL /heap
  server.on("/heap", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send(200, "text/plain", String(ESP.getFreeHeap()));
  });

  //  Upload la page web en http fixe
  server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    //index ();
    request->send(SPIFFS, "/login.htm");
  });

  // send a file when /index is requested
  server.on("/index", HTTP_ANY, [](AsyncWebServerRequest * request) {
    request->send(SPIFFS, "/index.htm");
  });

  // HTTP basic authentication
  server.on("/login", HTTP_GET, [](AsyncWebServerRequest * request) {
    if (!request->authenticate(http_username, http_password))
      return request->requestAuthentication();
    request->send(200, "text/plain", "Login Success!");
    //request->send(SPIFFS, "/index.htm");              //la suite à déterminer..
  });

  // STYLE.CSS
  server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send(SPIFFS, "/style.css");
  });

  // Page MASTER htm
  server.on("/master", HTTP_GET, [](AsyncWebServerRequest * request) {
    masterhtm();
    request->send(SPIFFS, "/master.htm");
  });

  // Page STUDIO htm
  server.on("/studio", HTTP_GET, [] (AsyncWebServerRequest * request) {
    if (request->hasArg("Id")) {
      int studioNumber = request->arg("Id").toInt();
      Serial.println(studioNumber);
      pageStudio(studioNumber);
      request->send(SPIFFS, "/studio.htm");
    } else {
      request->send(400, "text / plain", "Bad request");
    }
  });

  // De la page studio
  //implémenter 1
  server.on("/plus1", HTTP_GET, [] (AsyncWebServerRequest * request) {
    int studioNumber = request->arg("Id").toInt();
    Utilisateur[studioNumber].credit += 1; Serial.print("/plus1 : studio"); Serial.println(studioNumber);
    pageStudio(studioNumber);
    request->send(SPIFFS, "/index.htm");
  });
  //implémenter 5
  server.on("/plus5", HTTP_GET, [] (AsyncWebServerRequest * request) {
    int studioNumber = request->arg("Id").toInt();
    Utilisateur[studioNumber].credit += 5; Serial.printf("/plus5 : studio"); Serial.println(studioNumber);
    pageStudio(studioNumber);
    request->send(SPIFFS, "/index.htm");
  });
  //implémenter 10
  server.on("/plus10", HTTP_GET, [] (AsyncWebServerRequest * request) {
    int studioNumber = request->arg("Id").toInt();
    Utilisateur[studioNumber].credit += 10; Serial.print("/plus10 : studio"); Serial.println(studioNumber);
    pageStudio(studioNumber);
    request->send(SPIFFS, "/index.htm");
  });;
  // remise du tags a 0
  server.on("/raz", HTTP_GET, [] (AsyncWebServerRequest * request) {
    int studioNumber = request->arg("Id").toInt();
    Utilisateur[studioNumber].credit = 0; Serial.print("/RAZ : studio"); Serial.println(studioNumber);
    pageStudio(studioNumber);
    request->send(SPIFFS, "/studio.htm");
  });
  // Changer un Badge
  server.on("/cdc", HTTP_GET, [] (AsyncWebServerRequest * request) {
    int studioNumber = request->arg("Id").toInt();
    Serial.println(studioNumber );
    // Attente du passage du badge (affiche des . pour indiquer)
    while ( ! mfrc522.PICC_IsNewCardPresent()) {
      Serial.print(".");
      yield();
    }
    // Lecture du badge (affiche des + pendant la lecture)
    while ( ! mfrc522.PICC_ReadCardSerial()) {
      Serial.print("+");
      yield();
    }
    Serial.print("Card UID:");
    for (byte i = 0; i < 4; i++) {
      Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
      Serial.print(mfrc522.uid.uidByte[i], HEX);
      Utilisateur[studioNumber].uID = *(uint32_t *)mfrc522.uid.uidByte;
    }
    pageStudio(studioNumber);
    request->send(SPIFFS, "/studio.htm");
  });
  //désactiver le badge
  server.on("/des", HTTP_GET, [] (AsyncWebServerRequest * request) {
    if (request->hasArg("Id")) {
      int studioNumber = request->arg("Id").toInt();
      Serial.print("/des : studio"); Serial.println(studioNumber);
      Utilisateur[studioNumber].droits &= 0b11111101; // on met le bit 7 à 0
    }
    else {
      Serial.println("Id : argument manquant");
    }
  });

  // De la page Masterhtm
  //Reset ESP32
  server.on("/restart", HTTP_GET, [] (AsyncWebServerRequest * request) {
    buzStop();
    delay(2000);
    ESP.restart();
  });
  //sauvegarde des données
  server.on("/sauv", HTTP_GET, [] (AsyncWebServerRequest * request) {
    for (int i = 0; i < nbUtilisateurs; i++) sauveStudio (i);
    request->send(SPIFFS, "/master.htm");
  });
  // restitution des données
  server.on("/resti", HTTP_GET, [] (AsyncWebServerRequest * request) {
    for (int i = 0; i < nbUtilisateurs; i++) restoreStudio (i);
    request->send(SPIFFS, "/master.htm");
  });
  // restitution des DONNEES DE DEFAUT
  server.on("/restidefault", HTTP_GET, [] (AsyncWebServerRequest * request) {
    initUsers();
    request->send(SPIFFS, "/master.htm");
  });
  // Tableau récap
  server.on("/recap", HTTP_GET, [] (AsyncWebServerRequest * request) {
    pageRecap();
    request->send(SPIFFS, "/recap.htm");
  });

  server.begin();

  testStudio (1);   //tester si sauvegarde présente, etc.
  for (int i = 0; i < nbUtilisateurs; i++) restoreStudio (i); //restoration des données en sauvegarde
  delay(1000);
  lcd.setCursor(0, 3);
  lcd.print("          Init. OK..");
  buzKey();
  delay(1000);
  Serial.print("Free HEAP remaining: ");
  Serial.println(ESP.getFreeHeap()); //affiche nombre d'octets libres en 'heap'
  lcd.setCursor(0, 3);
  lcd.print("                    ");
  lcd.setCursor(0, 3);
  lcd.print(" Free Mem. ");
  lcd.setCursor(12, 3);
  lcd.print(ESP.getFreeHeap());
  buzKey();
  delay(2000);
  lcd.clear();
  lcd.print("Dispositif");
  lcd.setCursor(0, 1);
  lcd.print("d'identification");
  lcd.setCursor(0, 3);
  lcd.print("   Mise en sommeil..");
  buzKey();
  delay(4000);
  lcd.clear();
  lcd.noBacklight();
}


// ----------------------------------------------------------------------------
// Main control loop
// ----------------------------------------------------------------------------

void loop() {
  // put your main code here, to run repeatedly:

  // Si REBOOT initialisé
  if (shouldReboot) {
    Serial.println("Rebooting...");
    delay(3000);
    ESP.restart();
  }
  static char temp[128];
  sprintf(temp, "Seconds since boot: %u", millis() / 1000);
  events.send(temp, "time"); //send event "time"

  // Look for new cards
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
    return;
  }
  // Select one of the cards
  if ( ! mfrc522.PICC_ReadCardSerial())    return;

  Serial.print("Card UID:");    //Dump UID
  for (byte i = 0; i < 4; i++) {
    Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
    Serial.print(mfrc522.uid.uidByte[i], HEX);
  }
  Serial.print(" ");
  tagok = 255;
  for  (byte i = 0; i < nbUtilisateurs; i++) {
    if (*(uint32_t *)mfrc522.uid.uidByte == Utilisateur[i].uID) {
      Serial.println("Carte reconnue");
      tagok = i;
      break;  // pas la peine d'aller plus loin
    }
  }
  switch (tagok) {
    case 0:
      master();
      // instructions master
      break;
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
    case 8:
    case 9:
    case 10:
    case 11:
    case 12:
    case 13:
    case 14:
    case 15:
    case 16:
    case 17:
    case 18:
    case 19:
    case 20:
    case 21:
    case 22:
    case 23:
    case 24:
    case 25:
    case 26:
    case 27:
    case 28:
    case 29:
    case 30:
      // instructions user connu
      if (Utilisateur[tagok].droits % 2 == 0) user(tagok);
      else master();
      break;
    case 255:
      inconnu();
      // badge inconnu
      break;
    default:
      inconnu();
      // instructions si autre numéro
  }
  mfrc522.PICC_HaltA(); // Stop reading
}

et niveau suivi des versions:
Versions_Lave_linge_federes.zip (2,4 Ko)

La partie htm et webserver est en travaux,
Et pour le reste, comme tu pouras le voir, je reste très prudent dans les changements.

Tu n'hésites surtout pas si tu as des remarques, commentaires, idées, questions, etc.!
Je suis là pour ça! :+1: :nerd_face:

C'est aussi bien comme ça, plutôt qu'en fichier joint, tant que ça rentre dans les limites permises...
Pour les changements, je dois avouer que je ne décortique pas le code. J'attends plutôt que tu dises où tu as besoin d'aide...

A+

ça marche! :+1: :sunglasses:

Comme je te dis, pour le moment je m'imbibe de tout ce qu'il faut, puis je m'y colle sur l'interface web, quand ce sera le bon alignement des astres... :stuck_out_tongue_winking_eye:

Après ce sera surement au tour de m'initier à l'usage de millis..

je pense aussi qu'il serait bien que certaines valeurs puissent être changées via l'interface web, pour éviter d'avoir à passer par le téléversement pour changer le mot de passe du wifi, changer des cartes ou des id de logements, etc...

La liste est longue, alors une chose à la fois... :+1:

Hello, bravo tu as bien bossé :clap: :+1:!

Je crois que @hbachetti a fait un tuto sur le sujet...

Merci @yohann74 , venant de toi, ça me va droit au cœur.
Le top serait que tu puisses aussi en profiter, tout comme le fait que j'ai apprécié de ne pas partir de zéro.. :stuck_out_tongue_winking_eye:
:+1: :sunglasses:

oui, ça va bien finir par arriver, je regarde bien au code chaque fois que je croise des exemples, et oui je passerai par ritonduino qui est effectivement pour moi un très bonne source d'exemples, d'idées, une très bonne référence! :nerd_face: :+1: :sunglasses:

...et tout comme notamment tes conseils et aiguillages, comme le dernier sur random nerds sur le http authentication... qui m'a bien servi!!

Donc côté avancée, ça y est j'ai le site opérationnel, avec les divers pages, (il y en aura d'autres encore, dont celles pour des affichages et interactions d'états et de données réelles...) le site est sécurisé, quelques finitions et cosmétiques...

Pour le moment, je me demande si avant d'aller plus loin je dois me pencher sur la façon de manipuler la valeur des tags...

Exemple, pour le badge du studio3:
-Quand il passe au lecteur RC522, la valeur annoncée est

Card UID: C4 99 67 03 Carte reconnue

-Au niveau du système, le badge est enregistré à l'envers (manipulation faite par mes soins pour le moment, je n'ai pas encore testé la fonction changer badge..)
Donc en mémoire le badge est

Utilisateur[3].uID = 0x036799C4;

et ceci pour accommoder une 'malheureuse' histoire de façon de sortir et lire la valeur de la mémoire, pour la comparer à la valeur provenant du RC522 lors de la présentation d'un badge..

  // Look for new cards
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
    return;
  }
  // Select one of the cards
  if ( ! mfrc522.PICC_ReadCardSerial())    return;

  Serial.print("Card UID:");    //Dump UID
  for (byte i = 0; i < 4; i++) {
    Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
    Serial.print(mfrc522.uid.uidByte[i], HEX);
  }
  Serial.print(" ");
  tagok = 255;
  for  (byte i = 0; i < nbUtilisateurs; i++) {
    if (*(uint32_t *)mfrc522.uid.uidByte == Utilisateur[i].uID) {
      Serial.println("Carte reconnue");
      tagok = i;

...la question est: le conducteur a t'il vraiment besoin de savoir ce qui se trame sous le capot de sa 'voiture'...?!
...pas forcément, mais dans ce cas j'aimerais quand même manipuler la valeur pour la remettre dans le bon ordre avant de l'afficher dans la tableau de synthèse par ce que pour le moment elle s'affiche elle aussi dans le désordre, et en plus sans les zéros en début de ligne... ce que je dois aussi trouver le moyen de résoudre... :nerd_face: J'imagine que ça va être une histoire de formatage... genre

< 0x10 ? " 0" : " ");

(le 0x n'étant pas nécessaire pour une info destinée à un utilisateur..)


...obtenu avec ce code:

  file.printf("<thead><tr class='w3-light-grey'><th>ID.</th><th>Nom</th><th>Créd.LL</th><th>Conso.LL</th><th>Créd.SL</th><th>Conso.SL</th></tr></thead>");
  for (int i = 0; i < nbUtilisateurs; i++) {
    file.printf("<tr><td><a class=\"button stu\" href=\"/studio?Id=%d\">%d</a></td><td>%s</td><td>%d</td><td>%d</td><td>%d</td><td>%d</td></tr>", i, i, Utilisateur[i].nom, Utilisateur[i].credit, Utilisateur[i].nbUtils, Utilisateur[i].credit, Utilisateur[i].nbUtils);
  }
  file.println("</table></div>");

Pour un futur proche, et histoire de tout avoir sous les yeux dans le même post',
'Changer un badge'

  // Changer un Badge
  //**********************************************************
  server.on("/cdc", HTTP_GET, [] (AsyncWebServerRequest * request) {
    int studioNumber = request->arg("Id").toInt();
    Serial.println(studioNumber );
    // Attente du passage du badge (affiche des . pour indiquer)
    while ( ! mfrc522.PICC_IsNewCardPresent()) {
      Serial.print(".");
      yield();
    }
    // Lecture du badge (affiche des + pendant la lecture)
    while ( ! mfrc522.PICC_ReadCardSerial()) {
      Serial.print("+");
      yield();
    }
    Serial.print("Card UID:");
    for (byte i = 0; i < 4; i++) {
      Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
      Serial.print(mfrc522.uid.uidByte[i], HEX);
      Utilisateur[studioNumber].uID = *(uint32_t *)mfrc522.uid.uidByte;
    }
    pageStudio(studioNumber);
    request->send(SPIFFS, "/studio.htm");
  });

Désactiver un badge

  //désactiver le badge
  //**********************************************************
  server.on("/des", HTTP_GET, [] (AsyncWebServerRequest * request) {
    if (request->hasArg("Id")) {
      int studioNumber = request->arg("Id").toInt();
      Serial.print("/des : studio"); Serial.println(studioNumber);
      Utilisateur[studioNumber].droits &= 0b11111101; // on met le bit 7 à 0
    }
    else {
      Serial.println("Id : argument manquant");
    }
  });

Bon voilà, réfléchir avant d'agir.. :thinking: :sweat_smile:

@yohann74 elle est fonctionnelle et utilisée chez toi la fonction pour changer la valeur d'un badge pour un utilisateur donné?