Contrôler débit eau

n'étant pas assez expérimenté, je comprends vos commentaires, mais je n'arrive pas à les mettre en forme pour comprendre et valider un choix dans mon code
Le principe de structure pourrait être intéressant dans une version évolutive, mais quoi inscrire ou comment programmer dans le Setup et dans le loop
Pour ce qui est du For, ci dessous le code qui ne fonctionne pas dans le Void Loop......mais où est l'erreur ?
Merci de votre retour

//detection de fuite avec bouton de réactivation pour plusieurs Zones avec plusieurs seuils de débit maxi

#include <Wire.h>  // Bibliothèque pour la communication I2C
#include <LiquidCrystal_I2C.h>  // Bibliothèque pour le contrôle de l'écran LCD en mode I2C

LiquidCrystal_I2C lcd(0x27, 16, 2); // Adresse I2C de l'écran LCD 16x2 

const int brocheDebit = 2;           // Broche de la sonde de débit d'eau
const int brocheElectrovanne = 3;    // Broche pour contrôler l'électrovanne
const int brocheBouton = 4;          // Broche du bouton-poussoir pour remise à zéro
const int brocheZone[] {0,5,6,7,8,9,10}; // la broche 0 correspond à la zone 0, la N° 5 à la Zone 2, etc... 
//const int brocheZone1 = 5;          // Broche d'information de l'arrosage de la Zone 1
//const int brocheZone2 = 6;          // Broche d'information de l'arrosage de la Zone 2
//const int brocheZone3 = 7;          // Broche d'information de l'arrosage de la Zone 3
//const int brocheZone4 = 8;          // Broche d'information de l'arrosage de la Zone 4
//const int brocheZone5 = 9;          // Broche d'information de l'arrosage de la Zone 5
//const int brocheZone6 = 10;         // Broche d'information de l'arrosage de la Zone 6
unsigned int compteImpulsions = 0;
float debitInstantane = 0.0;
float totalMetresCubes = 0.0; // Changement d'unité de litres à mètres cubes
unsigned long tempsPrecedent = 0;
unsigned long tempsDebutDebit = 0;
unsigned long tempsDebutEtat = 0;
const long intervalle = 500;  // Intervalle d'échantillonnage en millisecondes
const float seuilDebitCoupeZone1 = 1.1; //Débit maxi pour la zone 1
const float seuilDebitCoupeZone2 = 2.1; //Débit maxi pour la zone 2
const float seuilDebitCoupeZone3 = 5.1; //Débit maxi pour la zone 3
const float seuilDebitCoupeZone4 = 4.1; //Débit maxi pour la zone 4
const float seuilDebitCoupeZone5 = 3.1; //Débit maxi pour la zone 5
const float seuilDebitCoupeZone6 = 6.1; //Débit maxi pour la zone 6
const unsigned long delaiFermetureElectrovanne = 1000;  // Délai de fermeture de l'électrovanne en millisecondes

enum Etat { REPOS, ACTIF, STOP };
Etat etat = REPOS;



void setup() {
  lcd.begin(16, 2);  // Initialisation de l'écran LCD 16x2
  lcd.print("Debit: 0.0 m3/h");
  lcd.setCursor(0, 1);
  lcd.print("Total: 0.0 m3");

  pinMode(brocheDebit, INPUT);
  pinMode(brocheElectrovanne, OUTPUT);
  pinMode(brocheBouton, INPUT_PULLUP);
  for (int i = 1; i<7; i++){
  pinMode(brocheZone[i], INPUT);
  }

  //pinMode(brocheZone1, INPUT);
  //pinMode(brocheZone2, INPUT);
  //pinMode(brocheZone3, INPUT);
  //pinMode(brocheZone4, INPUT);
  //pinMode(brocheZone5, INPUT);
  //pinMode(brocheZone6, INPUT);


 
}

void loop() {
  unsigned long tempsActuel = millis();  // Obtient le temps actuel en millisecondes


     if(digitalRead(brocheZone[i] == HIGH)){   //Lecture de l'information de l'arrosage de la Zone 1
      
      
  switch (etat) {
    case REPOS:
    digitalWrite(brocheElectrovanne, HIGH);
      if (debitInstantane > seuilDebitCoupeZone1) {
        tempsDebutDebit = tempsActuel;
        etat = ACTIF;
      }
      break;

    case ACTIF:
      if (debitInstantane > seuilDebitCoupeZone1) {
        if (tempsActuel - tempsDebutDebit >= delaiFermetureElectrovanne) {
          digitalWrite(brocheElectrovanne, LOW);
          etat = STOP;
          tempsDebutEtat = tempsActuel;
        }
      } else {
        etat = REPOS;
      }
      break;

    case STOP:
      //lcd.clear();  // Effacer l'écran après le message de démarrage
      lcd.setCursor(0, 0);
      lcd.print("Fuite Zone1 ");  // Affiche un avertissement de fuite
      // Nouvelle condition pour la réinitialisation avec maintien de 2 secondes
      if (digitalRead(brocheBouton) == LOW) {
        if (tempsActuel - tempsDebutEtat >= 2000) {
          digitalWrite(brocheElectrovanne, HIGH);
          etat = REPOS;
          lcd.setCursor(0, 0);
          lcd.print("                 ");  // Efface le texte "Fuite" lors du retour à l'état REPOS
          lcd.setCursor(0, 1);
          lcd.print("                 ");  // Efface le texte "Fuite" lors du retour à l'état REPOS
        }
      } else {
        tempsDebutEtat = tempsActuel;
      }
      break;
  }
  }

 /*    if(digitalRead(brocheZone2) == HIGH){   //Lecture de l'information de l'arrosage de la Zone 2
      
      
  switch (etat) {
    case REPOS:
    digitalWrite(brocheElectrovanne, HIGH);
      if (debitInstantane > seuilDebitCoupeZone2) {
        tempsDebutDebit = tempsActuel;
        etat = ACTIF;
      }
      break;

    case ACTIF:
      if (debitInstantane > seuilDebitCoupeZone2) {
        if (tempsActuel - tempsDebutDebit >= delaiFermetureElectrovanne) {
          digitalWrite(brocheElectrovanne, LOW);
          etat = STOP;
          tempsDebutEtat = tempsActuel;
        }
      } else {
        etat = REPOS;
      }
      break;

    case STOP:
      //lcd.clear();  // Effacer l'écran après le message de démarrage
      lcd.setCursor(0, 0);
      lcd.print("Fuite Zone2 ");  // Affiche un avertissement de fuite
      // Nouvelle condition pour la réinitialisation avec maintien de 2 secondes
      if (digitalRead(brocheBouton) == LOW) {
        if (tempsActuel - tempsDebutEtat >= 2000) {
          digitalWrite(brocheElectrovanne, HIGH);
          etat = REPOS;
          lcd.setCursor(0, 0);
          lcd.print("                 ");  // Efface le texte "Fuite" lors du retour à l'état REPOS
          lcd.setCursor(0, 1);
          lcd.print("                 ");  // Efface le texte "Fuite" lors du retour à l'état REPOS
        }
      } else {
        tempsDebutEtat = tempsActuel;
      }
      break;

  }
     
  }
*/
  if (tempsActuel - tempsPrecedent >= intervalle) {
    detachInterrupt(digitalPinToInterrupt(brocheDebit));

    debitInstantane = compteImpulsions / 7.5;  // Conversion du nombre d'impulsions en débit instantané en mètres cubes par heure
    totalMetresCubes += (debitInstantane / 3600.0);  // Ajout du débit instantané au total en mètres cubes

    lcd.clear();
    lcd.print("Debit: ");
    lcd.print(debitInstantane, 1);
    lcd.print(" m3/h");

    lcd.setCursor(0, 1);
    lcd.print("Total: ");
    lcd.print(totalMetresCubes, 1);
    lcd.print(" m3");

    compteImpulsions = 0;
    tempsPrecedent = tempsActuel;

    attachInterrupt(digitalPinToInterrupt(brocheDebit), compteurImpulsions, FALLING);
  }
}

void compteurImpulsions() {
  compteImpulsions++;
}

Il manque un égal ci dessus.
const int brocheZone[]={0,5,6,7,8,9,10}; // la broche 0 correspond à la zone 0, la N° 5 à la Zone 2, etc...

Voici un petit exemple. J'ai repris ton programme du post #10 pour utiliser une structure et une boucle for.

//detection de fuite avec bouton de réactivation pour plusieurs Zones avec plusieurs seuils de débit maxi

#include <Wire.h>               // Bibliothèque pour la communication I2C
#include <LiquidCrystal_I2C.h>  // Bibliothèque pour le contrôle de l'écran LCD en mode I2C

LiquidCrystal_I2C lcd(0x27, 16, 2);  // Adresse I2C de l'écran LCD 16x2

const int brocheDebit = 2;         // Broche de la sonde de débit d'eau
const int brocheElectrovanne = 3;  // Broche pour contrôler l'électrovanne
const int brocheBouton = 4;        // Broche du bouton-poussoir pour remise à zéro

unsigned int compteImpulsions = 0;
float debitInstantane = 0.0;
float totalMetresCubes = 0.0;  // Changement d'unité de litres à mètres cubes
unsigned long tempsPrecedent = 0;
const long intervalle = 500;                            // Intervalle d'échantillonnage en millisecondes
const unsigned long delaiFermetureElectrovanne = 1000;  // Délai de fermeture de l'électrovanne en millisecondes

enum Etat { REPOS,
            ACTIF,
            STOP };

struct SZone {
  const byte broche;
  const float seuilDebitCoupe;
  Etat etat;
  unsigned long tempsDebutDebit;
  unsigned long tempsDebutEtat;
};

SZone zones[] = {
  {5, 1.1, REPOS, 0, 0},
  {6, 2.1, REPOS, 0, 0},
  {7, 3.1, REPOS, 0, 0},
  {8, 4.1, REPOS, 0, 0},
  {9, 5.1, REPOS, 0, 0},
  {10, 6.1, REPOS, 0, 0}
};


void setup() {
  lcd.begin(16, 2);  // Initialisation de l'écran LCD 16x2
  lcd.print("Debit: 0.0 m3/h");
  lcd.setCursor(0, 1);
  lcd.print("Total: 0.0 m3");

  pinMode(brocheDebit, INPUT);
  pinMode(brocheElectrovanne, OUTPUT);
  pinMode(brocheBouton, INPUT_PULLUP);
  
  attachInterrupt(digitalPinToInterrupt(brocheDebit), compteurImpulsions, FALLING);
}

void loop() {
  unsigned long tempsActuel = millis();  // Obtient le temps actuel en millisecondes

  if (tempsActuel - tempsPrecedent >= intervalle) {
    // calculk et affichage du débit
    noInterrupts();
    debitInstantane = compteImpulsions / 7.5;  // Conversion du nombre d'impulsions en débit instantané en mètres cubes par heure
    compteImpulsions = 0;
    tempsPrecedent = tempsActuel;
    interrupts();

    totalMetresCubes += (debitInstantane / 3600.0);  // Ajout du débit instantané au total en mètres cubes

    lcd.clear();
    lcd.print("Debit: ");
    lcd.print(debitInstantane, 1);
    lcd.print(" m3/h");

    lcd.setCursor(0, 1);
    lcd.print("Total: ");
    lcd.print(totalMetresCubes, 1);
    lcd.print(" m3");
  }

  for (auto &zone : zones)  // effectue le traitement pour toutes les zones
  {

    if (digitalRead(zone.broche) == HIGH) {  //Lecture de l'information de l'arrosage de la Zone

      switch (zone.etat) {
        case REPOS:
          digitalWrite(brocheElectrovanne, HIGH);
          if (debitInstantane > zone.seuilDebitCoupe) {
            zone.tempsDebutDebit = tempsActuel;
            zone.etat = ACTIF;
          }
          break;

        case ACTIF:
          if (debitInstantane > zone.seuilDebitCoupe) {
            if (tempsActuel - zone.tempsDebutDebit >= delaiFermetureElectrovanne) {
              digitalWrite(brocheElectrovanne, LOW);
              zone.etat = STOP;
              zone.tempsDebutEtat = tempsActuel;
            }
          } else {
            zone.etat = REPOS;
          }
          break;

        case STOP:
          //lcd.clear();  // Effacer l'écran après le message de démarrage
          lcd.setCursor(0, 0);
          lcd.print("Fuite Zone");       // Affiche un avertissement de fuite
          lcd.print(&zone - zones + 1);  // affiche numéro de zone
          // Nouvelle condition pour la réinitialisation avec maintien de 2 secondes
          if (digitalRead(brocheBouton) == LOW) {
            if (tempsActuel - zone.tempsDebutEtat >= 2000) {
              digitalWrite(brocheElectrovanne, HIGH);
              zone.etat = REPOS;
              lcd.setCursor(0, 0);
              lcd.print("                 ");  // Efface le texte "Fuite" lors du retour à l'état REPOS
              lcd.setCursor(0, 1);
              lcd.print("                 ");  // Efface le texte "Fuite" lors du retour à l'état REPOS
            }
          } else {
            zone.tempsDebutEtat = tempsActuel;
          }
          break;
      }
    }
  }
}

  void compteurImpulsions() {
    compteImpulsions++;
  }

Hello,

Je vois bien que tu n'y arrive pas, la difficulté est aussi d'arriver à te faire comprendre.

Excuse moi si je me répete.

Quand tu ècris

brocheZone[] = {0,5,6,7,8,9,10};
Tu décris un tableau de 7 variables
brocheZone[0], qui ici vaut 0
brocheZone[1], qui ici vaut 5
...
brocheZone[6] , qui ici vaut 10

Tu peux faire pareil avec

seuilDebitCoupeZone[] = {0, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1}

Après je te donne un exemple bidon, et en plus je n'ai pas mon PC pour l'écrire!

J'ai disons une action que je dois répeter 6 fois, donc je fais une boucle de 1 à 6 (ou à inférieur à 7

for (int i = 1; i<7; i++)
Si tu veut, "i", 0s'est le curseur, il va de 1 à 6

Dans cette boucle j'écris ce que je dois faire, par exemple

for (int i = 1; i<7; i++) { // je repéte 6 fois

if (debitInstantane > seuilDebitCoupeZone[i] {
jeFaisCeQueJeDoisFaire(); // fonction bidon inventée pour l'exemple
}

}

Au départ, i=1
Donc seuilDebitCoupeZone[1] = 1.1
i++, ça veut dire que i=i+1 Cad 1+1 = 2
Pour i=2, seuilDebitCoupeZone[2] = 2.1
i++ donne 2+1 = 3
...
Pour i=6, seuilDebitCoupeZone[6] = 6.1
i++ 6+1 = 7, ce n'est plus <7 donc la boucle for s'arrête.

J'ais fais 6 fois ce que je n'ais écris qu'une seule fois..

TOI TU AS OUBLIÉ LE FOR dans to code.

Merci Kamill pour ton aide, ce code fonctionne très bien, mais sans abuser de ta gentillesse, j'aimerai comprendre, et ne pas faire que copier un code.
Quelques questions :
struct SZone { : Le "S" majuscule doit toujours être devant le nom de la structure ?
Etat etat; le "Etat" correspond à celui qui est dans "enum Etat" ? si oui l'autre "etat" correspond aux différents états du "Switch" ?

for (auto &zone : zones) => explication des mots " auto " "zone" "zones"

Bon, je m'en arrête à cela pour l'instant en espérant trouver d'autres explication sur le net (un site à conseiller ? )
Merci

Oui : Locoduino.
Par exemple pour les enum : LOCODUINO - Trois façons de déclarer des constantes
Ou bien pour les structures : LOCODUINO - Les structures
Ou les pointeurs, qu'a utilisé @kamill dans son code : LOCODUINO - Les pointeurs (1)

C'est une mine d'or :grinning:

EDIT Bonjour @jef59
Je pense que vous êtes bien meilleur en développement que moi, mais cependant j'aimerai ajouter quelque chose vis-à-vis de ce bout de code que vous avez posté (et qui est parfaitement juste, ne vous méprenez pas ! :wink:)

Personnellement, au lieu de placer directement la taille du tableau dans le for (ici 7), j'utilise l'opérateur sizeof() - comme ça, si jamais je décide d'ajouter une pins dans le tableau de pins, je n'ai pas besoin de modifier aussi cette valeur dans le programme. C'est un détail, bien entendu, mais c'est toujours un petit truc pratique :slight_smile:

Cordialement
Pandaroux007

1 Like

Salut Jef59,
oui, j'ai bien oublié le For pour suivre le "seuilDebitCoupeZone" de toutes les zones
Mais je souhaitais d'abord comprendre le paramétrage de la première boucle For dans le "void loop",
Et là j'avais le message suivant "Compilation error: 'i' was not declared in this scope"
Donc, où déclarer le " i " ?
Puis avec deux boucles " For " , ne doit-on pas faire le " i ", puis le " j " ?
Merci par avance pour tes explications et ainsi être plus autonome

Merci, je vais me mettre au travail

Pour le SZone c'est un convention, tu es libre de faire ce que tu veux.
dans "Etat etat", le Etat représente le nom du type de ta variable, c'est exactement comme int dans "int count"
Donc etat représente le nom de la variable.
cette variable "etat" poura avoir effectivement les valeurs REPOS, ACTIF, STOP
tu peux par exemple faire:

if (etat == REPOS) {
  Serial.println("l'état est au repos");
}

Non, la structure peut avoir n'importe quel nom. C'est quand même bien d'avoir une notation homogène.
S c'est une convention (Microsoft) qui est utilisée dans les développements Windows et C pour class.

Oui c'est le nom de l'enum. En C il fallait préciser enum Etat (je pense que c'est aussi supporté en C++)

C'est une nouvelle syntaxe (enfin ça date déjà de quelques années). On fait la boucle pour tous les éléments du tableau zones. C'est l'équivalent du foreach de certains langages.
auto veut dire que zone aura le même type que zones. On aurait pu mettre SZone à la place.
zone est le nom d'une variable (peut être quelconque)
zones est le tableau créé au début du fichier.

Je ne suis en rien meilleur que qui que se soit, surtout en code, JE N'AI PAS HONTE DE LE DIRE, JE NE SUIS PAS UN CAÏD.
il y a quelques astuces (connues) que j'ai testées sur mes appli perso, qui marchent, et que je partage ici à ceux qui ne les connaissent pas, je ne connaissais pas l'astuce que vous décrivez, et je vais m'y pencher.

Merci à tous pour votre aide et explications qui me permettent d'y voir un peu plus clair
J'ai découvert d'autres possibilités à coder que je vais approfondir sur le site " locoduino"
J'ai aussi consulté Wokwi qui ne correspond que partiellement à un simulateur, en l'occurrence, je n'ai pas trouvé la simulation d'un débitmètre à moins que je n'ai pas trouvé la solution.
il me reste la solution proposé par Jef59, création d'un "void simulation débit" mais là je butte car je manque d'une explication claire sur le sujet

Effectivement wokwi ne propose pas de débitmètre mais il permet de créer ses propres "chips"
Voici une simulation avec un chip qui simule un générateur d'impulsion. Pour faire varier le débit cliquer sur le débitmètre.

Comme autre simulateur tu as aussi tinkercad qui propose des instruments, entre autres un générateur de fonctions.

Merci Kamill
tu es trop fort, j'aimerai en faire autant, mais la vie est trop courte, je n'aurai certainement pas assez de temps pour être à ton niveau

Ca va venir petit à petit.
Moi ça fait quelques dizaines d'années que je fais de la programmation.

Hello,

J'ais un petit sketch chez moi qui envoit un signal cerré de période variable en sortie 13, et que l'on peut connecter sur la broche 2 par exemple de n'importe lequel Arduino pour simuler physiquement un débitmétre, je le dépose ici, il y a quelques modifs faciles à faire.

Je le dépise ici au + vite.

A suivre.

Je pense progresser, mais déjà à plus de 65 ans, je n'arriverai pas à ton niveau

Bonsoir.

Chose promise...
Je ne suis pas un grand codeur, et ce n'est pas un generateur de signaux périodique de Grande vitesse, ca va bien pour simuler les débit de petits à 10-12 l/mn

Il y a surement des erreurs et des améliorations n'hésitez pas

/*
Generateur de fréquence de précision moyenne, allant de T = 20ms à ...
Sortie Géné sur broche 13 (Led integre sur UNO)
Relier la broche 13 à la broche 2 par un fil
adaptée la valeur T à votre souhait...
*/
unsigned long T = 10;// T(ms) = 135/Q(l/mn)
int interuptInPin = 2;
int freqOutPin = 13;

float t0 = 0;// départ chrono, passage à l'état HAUT
float t1 = T/2;// 1/2 période
float t2 = 0;// temps intermédiaire
float t3 = 0;// = t0 + t1, passage à l'état BAS
float t4 = 0;// = t0 + T, fin état BAS
float t5 = 0;
float t6 = 0;
float T0 = 0;
float temps = 0;
float frequence = 0;
float debit = 0;

void setup() {

  pinMode(interuptInPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(interuptInPin), mesureTemp, RISING);

  Serial.begin(9600);
  pinMode(freqOutPin, OUTPUT);
  
}

void loop() {

  digitalWrite(freqOutPin,HIGH);//mette sortie à niveau HAUT
  t0 = millis();// démarrer chrono
  t3 = t0+t1;// calcul temps 1/2 période
  t4 = t0+T;// calcul temps période compléte

  while (t2<t3){//attendre que t = 1/2 période
    t2=millis();// actualiser t2
    }
  t2=millis();// actualiser t2
  digitalWrite(freqOutPin,LOW);// mettre à 0

  while (t2<t4){// attendre que t = période compléte
    t2=millis();// actualiser t2
    }
  t2=millis();// actualiser t2
}

void mesureTemp(){

  t5 = t6;// mesurer la valeur d'avant
  t6 = millis();// démarrer chrono
  T0 = t6-t5;//période du signal entrant sur la broche2

  debit = 135/T0;
  
  Serial.println("-------------------------");
  Serial.print("Période= ");
  Serial.print(T0);
  Serial.println(" ms (+/-10%)");
  Serial.print("débit= ");
  Serial.print(debit);
  Serial.println(" l/mn (+/-10%)");
}

Bonsoir jef59

Moi, je ne suis pas un pro, non plus, mais quelques petites remarques, toutes les variables de temps Tx doivent être des unsigned long et, vu qu'elles sont traitées dans une interruption, en plus être volatiles:
volatile unsigned long temps = 0;

Mais plus "sérieux", on ne mets pas autant dans une interruption, surtout pas des print, on mets le strict minimum comme un flag qui "dit" une impulsion est arrivée et on traite ce flag dans loop().

PS: le sérial à 115200 c'est mieux :wink:
rePS: J'ai cru que ton programme ne fonctionnai pas, mais j'avais un problème de GND commun :woozy_face:

Cordialement
jpbbricole

Super, merci pour ces remarques, moi je prend en compte ces remarques.

C'est vrai, moins il y a de print dans l’interruption, plus la lecture serra juste, je l'avais constaté.

Pour qu'il soit facilement utilisable, il faudrait soit rajouter un potentiomètre pour faire une choix analogique de la valeur de débit à simuler.

Soit utiliser me moniteur série pour entrer et envoyer des valeur de débit (périodes) à simuler.

Et pour ceux qui ont 2 arduino, on peut utiliser l'un des 2 pour envoyer le signal de sortie de la broche 13 sur le signal d'entrée de la broche 2 (ou autre) de l'autre arduino sensé recevoir le signal d'un débitmétre.

Avec les