#include <Adafruit_Fingerprint.h>
#include <LiquidCrystal_I2C.h>
#include <ESP32Servo.h>
#include <Arduino.h>
#include <DS1302.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#if (defined(__AVR__) || defined(ESP8266)) && !defined(__AVR_ATmega2560__)
// For UNO and others without hardware serial, we must use software serial...
// pin #2 is IN from sensor (GREEN wire)
// pin #3 is OUT from arduino (WHITE wire)
// Set up the serial port to use softwareserial..
SoftwareSerial mySerial(2, 3);
#else
// On Leonardo/M0/etc, others with hardware serial, use hardware serial!
// #0 is green wire, #1 is white
#define mySerial Serial2
#endif
// Définition des différentes broches
#define COIN_ACCEPTOR_PIN 33
#define FLOW_METER_PIN 32
#define BUTTON_SUBSCRIPTION_PIN 34
#define BUTTON_OK_PIN 35
#define BUTTON_CANCEL_PIN 3
#define BUTTON_OK1_PIN 14
#define BUTTON_PSS_PIN 25
#define BUTTON_START_PIN 36
#define BUTTON_STOP_PIN 39
#define ELECTROVALVE_PIN 27 // Pin pour l'électrovanne
#define AUPM_PIN 26 // Pin pour l'accepteur universel de pieces de monnaies
#define PAUSE_TIMEOUT 5000 // Temps d'attente avant d'actualiser les données (5 secondes)
#define CERVOMOTEUR_PIN 13 //
// Chip select pin for the SD card
#define CS_PIN 5
// Définition des broches du RTC DS1302
#define RST_PIN 2 // CE pin
#define DAT_PIN 4 // I/O pin
#define CLK_PIN 15 // SCLK pin
#define MAX_CLIENTS 5 // Limite le nombre d'empreintes à 20
#define DATA_COLUMNS 7
// Global Variables
struct ClientData
{
float waterVolume = 0.0f;
int hour = 0;
int min = 0;
int sec = 0;
int date = 0;
int mon = 0;
int year = 0;
};
ClientData clientData[MAX_CLIENTS] = {0};
int subscriptionAmount = 0;
int pos = 90;
float waterVolume = 0.0f;
float flowRate = 0.0; // Débit en litres par minute
float calibrationFactor = 7.5; // Facteur de calibration pour le débitmètre YF-S201
float remainingWater = 0.0; // Eau restante pour le client
volatile int coinCount = 0; // Déclare la variable coinCount ici
volatile int countImpulsions = 0; // Nombre d'impulsions du débitmètre
bool electroValveOpen = false; // État de l'électrovanne
bool saveOk = false;
bool systemInactive = true; // Variable pour savoir si le système est inactif
unsigned long lastActionTime = 0; // Horodatage de la dernière action du client
uint8_t id = 1;
File dataFile;
Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);
LiquidCrystal_I2C lcd(0x27, 20, 4);
DS1302 rtc(CLK_PIN, DAT_PIN, RST_PIN); // Initialisation du RTC DS1302
Servo myservo;
// Function Prototypes
void setup();
void loop();
void loadClientData();
bool saveClientData(int clientID, float waterVolume, int date, int mon, int year, int hour, int min, int sec);
void saveCoinData(int coinValue);
void nextID();
void handleSubscription();
void ecranAcceuil();
void identificationClient(int id);
void puissage();
void openElectroValve();
void closeElectroValve();
void measureFlow(int clientID);
void updateClientData(int clientID);
void resetClientData(int clientID);
void deleteFingerprint(int clientID);
uint8_t getFingerprintEnroll();
uint8_t deleteFingerprint(uint8_t id);
uint8_t waitForFingerprint(int step);
int getCoinValue();
int calculateWaterVolume(int amount);
int verifyFingerprint();
void coinISR();
void IRAM_ATTR compteurImpulsions();
void attenteValidee();
void attenteAnnulee();
void setup() {
lcd.init();
lcd.backlight();
lcd.clear();
for (int i = 0; i < 3; i ++)
{
lcd.setCursor(0, 0);
lcd.print(" Initialisation. ");
vTaskDelay(pdMS_TO_TICKS(250)); // Remplacer delay(250) par vTaskDelay
lcd.setCursor(0, 0);
lcd.print(" Initialisation.. ");
delay(250);
lcd.setCursor(0, 0);
lcd.print(" Initialisation... ");
delay(250);
lcd.setCursor(0, 0);
lcd.print(" Initialisation ");
delay(250);
}
// Initialize SD card
if (!SD.begin(CS_PIN)) {
lcd.setCursor(0, 1);
lcd.print("Erreur, carte SD :(");
lcd.setCursor(0, 2);
lcd.print("Impos. de continuer!");
while (1);
}
lcd.setCursor(0, 1);
lcd.print("Carte SD OK! ");
delay(1500);
mySerial.begin(57600, SERIAL_8N1, 16, 17);
Serial.begin(115200);
while (!Serial); // For Yun/Leo/Micro/Zero/...
delay(100);
// set the data rate for the sensor serial port
finger.begin(57600);
if (finger.verifyPassword()) {
lcd.setCursor(0, 2);
lcd.print("Lect. empreintes OK!");
} else {
lcd.setCursor(0, 1);
lcd.print("Erreur du lecteur :(");
lcd.setCursor(0, 2);
lcd.print("Impos. de continuer!");
while (1) { delay(1); }
}
delay(1500);
Serial.println(F("Reading sensor parameters"));
finger.getParameters();
Serial.print(F("Status: 0x")); Serial.println(finger.status_reg, HEX);
Serial.print(F("Sys ID: 0x")); Serial.println(finger.system_id, HEX);
Serial.print(F("Capacity: ")); Serial.println(finger.capacity);
Serial.print(F("Security level: ")); Serial.println(finger.security_level);
Serial.print(F("Device address: ")); Serial.println(finger.device_addr, HEX);
Serial.print(F("Packet len: ")); Serial.println(finger.packet_len);
Serial.print(F("Baud rate: ")); Serial.println(finger.baud_rate);
lcd.setCursor(0, 3);
lcd.print("Parametres lect. OK!");
delay(1500);
// Autres configurations
pinMode(COIN_ACCEPTOR_PIN, INPUT);
pinMode(FLOW_METER_PIN, INPUT);
pinMode(BUTTON_SUBSCRIPTION_PIN, INPUT);
//pinMode(BUTTON_SUBSCRIPTION_MANUEL_PIN, INPUT);
pinMode(BUTTON_PSS_PIN, INPUT);
pinMode(BUTTON_OK_PIN, INPUT);
pinMode(BUTTON_OK1_PIN, INPUT);
pinMode(BUTTON_CANCEL_PIN, INPUT);
pinMode(BUTTON_START_PIN, INPUT);
pinMode(BUTTON_STOP_PIN, INPUT);
pinMode(ELECTROVALVE_PIN, OUTPUT);
pinMode(AUPM_PIN, OUTPUT);
digitalWrite(ELECTROVALVE_PIN, HIGH);
digitalWrite(AUPM_PIN, HIGH);
// Attacher une interruption pour l'accepteur de pièces
attachInterrupt(digitalPinToInterrupt(COIN_ACCEPTOR_PIN), coinISR, FALLING);
attachInterrupt(digitalPinToInterrupt(FLOW_METER_PIN), compteurImpulsions, RISING);
// Set the clock to run-mode, and disable the write protection
rtc.halt(false);
rtc.writeProtect(false);
/*
// Setting the time and date
rtc.setTime(19, 33, 0); // Set to 12:00:00
rtc.setDate(12, 12, 2024); // Set to 20th August 2024
*/
myservo.attach(CERVOMOTEUR_PIN); // attaches the servo on pin 9 to the Servo object
myservo.write(pos); // tell servo to go to position in variable 'pos'
// Pour vider ou pas le capteur au demarrage
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("MENAGE DU CAPTEUR...");
for (int i = 5; i >= 1; i--)
{
lcd.setCursor(0, 1);
lcd.print("Vous avez secondes");
lcd.setCursor(10, 1); lcd.print(i);
lcd.setCursor(0, 2);
lcd.print("pour le vider ou pas");
delay(1000);
if (digitalRead(BUTTON_CANCEL_PIN) == LOW)
{
// set the data rate for the sensor serial port
finger.begin(57600);
if (finger.verifyPassword())
{
Serial.println("Found fingerprint sensor!");
} else {
Serial.println("Did not find fingerprint sensor :(");
while (1);
}
finger.emptyDatabase();
lcd.setCursor(0, 2);
lcd.print("Ok capteur vide!");
break;
}
}
lcd.setCursor(0, 3);
lcd.print("Temps ecoule :(");
delay(1000);
// Load client data from the SD card
loadClientData();
lcd.clear();
lcd.setCursor(0, 1);
lcd.print(" YOU ARE WELLCOM!");
lcd.setCursor(0, 2);
lcd.print(" READY TO START :)");
delay(2500);
lcd.clear();
}
void loop() {
// Vérifie si le système est inactif et affiche l'écran d'accueil
if (systemInactive) {
ecranAcceuil(); // Appelle la fonction pour afficher l'écran d'accueil
}
handleSubscription();
puissage();
}
/********FONCTION POUR LE PROCESSUS D'ABONNEMENT********/
void handleSubscription() {
if (digitalRead(BUTTON_SUBSCRIPTION_PIN) == LOW){
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(">>Recherche espace<<");
bool idFound = false;
if (digitalRead(BUTTON_CANCEL_PIN) == LOW) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Retour a l'accueil");
delay(1000);
lcd.clear();
return;
}
File dataFile = SD.open("/clientData.txt", FILE_READ);
if (dataFile) {
// Loop through the entire clientData array to find the first available slot where waterVolume == 0
for (int i = 0; i < MAX_CLIENTS; i++) {
if (digitalRead(BUTTON_CANCEL_PIN) == LOW) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Retour a l'accueil");
delay(1000);
lcd.clear();
return;
}
String line = dataFile.readStringUntil('\n');
sscanf(line.c_str(), "%f", &clientData[i].waterVolume);
if (clientData[i].waterVolume == 0.0f) { // Find an empty spot
lcd.setCursor(0, 1);
lcd.print("Client_");
lcd.print(i+1); lcd.print(", vide!");
id = i + 1; // Set the next available ID (1-based index)
idFound = true;
break;
} else {
lcd.setCursor(0, 1);
lcd.print("Client_");
lcd.print(i+1); lcd.print(", occupe!");
delay(100);
lcd.setCursor(0, 1); lcd.print(" ");
}
}
dataFile.close();
} else {
lcd.setCursor(0, 1);
lcd.print("Erreur de carte SD!");
delay(1000);
lcd.clear();
return;
}
if (idFound == false) {
lcd.setCursor(0, 1);
lcd.print("Plus d'espace...");
delay(2000);
lcd.clear();
return; // Exit if subscription is full
} else {
// Save the last available ID (for later use)
dataFile = SD.open("/nextID.txt", FILE_WRITE);
if (dataFile) {
dataFile.println(id);
dataFile.close();
systemInactive = false; // Le système devient actif
lcd.setCursor(0, 1);
lcd.print("Espace trouvee!");
lcd.setCursor(0, 2);
lcd.print("Vous etes client "); lcd.print(id);
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("PAIYEMENT, CLIENT ");
lcd.print(id);
digitalWrite(AUPM_PIN, LOW);
delay(1500);
lcd.setCursor(0, 1);
lcd.print("Inserez votre somme");
subscriptionAmount = 0;
coinCount = 0;
while (digitalRead(BUTTON_OK_PIN) == HIGH && digitalRead(BUTTON_CANCEL_PIN) == HIGH) {
if (coinCount > 0) {
int coinValue = getCoinValue();
subscriptionAmount += getCoinValue(); // Appelle la fonction pour obtenir la valeur de la pièce
//saveCoinData(coinValue);
waterVolume = calculateWaterVolume(subscriptionAmount); // Calcule le volume d'eau
lcd.setCursor(0, 2);
lcd.print("T= ");
lcd.print(subscriptionAmount);
lcd.print("F");
lcd.print(" ==>");
lcd.print(waterVolume);
lcd.print("L");
lcd.setCursor(0, 3);
lcd.print("OK? ou Annuler?");
coinCount = 0;
}
}
if (digitalRead(BUTTON_OK_PIN) == LOW && waterVolume <= 0.0f)
{
digitalWrite(AUPM_PIN, HIGH);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Aucune piece trouvee");
lcd.setCursor(0, 1);
lcd.print("Rappuyez sur Abmt ");
lcd.setCursor(0, 2);
lcd.print("puis inserrez-en une");
delay(3000);
lcd.clear();
systemInactive = true;
} else if (digitalRead(BUTTON_OK_PIN) == LOW && waterVolume > 0.0f) {
digitalWrite(AUPM_PIN, HIGH);
Time t = rtc.getTime();
clientData[id-1].waterVolume = waterVolume;
clientData[id-1].date = t.date;
clientData[id-1].mon = t.mon;
clientData[id-1].year = t.year;
clientData[id-1].hour = t.hour;
clientData[id-1].min = t.min;
clientData[id-1].sec = t.sec;
// Sauvegarde des données du client
if (saveClientData(id-1, waterVolume, t.date, t.mon, t.year, t.hour, t.min, t.sec)) {
delay(5000);
// Si le stockage a réussi, appeler la fonction d'identification
identificationClient(id);
} else {
// Si le stockage échoue, afficher un message d'erreur
lcd.setCursor(0, 3);
lcd.print("Erreur de carte SD");
delay(2000);
lcd.clear();
systemInactive = true;
}
} else if (digitalRead(BUTTON_CANCEL_PIN) == LOW) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Abonnement annule");
digitalWrite(AUPM_PIN, HIGH);
waterVolume = 0.0f;
// FONCTION POUR RETOURNEE LA PIECE
attenteAnnulee();
lcd.setCursor(0, 2);
lcd.print("Reccuperez vos pices");
lcd.setCursor(0, 3);
lcd.print(" Au revoir!");
delay(2500);
lcd.clear();
systemInactive = true; // Retour à l'état inactif
}
} else {
digitalWrite(AUPM_PIN, HIGH);
Serial.println("Error opening ID file for writing");
lcd.setCursor(0, 3);
lcd.print("Err. ouvert. fichier");
delay(1000);
lcd.clear();
systemInactive = true;
}
}
}
}
/********FONCTION POUR L'IDENTIFICATION PAR EMPREINTE DE CHAQUE CLIENT********/
void identificationClient(int id) {
uint8_t p = -1; // Variable pour stocker le statut de l'enregistrement
int attempts = 0; // Compteur de tentatives
lcd.clear();
lcd.setCursor(0, 0); lcd.print(" IDENTIFICATION... ");
delay(1500);
lcd.setCursor(0, 1); lcd.print("Client "); lcd.print(id);
while (attempts < 5) { // Limite à 5 tentatives maximum
lcd.setCursor(8, 1); lcd.print(" ==> "); lcd.print(attempts + 1); lcd.print(" fois ");
lcd.setCursor(0, 2); lcd.print("Posez votre doigt");
Serial.print("Tentative "); Serial.println(attempts + 1);
// Attente que le client place son doigt
p = waitForFingerprint(1); // Fonction pour capturer l'empreinte
if (p != FINGERPRINT_OK) {
attempts++; // Incrémenter le compteur si la capture échoue
continue; // Recommencer si la première capture échoue
}
// Demander au client de retirer son doigt
lcd.setCursor(0, 2); lcd.print("Retirez le doigt ");
delay(2000);
while (finger.getImage() != FINGERPRINT_NOFINGER); // Attente du retrait du doigt
// Demander au client de replacer le même doigt
lcd.setCursor(0, 2); lcd.print("Remettez le doigt ");
p = waitForFingerprint(2); // Fonction pour capturer la deuxième empreinte
if (p != FINGERPRINT_OK) {
attempts++; // Incrémenter le compteur si la deuxième capture échoue
continue; // Recommencer si la deuxième capture échoue
}
// Créer le modèle à partir des deux empreintes
lcd.setCursor(0, 3); lcd.print("Creation modele ");
Serial.println("Creation du modèle...");
p = finger.createModel();
if (p == FINGERPRINT_OK) {
lcd.setCursor(0, 3); lcd.print("Empreintes OK! ");
Serial.println("Modèle créé avec succès !");
} else {
// Ajout de messages détaillés sur la cause de l'erreur de création
if (p == FINGERPRINT_ENROLLMISMATCH) {
Serial.println("Erreur: Les empreintes ne correspondent pas.");
lcd.setCursor(0, 3); lcd.print("Non correspondantes ");
} else {
Serial.print("Erreur de creation de modèle: ");
Serial.println(p); // Afficher le code d'erreur pour analyse
lcd.setCursor(0, 3); lcd.print("Erreur creation ");
}
attempts++; // Incrémenter le compteur si la création du modèle échoue
delay(1000);
continue; // Recommencer si la création échoue
}
// Stocker le modèle avec l'ID donné
lcd.setCursor(0, 3); lcd.print("Stockage modele ");
Serial.println("Stockage du modèle...");
p = finger.storeModel(id);
if (p == FINGERPRINT_OK) {
attenteValidee();
lcd.setCursor(0, 3); lcd.print("Modele stocke! ");
Serial.println("Modèle stocké avec succès !");
delay(1000);
lcd.clear();
lcd.setCursor(0, 0); lcd.print("ABONNEMENT BIEN RECU");
lcd.setCursor(0, 2); lcd.print(" VOUS POUVEZ PASSEZ ");
lcd.setCursor(0, 3); lcd.print(" AU PUISAGE D'EAU ");
delay(4000);
lcd.clear();
systemInactive = true;
return; // Sortir de la fonction si l'enregistrement a réussi
} else {
// Ajout de messages détaillés sur la cause de l'erreur de stockage
if (p == FINGERPRINT_PACKETRECIEVEERR) {
Serial.println("Erreur de communication pendant le stockage.");
lcd.setCursor(0, 3); lcd.print("Erreur communication");
subscriptionAmount = 0;
waterVolume = 0.0f;
} else if (p == FINGERPRINT_BADLOCATION) {
Serial.println("Erreur: Mauvaise position de stockage.");
lcd.setCursor(0, 3); lcd.print("Erreur stockage ");
subscriptionAmount = 0;
waterVolume = 0.0f;
} else if (p == FINGERPRINT_FLASHERR) {
Serial.println("Erreur d'écriture dans la mémoire flash.");
lcd.setCursor(0, 3); lcd.print("Erreur flash ");
subscriptionAmount = 0;
waterVolume = 0.0f;
} else {
Serial.print("Erreur inconnue lors du stockage: ");
Serial.println(p); // Afficher le code d'erreur
lcd.setCursor(0, 3); lcd.print("Erreur stockage ");
subscriptionAmount = 0;
waterVolume = 0.0f;
}
attempts++; // Incrémenter le compteur si le stockage échoue
delay(2000);
}
}
// Si on atteint ici, cela signifie que les 5 tentatives ont échoué
subscriptionAmount = 0;
waterVolume = 0.0f;
clientData[id-1].waterVolume = 0.0;
clientData[id-1].hour = 0;
clientData[id-1].min = 0;
clientData[id-1].sec = 0;
clientData[id-1].date = 0;
clientData[id-1].mon = 0;
clientData[id-1].year = 0;
delay(50);
bool saveOk = saveClientData(id-1, 0, 0, 0, 0, 0, 0, 0); // Réinitialiser les données sur la carte SD
lcd.clear();
if (saveOk) {
// FOCTION POUR RAMEMENER L'ARGENT DU CLIENT
attenteAnnulee();
subscriptionAmount = 0;
waterVolume = 0.0f;
lcd.setCursor(0, 0); lcd.print("Echec apres 5 essais");
lcd.setCursor(0, 1); lcd.print("Essuyez le doigt ou ");
lcd.setCursor(0, 2); lcd.print("le vitre du capteur ");
lcd.setCursor(0, 3); lcd.print("puis Reessayez !!! ");
delay(5000);
lcd.clear();
systemInactive = true;
return;
} else {
lcd.setCursor(0, 0); lcd.print("Erreur de carte SD");
lcd.setCursor(0, 1); lcd.print("Verifiez la carte SD");
subscriptionAmount = 0;
waterVolume = 0.0f;
delay(5000);
lcd.clear();
systemInactive = true;
return;
}
}
/********FONCTION POUR CAPTURER L'IMAGE D'UNE EMPREINTE AVEC ATTENTE D'UNE EMPREITE VALIDE********/
uint8_t waitForFingerprint(int step) {
uint8_t p;
while (true) {
p = finger.getImage();
if (p == FINGERPRINT_OK) {
lcd.setCursor(0, 3); lcd.print("Empreinte prise ");
Serial.println(step == 1 ? "Première empreinte prise" : "Deuxième empreinte prise");
break; // Empreinte réussie, sortir de la boucle
} else if (p == FINGERPRINT_NOFINGER) {
lcd.setCursor(0, 3); lcd.print("Attente doigt... ");
} else if (p == FINGERPRINT_PACKETRECIEVEERR) {
lcd.setCursor(0, 3); lcd.print("Erreur comm. ");
Serial.println("Erreur de communication lors de la prise de l'empreinte.");
break; // Erreur critique, sortir de la boucle
} else {
lcd.setCursor(0, 3); lcd.print("Erreur imagerie ");
Serial.println("Erreur lors de la capture de l'image d'empreinte.");
break; // Erreur critique, sortir de la boucle
}
delay(500); // Pause pour éviter une surcharge de lecture
}
// Convertir l'image en modèle d'empreinte
p = finger.image2Tz(step);
if (p == FINGERPRINT_OK) {
lcd.setCursor(0, 3); lcd.print("Image convertie ");
Serial.println("Image convertie avec succès.");
} else if (p == FINGERPRINT_IMAGEMESS) {
lcd.setCursor(0, 3); lcd.print("Image floue ");
Serial.println("Erreur: Image trop floue.");
} else {
lcd.setCursor(0, 3); lcd.print("Erreur conversion ");
Serial.println("Erreur lors de la conversion de l'image.");
}
delay(1000);
return p; // Retourner le statut (succès ou erreur)
}
/**********FONCTION POUR CHARGER LES DONNEES DE CHAQUE CLIENT DU TABLEAU**********/
void loadClientData() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(">>Charg. donn. SD <<");
delay(1000);
File dataFile = SD.open("/clientData.txt", FILE_READ);
if (dataFile) {
for (int i = 0; i < MAX_CLIENTS; i++) {
if (dataFile.available()) {
String line = dataFile.readStringUntil('\n');
sscanf(line.c_str(), "%f %d %d %d %d %d %d",
&clientData[i].waterVolume,
&clientData[i].date,
&clientData[i].mon,
&clientData[i].year,
&clientData[i].hour,
&clientData[i].min,
&clientData[i].sec);
}
}
dataFile.close();
lcd.setCursor(0, 1);
lcd.print("Chargement recu!");
} else {
lcd.setCursor(0, 1);
lcd.print("Pas de fichier!");
delay(1000);
lcd.setCursor(0, 2);
lcd.print("Chargement echoue!");
}
}
/**********FONCTION POUR SAUVEGARDER LES DONNEES DE CHAQUE CLIENT DANS LE TABLEAU**********/
bool saveClientData(int clientID, float waterVolume, int date, int mon, int year, int hour, int min, int sec) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(">>>> Stockage <<<<");
ClientData tempData[MAX_CLIENTS];
// Ouvrir le fichier pour lecture des données existantes
File dataFileRead = SD.open("/clientData.txt", FILE_READ);
if (dataFileRead) {
// Lire les données existantes dans tempData
for (int i = 0; i < MAX_CLIENTS; i++) {
if (dataFileRead.available()) {
String line = dataFileRead.readStringUntil('\n');
sscanf(line.c_str(), "%f %d %d %d %d %d %d",
&tempData[i].waterVolume,
&tempData[i].date,
&tempData[i].mon,
&tempData[i].year,
&tempData[i].hour,
&tempData[i].min,
&tempData[i].sec);
}
}
dataFileRead.close();
} else {
lcd.setCursor(0, 1);
lcd.print("Erreur de lecture SD");
delay(2000);
return false; // Retourner faux si le fichier de lecture n'a pas pu être ouvert
}
// Mettre à jour uniquement les données du client modifié
tempData[clientID].waterVolume = waterVolume;
tempData[clientID].date = date;
tempData[clientID].mon = mon;
tempData[clientID].year = year;
tempData[clientID].hour = hour;
tempData[clientID].min = min;
tempData[clientID].sec = sec;
// Ouvrir le fichier pour écriture (remplace le contenu)
File dataFileWrite = SD.open("/clientData.txt", FILE_WRITE);
if (dataFileWrite) {
// Réécrire toutes les données dans le fichier, sans texte
for (int i = 0; i < MAX_CLIENTS; i++) {
dataFileWrite.printf("%.2f %d %d %d %d %d %d\n",
tempData[i].waterVolume,
tempData[i].date,
tempData[i].mon,
tempData[i].year,
tempData[i].hour,
tempData[i].min,
tempData[i].sec);
}
dataFileWrite.close();
lcd.setCursor(0, 1);
lcd.print("Stockage reussi");
lcd.setCursor(0, 2);
lcd.print("Client numero "); lcd.print(clientID + 1);
lcd.setCursor(0, 3);
lcd.print("Vol. total= ");
lcd.print(tempData[clientID].waterVolume, 2);
lcd.print("L");
return true; // Retourner vrai si le stockage a réussi
} else {
lcd.setCursor(0, 1);
lcd.print("Erreur stockage SD");
delay(2000);
return false; // Retourner faux si le fichier d'écriture n'a pas pu être ouvert
}
}
/********FONCTION DE L'ECRAN D'ACCUEIL********/
void ecranAcceuil() {
Time t = rtc.getTime();
//lcd.clear();
lcd.setCursor(0, 0);
lcd.print(t.date);
lcd.print("/");
lcd.print(t.mon);
lcd.print("/");
lcd.print(t.year);
lcd.setCursor(15, 0); lcd.print(t.hour);
lcd.print(":"); lcd.print(t.min);
delay(500);
lcd.setCursor(15, 0); lcd.print(t.hour);
lcd.print(" "); lcd.print(t.min);
lcd.setCursor(0, 2);
lcd.print(" CASCH WATER! ");
lcd.setCursor(0, 3);
lcd.print("Un syst. automatique");
delay(500);
}
/********FONCTION D'INTERRUPTIONS POUR COMPTER LES IMPULTIONS DE CHAQUE PIECE********/
void coinISR() {
coinCount++;
}
/********FONCTION D'INTERRUPTIONS POUR COMPTER LES IMPULTIONS DU DEBIMETRE********/
void IRAM_ATTR compteurImpulsions() {
countImpulsions++;
}
/********FONCTION POU OBTENIR LA VALEUR DE CHAQUE PIECE********/
int getCoinValue() {
switch (coinCount) {
case 1: return 25;
case 2: return 50;
case 4: return 100;
case 8: return 200;
case 10: return 250;
case 20: return 500;
default: return 0;
}
}
/********FONCTION POUR CALCULER LE VOLUME DE CHAQUE CLIENT********/
int calculateWaterVolume(int amount) {
return (amount / 25) * 50;
}
/********FONCTION POUR METTRE LES PIECES DANS LA CAISSE SECURISSEE********/
void attenteValidee() {
for (pos = 90; pos <= 130; pos += 2) { // goes from 0 degrees to 180 degrees
// in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15 ms for the servo to reach the position
}
for (pos = 130; pos >= 90; pos -= 1) { // goes from 0 degrees to 180 degrees
// in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15 ms for the servo to reach the position
}
}
/********FONCTION POUR RAMMENER LES PIECES VERS LE CLIENT********/
void attenteAnnulee() {
for (pos = 90; pos >= 50; pos -= 2) { // goes from 180 degrees to 0 degrees
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15 ms for the servo to reach the position
}
for (pos = 50; pos <= 90; pos += 1) { // goes from 180 degrees to 0 degrees
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15 ms for the servo to reach the position
}
}
// Fonction pour lancer le mode puissage avec identification par empreinte
void puissage() {
int attempts = 0; // Compteur de tentatives pour la lecture d'empreintes
int clientID = -1; // Stockage de l'ID du client après vérification d'empreinte
// Vérifier si le bouton Pss est appuyé pour démarrer la vérification de l'empreinte
if (digitalRead(BUTTON_PSS_PIN) == LOW) {
systemInactive = false; // Le système devient actif
lcd.clear();
lcd.setCursor(0, 0); lcd.print("RECHERCHE EMPREINTE");
// Boucle de 3 tentatives pour détecter l'empreinte
while (attempts < 3 && clientID == -1) {
lcd.setCursor(0, 1); lcd.print("Posez votre doigt...");
clientID = verifyFingerprint(); // Vérifier l'empreinte
if (clientID > 0) {
lcd.setCursor(0, 3); lcd.print("Client numero "); lcd.print(clientID);
delay(2000);
// *** Afficher les données du client correspondant ***
remainingWater = clientData[clientID-1].waterVolume; // Volume d'eau du client
lcd.clear();
lcd.setCursor(0, 0); lcd.print("DONNEES DU CLIENT...");
lcd.setCursor(0, 1); lcd.print("Client numero "); lcd.print(clientID);
lcd.setCursor(0, 2); lcd.print("Volume: "); lcd.print(remainingWater, 2); lcd.print("L");
// Afficher la date et l'heure d'enregistrement
lcd.setCursor(0, 3);
lcd.print("Du "); lcd.print(clientData[clientID-1].date);
lcd.print("/"); lcd.print(clientData[clientID-1].mon);
lcd.print("/"); lcd.print(clientData[clientID-1].year);
lcd.print(" a ");
lcd.print(clientData[clientID-1].hour); lcd.print(":");
lcd.print(clientData[clientID-1].min);
delay(2000); // Afficher les données pendant quelques secondes
break; // Sortir de la boucle si empreinte trouvée
}
attempts++; // Incrémenter les tentatives si échec
delay(2000); // Pause avant une nouvelle tentative
}
// Si aucune empreinte n'a été trouvée après 3 tentatives
if (clientID == -1) {
lcd.setCursor(0, 2); lcd.print("Pas empreinte valide");
delay(2000);
lcd.clear();
systemInactive = true; // Revenir à l'écran d'accueil
return; // Quitter la fonction
}
// Si le client est trouvé, récupérer les données
remainingWater = clientData[clientID-1].waterVolume;
// Attendre le bouton de démarrage pour commencer le puissage
while (true) {
if (digitalRead(BUTTON_START_PIN) == LOW) {
lcd.clear();
lcd.setCursor(0, 0); lcd.print(" PUISSAGE... ");
openElectroValve(); // Ouvrir l'électrovanne
lastActionTime = millis(); // Mettre à jour l'heure de la dernière action
// Boucle de puissage avec affichage en temps réel
while (electroValveOpen) {
measureFlow(clientID-1); // Mesurer et décrémenter l'eau restante
// Afficher l'eau restante en temps réel
lcd.setCursor(0, 2); lcd.print("Vol. rest.: ");
lcd.print(remainingWater, 2); lcd.print("L");
// Si le bouton d'arrêt est appuyé, fermer l'électrovanne
if (digitalRead(BUTTON_STOP_PIN) == LOW) {
closeElectroValve();
lcd.setCursor(0, 2); lcd.print("Vol. rest.: ");
lcd.print(remainingWater, 2); lcd.print("L");
lastActionTime = millis(); // Mettre à jour l'heure d'arrêt
delay(1000); // Pause avant de continuer
// Vérifier si le bouton de démarrage est appuyé dans les 5 secondes
while (millis() - lastActionTime < PAUSE_TIMEOUT) {
if (digitalRead(BUTTON_START_PIN) == LOW) {
openElectroValve(); // Réouvrir l'électrovanne
break; // Recommencer le puissage si le bouton est appuyé
}
}
// Si aucun appui n'est détecté dans les 5 secondes, actualiser les données
if (millis() - lastActionTime >= PAUSE_TIMEOUT) {
updateClientData(clientID-1); // Sauvegarder les nouvelles données du client
lcd.clear();
systemInactive = true;
return; // Quitter de la fonction
}
}
// Si l'eau est épuisée, réinitialiser les données du client
if (remainingWater <= 0.0) {
lcd.clear();
lcd.setCursor(0, 0); lcd.print("VOLUME EPUISSE...");
closeElectroValve();
lcd.setCursor(0, 2); lcd.print("Client "); lcd.print(clientID);
lcd.setCursor(0, 3); lcd.print("Abonnement termine! ");
resetClientData(clientID); // Réinitialiser les données du client
delay(3000);
lcd.clear();
systemInactive = true; // Revenir à l'écran d'accueil
return; // Sortir de la fonction
}
}
}
}
}
}
// Fonction pour vérifier l'empreinte digitale avec 3 tentatives
int verifyFingerprint() {
int p;
for (int attempt = 0; attempt < 3; attempt++) {
p = finger.getImage();
// Si aucun doigt n'est détecté
if (p == FINGERPRINT_NOFINGER) {
lcd.setCursor(0, 2); lcd.print("Pas de doigt... ");
}
else {
// Si un doigt est détecté mais qu'il y a des erreurs
if (p == FINGERPRINT_OK) {
lcd.setCursor(0, 2); lcd.print("Doigt detecte! ");
p = finger.image2Tz();
if (p == FINGERPRINT_OK) {
p = finger.fingerFastSearch();
if (p == FINGERPRINT_OK) {
lcd.setCursor(0, 2); lcd.print("Empreinte validee! ");
delay(1000);
return finger.fingerID; // Retourne l'ID du client trouvé
} else {
// Si l'empreinte n'a pas été trouvée dans la base de données
lcd.setCursor(0, 2); lcd.print("Empreinte invalidee!");
delay(1000);
}
} else {
// Si une erreur survient dans la conversion de l'image
lcd.setCursor(0, 2); lcd.print("Erreur conversion ");
}
} else {
// Si une erreur survient lors de la capture de l'image
lcd.setCursor(0, 2); lcd.print("Erreur capture! ");
}
}
delay(1000); // Pause avant une nouvelle tentative
}
return -1; // Retourne -1 si aucune empreinte n'a été trouvée après 3 tentatives
}
// Fonction pour ouvrir l'électrovanne
void openElectroValve() {
digitalWrite(ELECTROVALVE_PIN, LOW);
electroValveOpen = true;
lcd.setCursor(0, 1); lcd.print("Robinet ouvert!");
}
// Fonction pour fermer l'électrovanne
void closeElectroValve() {
digitalWrite(ELECTROVALVE_PIN, HIGH);
electroValveOpen = false;
lcd.setCursor(0, 1); lcd.print("Robinet ferme!");
}
// Fonction pour mesurer le débit et mettre à jour l'eau restante
void measureFlow(int clientID) {
flowRate = (countImpulsions / calibrationFactor); // Calculer le débit en L/min
remainingWater -= flowRate / 60.0; // Décrémenter l'eau restante (par seconde)
clientData[clientID].waterVolume = remainingWater;
// Affichage sur le LCD
lcd.setCursor(0, 3);
lcd.print("Reste: "); lcd.print(remainingWater, 2); lcd.print("L");
countImpulsions = 0; // Réinitialiser les impulsions
}
// Fonction pour mettre à jour les données du client
void updateClientData(int clientID) {
Time t = rtc.getTime();
clientData[clientID].waterVolume = remainingWater;
clientData[clientID].date = t.date;
clientData[clientID].mon = t.mon;
clientData[clientID].year = t.year;
clientData[clientID].hour = t.hour;
clientData[clientID].min = t.min;
clientData[clientID].sec = t.sec;
bool saveOk = saveClientData(clientID, remainingWater, clientData[clientID].date, clientData[clientID].mon, clientData[clientID].year, clientData[clientID].hour, clientData[clientID].min, clientData[clientID].sec);
lcd.clear();
if (saveOk) {
lcd.setCursor(0, 1); lcd.print("Donnees actualisees");
lcd.setCursor(0, 2); lcd.print(" Au revoir!!!");
} else {
lcd.setCursor(0, 0); lcd.print("Erreur de carte SD!");
lcd.setCursor(0, 1); lcd.print("Actualisation echoue");
lcd.setCursor(0, 2); lcd.print("RETENEZ VOS DONNEES!");
// IL FAUT UN BOUTON POUR QUITTER AVEC UN CODE DE CONFIRMATION
}
delay(2000);
systemInactive = true; // Revenir à l'écran d'accueil
}
// Fonction pour réinitialiser les données d'un client
void resetClientData(int clientID) {
deleteFingerprint(clientID); // Suppression de l'ID dans le capteur d'empreinte
clientData[clientID-1].waterVolume = 0.0;
clientData[clientID-1].hour = 0;
clientData[clientID-1].min = 0;
clientData[clientID-1].sec = 0;
clientData[clientID-1].date = 0;
clientData[clientID-1].mon = 0;
clientData[clientID-1].year = 0;
delay(100);
bool saveOk = saveClientData(clientID-1, 0, 0, 0, 0, 0, 0, 0); // Réinitialiser les données sur la carte SD
lcd.clear();
if (saveOk) {
lcd.setCursor(0, 0); lcd.print("Empreinte supprimee");
delay(1500);
lcd.setCursor(0, 1); lcd.print(" Donneex vides!");
delay(1500);
lcd.setCursor(0, 2); lcd.print(" Au revoir!!!");
delay(2000);
} else {
lcd.setCursor(0, 0); lcd.print("Erreur de carte SD");
lcd.setCursor(0, 1); lcd.print("RETENEZ VOS DONNEES!");
// IL FAUT UN BOUTON POUR QUITTER AVEC UN CODE DE CONFIRMATION
}
delay(2000);
lcd.clear();
systemInactive = true; // Revenir à l'écran d'accueil
}
/*******FONCTION POUR SUPPRIMER UNE EMPREINTE********/
void deleteFingerprint(int clientID) {
uint8_t p = finger.deleteModel(clientID); // Suppression de l'empreinte
if (p == FINGERPRINT_OK) {
lcd.setCursor(0, 2); lcd.print("Empreinte supprimee");
lcd.setCursor(9, 2); lcd.print(clientID);
} else if (p == FINGERPRINT_PACKETRECIEVEERR) {
lcd.setCursor(0, 2); lcd.print("Erreur de comm.");
} else if (p == FINGERPRINT_BADLOCATION) {
lcd.setCursor(0, 2); lcd.print("Erreur emplacement");
} else if (p == FINGERPRINT_FLASHERR) {
lcd.setCursor(0, 2); lcd.print("Erreur ecriture flash");
} else {
lcd.setCursor(0, 2); lcd.print("Erreur inconnue");
}
}