Contrôler débit eau

Bonjour,
Je souhaite contrôler le débit d'eau dans le cadre de l'arrosage de mes légumes, gestion assurée par un automatisme d'irrigation de 6 zones
J'ai besoin de votre aide pour piloter correctement la fermeture de la vanne principale si le débit dépasse un certain seuil (fixé à 1 litre/minute dans le code)
Je vous joins mon code, et merci par avance pour comprendre mon erreur
Amicalement
Robert

/*
  Capteur de débit d'eau YF‐ S201 & LCD 20/04
  Sortie du capteur de débit d'eau traitée pour lire en litres/heure
  Courtoisie d'adaptation: hobbytronics.co.uk
*/
volatile int flow_frequency; // Mesure les impulsions du capteur de débit
float vol = 0.0, l_minute;  // Litres/heure calculés
unsigned char flowsensor = 2; // Entrée du capteur
unsigned long currentTime;
unsigned long cloopTime;
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 9);
int PinElectrovanne = 7;
void flow () // Fonction d'interruption
{
  flow_frequency++;
}
void setup()
{
  pinMode(PinElectrovanne, OUTPUT);
  pinMode(flowsensor, INPUT);
  digitalWrite(flowsensor, HIGH); // Pull-up interne en option
  Serial.begin(9600);
  lcd.begin(20, 4);
  attachInterrupt(digitalPinToInterrupt(flowsensor), flow, RISING); // Interruption de configuration
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Water Flow Meter");
  lcd.setCursor(0, 1);
  lcd.print("Circuit Digest");
  currentTime = millis();
  cloopTime = currentTime;
}
void loop ()
{
  currentTime = millis();
  // Chaque seconde, calculez et imprimez des litres/heure
  if (currentTime >= (cloopTime + 1000))
  {
    cloopTime = currentTime; // CloopTime met à jour
    if (flow_frequency != 0) {
      // Fréquence d'impulsion (Hz) = 7,5Q, Q est le débit en L/min.
      l_minute = (flow_frequency / 7.5); // (Fréquence d'impulsion x 60 min) / 7,5Q = débit en L/heure

     
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Debit: ");
      lcd.print(l_minute);
      lcd.print(" L/M");
      l_minute = l_minute / 60;
      lcd.setCursor(0, 1);
      vol = vol + l_minute;
      lcd.print("Vol:");
      lcd.print(vol);
      lcd.print(" L");
      flow_frequency = 0; // Réinitialiser le compteur
      Serial.print(l_minute, DEC); // Impression litres/heure
      Serial.println(" L/Sec");  
lcd.setCursor(0, 3);
lcd.print(l_minute);
Serial.print(l_minute, DEC); // Impression litres/heure
     
  
    }
      else {
        Serial.println(" flow rate = 0 ");
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Debit: ");
        lcd.print( flow_frequency );
        lcd.print(" L/M");
        lcd.setCursor(0, 1);
        lcd.print("Vol:");
        lcd.print(vol);
        lcd.print(" L");
      }
       if ((l_minute ) > 1)
     {digitalWrite(PinElectrovanne, HIGH);
    //delay(2000);
     }
       else{
        digitalWrite(PinElectrovanne, LOW);
     //delay(2000);
     }

      }
    }

Bonsoir;

On ne sait pas ce qui ne marche pas, et on n'a pas de schéma de câblage!
Est-ceà à nous de deviner en espérant qu'il corresponde à votre code?

Dans le code, j'ai relevé des contradictions:

unsigned char flowsensor = 2;


pinMode(flowsensor, INPUT);
digitalWrite(flowsensor, HIGH);
  • "flowsensor", c'est le NUMERO d'une entrée.
  • On ne peut pas écrite (digitalWrite) dans une entrée!

Bonjour,

Deux grosses erreurs:
Tu calcules le nombre de litres par minutes que tu ajoutes au volume toutes les secondes ce qui est déjà faux.
Ensuite tu calcules le nombre de litres par seconde et tu continues à l'appeler l_minute ce qui fait que dans ta comparaison tu compares le nombre de litres par seconde à 1.

Il faudrait faire:

      l_minute = flow_frequency / 7.5;  // (Fréquence d'impulsion x 60 min) / 7,5Q = débit en L/heure
      float l_sec=l_minute/60.0;
      vol+=l_sec;

D'autres remarques:

  • pourquoi avoir fait un test flow_frequency !=0 ? les calculs sont les mêmes dans les deux cas.
  • Les calculs son noyés dans l'affichage. Sépare les calcul de l'affichage.

Il reste d'autres points à améliorer.

Je me permet une remarque:

La définition de algorithme;
"Un algorithme est la description d'une suite d'étapes permettant d'obtenir un résultat à partir d'éléments fournis en entrée. Par exemple, une recette de cuisine est un algorithme permettant d'obtenir un plat à partir de ses ingrédients!"

Moi je ne suis pas un bon cuistot, mais si vous voulez faire des oeufs à la coque, et que votre recette dit de les cuire â l'eau bouillante 10 mn, si vous me demandez ce qui ne va pas dans vos oeufs, je vous dirais que vos oeufs durs ne sont pas mauvais...
pour les oeufs coque, revoyez l'algorithme !

Là vous nous donnez votre code, mais pas l'algorithme que vous vouliez créer

Si ça se trouve, il est faux, et j'ai bien l'impression que OUI!

Pouvez vous SVP nous décrire l'algorithme que vous avez imaginé pour ne pas dépasser 1l/mn?

On pourra vous dire si elle est juste, et si votre code lui correspond.

Comme quoi, il faut toujours faire attention aux unités de mesures et les expliciter dans les calculs..,
Ça arrive même aux meilleurs : Mars Climate Orbiter — Wikipédia

Je suis d'accord avec @jef59 , tu ne décris pas ce que vous voulez faire.
Si tu fermes la vanne quand le débit est supérieur à 1 l/min, le débit va tomber à 0 donc tu vas rouvrir la vanne et le débit va toujours être supérieur à 1l/min ce qui va provoquer la fermeture de la vanne ...

Je crois que c'est ce qu'il fait si on enlève la pollution du LCD, ?

      l_minute = (flow_frequency / 7.5); // (Fréquence d'impulsion x 60 min) / 7,5Q = débit en L/heure
      l_minute = l_minute / 60;
      vol = vol + l_minute;

Je pense que son soucis, c'est qu'il veut limiter le débit à 1 L/min, uniquement en jouant sur une valeur en L/s et donc sans savoir l'impact de couper le débit pendant x secondes.
Sans parler que l_minute est en L/s à la fin du calcule et avant de vérifier si il est supérieur à 1.

on en revient donc au commentaire de @jef59
Je n'ai pas testé, mais pour résoudre ce problème, j'essaierais d'avoir une moyenne glissante avec 2 points.
On commence à 1L/min, pour savoir si on éteint sur la prochaine seconde on prend la moyenne à la quelle on ajoute l'instantané en L/min et on devise par deux, si c'est supérieur à 1, on stoppe.
Au prochain tour le débit instantané serait de 0, donc le débit moyenne de 0,5, donc on réouvre.
A vérifier que cela fonctionne :slight_smile:
Le défaut majeur c'est qu'on doit faire travailler l'électrovanne souvent.
Une approche travaillant sur la minute voir, l'heure serait plus indiqué pour préserver le mécanisme.

Oui pour le calcul du volume. Avec le débit par seconde qui s’appelle l_minute et l'imbrication des calculs dans l’affichage j'ai conclu un peu vite à une erreur sur le calcul du volume.
Mais il reste l'erreur principale dans la commande de l'électrovanne ou il compare un débit en l/sec à une limite en l/min.

Bonjour,

On est bien obligé de faire avec ce que l'on a.

Si il y a une vanne tout ou rien, on ne peux pas dans le sens stricte du terme regulé le débit.

Par contre on peux réguler le volume et jongler avec.

1l/mn ca veut bien dire que en 1mn, j'ai laissé couler 1l.

EXEMPLE1:
-J'OUVRE la vanne et je démarre mon chrono
-Je mesure le débit (le capteur donne 444 impulsions par litre, quel que soit la vitesse du débit.
-dans mon exemple, il se trouve que au bout de 10 secondes, j'ai compté 444 impulsions (soit 1l)
_ALORS JE FERME LA VANNE pendant 50s

RÉSULTAT:
-en 1 minute, il s'est écoulé 1l
-je remet à 0 mon compteur de volume d'eau et mon chrono
-et je recommence

EXEMPLE2:

-J'OUVRE la vanne et je démarre mon chrono
-Je mesure le débit (le capteur donne 444 impulsions par litre, quel que soit la vitesse du débit.
-dans mon exemple, il se trouve que au bout de 60 secondes, j'ai compté 100 impulsions (soit 0,22l)
-ALORS JE FERME LA VANNE pendant 0s (pour garder la même structure que le premier exemple)

RÉSULTAT:
-en 1 minute, il s'est écoulé 0,22l et je n'y peut rien!
-je remet à 0 mon compteur de volume d'eau et mon chrono
-et je recommence

Ça se serait mon algorithme

Si on trouve que 1mn c'est pas assez ou trop, on change la période, et on change le seuile de comparaison, 1l/mn, ce n'est que 0,5l/30s ou 60l/h.

Si c'est pour arroser un milieu qui absorbe plus ou moins lentement l'eau qu'on lui apporte, pas besoin de l'arroser toutes les secondes non?

Bonjour
Tout d'abord merci pour vos réponses, et mes excuses pour ne pas avoir répondu plus tôt
Entre temps, mon projet a mûri et mes recherches ont un peu mieux abouti, notamment après avoir trouvé un projet similaire à mon besoin, celui de jeje95150 qui traite un seul débit anormal
Par contre je cherche à détecter un débit anormal d'un arrosage automatique pour 6 zones avec un débit différent sur chaque zone, le tout piloté par 6 électrovannes, mais alimenté par une seule vanne motorisée
Dans mon code, je dois détecter un débit anormal d'une zone quand celle-ci est activée (donc fermeture de la vanne motorisée), tout en ne bloquant pas l'arrosage des autres zones quand elles seront à leur tour activées par le programmateur d'arrosage (aucune superposition horaires des zones)
La zone en défaut ne sera réactivée qu'avec l'action d'un bouton poussoir
Dans le code ci dessous, je n'ai simulé que 2 zones, et déjà le code me parait long
Ai-je choisi la bonne méthode de programmation ou avez-vous des idées pour qu'il soit plus condensé, en sachant que je prévois à terme d'avoir 10 zones à gérer
Merci par avance de votre aide

 //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 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);
  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(brocheZone1) == 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++;
}


Intéresse toi aux listes, et aux opérations sur les listes.

Par exemple tu a définis 6 variables ayant chaqune 1 nom et 1 valeur que tu décrit dans setup
const int brocheZone1 = 5
...
cons int brocheZone6 = 10

Tu aurais pu créer une liste comme celle ci
const int brocheZone[] {0,5,6,7,8,9,10}

Dans ce tableau, il y a 7 variable
brocheZone[0] vaut 0
brocheZone[1] vaut 5
...
bricheZone[4] vaut 8

Dans le setup, tu fais une boucle

for (int i = 1; i<7; i++){
pinMode(brocheZone[i], INPUT);
}

Ici, quand i vaut 4, on va chercher dans le tableau brocheZone[] = {0,5,6,7,8,9,10}
La 4éme valeur entre les { }, c'est à dire 8, et on l'attribue à brocheZone[4] (ce que toi tu écris brocheZone4 = 8)
...

Là, tu n'as que 6 broches à mettre en entrées, mais si tu en avais 100, la structure reste la même, avec seulement 3 lignes

Ça c'est un exemple qui s'applique à toutes séquences qui se répètent.

Le tout est de bien penser à définir les listes avant le Setup, et à correctement défînir l'incrément dans la boucle.

Bref, je ne suis peut-être pas au top dans mes explications.

Mais j'ai utilisé ce que je décris, et c'est top pour les choses répétitives à écrire et à debugger ou modifier

Si ton soucis est la multiplication des variables pour chaque zone, une solution est de définir une structure.

struct Zone {
  int brocheZone;
  float seuilDebitCoupeZone1;
  bool active;
};

struct Zone zone1 = {5, 1.1, false};
struct Zone zone2 = {6, 2.2, false};

Bien sûre tu peux en plus appliqué la remarque de @jef59 sur l'utilisation d'un tableau.

struct Zone zones[2] = {{5, 1.1, false}, {6, 2.2, false}};

Je ne vois pas trop comment ton programme ferra la différence entre les zones avec un seul débitmètre lorsque plusieurs zone fonctionne en même temps?
J'ai survolé ton programme, peut être ai-je loupé un truc?

bobardui a écrit

Donc ça devrait marcher dans l'idée...

Donc le blocage évoqué de ne devrait pas arriver :sweat_smile:
Si la détection ne ce fait que sur la zone activée ?

Bonjour Terwal ,
Quelle est la différence entre la proposition de Jef59 basé sur une boucle "For" et la tienne sous forme d'une structure, tout en sachant que c'est la programmation dans "Void Loop" qui me pose problème avec 6, voire 10 zones à surveiller
Je te confirme aussi qu'il n'y aura aucune superposition horaire des différentes zones, ce qui sera plus facile à identifier le débit par Zone

Bonjour Jef59
ta proposition d'une boucle "For" m'a effleuré l'esprit, mais comme je ne suis pas un pro, je n'ai pas pu la mettre en œuvre, surtout dans le "Void Loop"

Autre question : existe-t-il un simulateur pour essayé mon code sans que je sois obligé de souffler dans le débitmètre pour avoir un débit ?

La différence, si différence il y a, c'est que si tu fais un tableau de structure, cela t'évite d'avoir deux tableaux, voir plus si tu dois rajouter d'autre propriété à chaque zone.
Mais c'est plutôt complémentaire.

C'est à dire peut tu nous montrer ce que tu as fait?

Oui, il y a wokwi

Bonjour,

Comme te l'a dit @terwal c'est complémentaire.
Il faut utiliser for si tu ne veux pas dupliquer 6 fois ton code. Et optionnellement utiliser une structure permet de simplifier ton code en ayant un seul tableau au lieu de 3 ou 4.

Tu pourrais aussi utiliser une class avec des méthodes mais c'est une programmation un peu plus avancée.

1 Like

Bonjour,

Je fais une petite digression:
Sans parler d'intelligence, je dirais juste que l'on a tous un systéme cognitif DIFFERENT, ce qui fait par exemple que certains dits surdoués ne savent toujours pas, même à 20 ans, nouer leurs lacets.
Même si tu leurs explique une 100.001 fois.

Il y a une solution parmis tant d'autres à cela, c'est de mettre "les mains dans le cambouis".

Les "struct", c'est quelque chose que je ne connais pas et ça à l'air (c'est peu de le dire) vachement bien structuré.

Mais perso j'en comprendrais bien toutes les subtilités que si j'en construit une de structure (apprendre par l'essai), pour l'instant mon systéme cognitif me dit "Je sais que ça existe, passons autre chose".

Bref.

Pour la simulation, et éviter de perdre son souffle.

Une petite idée, pour apprendre le code en même temps.

Construit toi un tout petit simulateur de capteur de débit.

De même que dans ton programme, tu as une void loop(), fais toi une "void simulationDebit()"

Et dans ta "void loop", tu rajoute une ligne qui appelle "simulationDebit()"

En fait, c'est quoi un capteur de débit?
C'est juste une boîte noire qui envoit un nombre précis d'impulsions par litre d'eau qui défile dans son corps, je crois que c'est 444 impulsions = 1 litre.

Si toi tu veut aller par exemple jusqu'à 10l minutes, il faudrait que tu puisse simuler un "truc" qui t'envoie 4440 impulsions en 1 mn, 4440/60=74, la fréquence, c'est le nombre d'impulsions/s, donc 74, c'est des Hz, c'est la fréquence.
Pour revenir à ton simulateur, si tu arrive à créer un systéme qui envoit un signal de fréquence comprise entre 0 et 100Hz, c'est dans la poche (équivalent à un débit de 0 à 13l/mn).
Regarde entre autre la fonction tone(), elle sait faire cela.

Ce n'est pas un exercice très difficile, j'en connais qui serait ravis de te faire et expliquer cela dans les détails.

Tu peux donc créer un signal numérique sur une broche en sortie pour simuler le signal de sortie du capteur, et pour tes essais, tu branche cette broche sur ton Arduino à la place du capteur.

C'est une idée comme une autre.

Bonjour,
Hors des explications justifiées des précédents intervenants:

Ton problème c’est l’organisation globale de ton code

Penses à ce problème j’ai 100 rectangles dont je veux calculer le périmètre et la surface tu ne vas pas faire
rect1 : périmètre = l+L2 surface = lL
100 fois de suite les deux formules
mais faire une fonction qui calcule le périmètre et une fonction qui calcule la surface en prenant en paramètres nom, largeur et longueur du rectangle

Ton problème est le même j’ai 10 réseaux avec pour conditions...etc. Je fais 10 fois les opérations ou j’écris une fonction qui fait les opérations 1fois et je l’appelle quand je le souhaite.

Je pense que des pros du forum doivent connaître des tuto qui explique facilement ce principe sans entrer dans la programmation modulaire de gros codes.