Pilotage de goutte à goutte avec économie d'eau


En été, les réserves d’eau de pluie ont souvent tendance à se vider plus vite qu’elles ne se reconstituent. Raison de plus pour qu’un système d’arrosage automatique prenne en compte l’humidité du sol pour déclencher ou non l’arrosage goutte à goutte.

Le système dont j’ai réalisé deux variantes en 2023 fonctionnent l’un à l’énergie solaire, l’autre avec une batterie de voiture de 12V (selon la surface à traiter). Il utilise de petits modules du monde Arduino que l’on peut se procurer facilement et à des prix très raisonnables : un Nano, une horloge « temps réel, un visuel 2 lignes 16 caractères, trois boitons de commande, une platine relais et deux détecteurs capacitifs d’humidité vendus par AZDelivery.

Voici tout d’abord le schéma de la version « énergie solaire », celui de la version batterie 12V n’en diffère que parce que le 5V est fourni par un circuit régulateur 7805 et le convertisseur 12V n’a pas lieu d’être, juste une diode pour éviter le risque d’une inversion de polarité.


La réalisation pratique tient dans un boîtier étanche pour installations électriques.
Les capteurs ont pour le moment une liaison filaire qui passe par un connecteur étanche.La partie électronique des capteurs est noyée dans la résine pour une protection contre la corrosion.

Pourquoi deux capteurs :

L’humidité du sol n’est pas homogène. Il convient donc de faire une moyenne de la mesure de l’humidité du sol à 10 cm de profondeur et à quelque 25 cm d’un goutteur à deux endroits différents. Pour le moment, tous les goutteurs d’un même ensemble fonctionnent sur la même pompe.

Mode de fonctionnement

Il est évident que l’eau apportée ne se répand que lentement et qu’il existe un délai important entre cet apport et la modification de la mesure. Pour cette raison, l’apport en eau se fait par intermittence : quelques minutes de goutte à goutte, un temps d’attente, (40 minutes par exemple), puis reprise du goutte à goutte.

Réglages du système

Tous les réglages s’opèrent au moyen de trois boutons poussoirs : « sélection », « + », « - ».

À l’appui sur « sélection », le visuel s’allume quelques instants et affiche notamment le taux d’humidité.

À l’appui suivant, on entre dans le mode réglage, à commencer par la mise à l’heure de l’horloge sauvegardée temps réel. Les appuis sur « + » ou « - » augmentent ou diminuent le chiffre en surbrillance.

Les appuis successifs permettent de régler l’heure du début et l’heure de la fin de l’apport en eau, la durée de cet apport et le temps d’attente entre deux apports, puis le seuil à partir duquel on trouve que l’arrosage est nécessaire. Enfin, on quitte le mode réglage et le système est prêt à fonctionner.

Il n’y a donc qu’un seuil de déclenchement, l’adaptation aux besoins plus ou moins exigeants des différents plants est possible grâce à l’emploi de goutteurs à débit réglable.

Voici enfin le code de cette réalisation :

[code]
/*Programme pour système d'arrosage avec économie d'eau
  fondée sur la mesure de l'humidité du sol par sonde capacitive
  Copyleft Jacques Cuvillier 2023
*/

#include <LiquidCrystal.h>
#include <uRTCLib.h>
#include <Wire.h>
#include <EEPROM.h>

uRTCLib rtc(0x68);
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
//initialisation capteur
const int dry = 680; // valeur pour niveau sec 595 en principe
const int wet = 270;      // valeur pour niveau humide 239 en principe
const int tmax = 1439;    // 23*60+59
const int seuilbat = 700; // seuil d'alerte tension batterie (3,42V)
int bat;                  // fraction de la tension de la batterie
bool clign = false;
bool clign2 = true;
bool attention = false;   // alerte alimentation
int ki = 0;               // comptage itération
int maxcount = 256;       // temporisation du clignottement
int affiche = 0;          // écrans successifs sur l'afficheur
bool change;              // demande de reéaffichage sur le visuel si une donnée a été modifiée
bool prog = true;         // true:les repères de temps doivent être calculés et enregistrés
int Fin = 0;              // heure de fin convertie en minutes
byte Darr;                // durée d'écoulement en minutes
byte seuil = 50;          // seuil de déclenchement en fonction du capteur
const byte hysteresis = 2; // pour maintien de condition d'allumage/arret de la pompe
byte Esp = 30;            // temps entre la fin d'un écoulement et le début du suivant
                          // 0 pour arrosage unique
int depart = 1200;        // depart initial d'écoulement
int depart1;              // departs successifs du processus en cours
uint8_t H, H1, M, M1;     // temps en heure et minute
int temps;                // equivalent en minutes de l'heure réelle
int tmpStop, tmpStop1;    // arrêts initial et successifs d'écoulement
byte Harr = 19;           // Heure du départ
byte Marr = 30;           // minute du départ
byte Hstop;               // Heure et de fin
byte Mstop;               // minute de fin
int adress = 0;           // pour enregistrement des réglages en EEPROM
bool ouvre = false;       // commande de pompe


void setup() {
  //  A0 : broche analogique           sonde humidite 1
  pinMode(A1, INPUT_PULLUP); // sélection affichage
  pinMode(A2, INPUT_PULLUP); // Bouton +
  pinMode(A3, INPUT_PULLUP); // bouton -
  //  A7 : broche analogique            sonde humidite 2

  pinMode(8, OUTPUT);       // Commande relais arrosage
  pinMode(13, OUTPUT);      // LED sur circuit nano et éclairage afficheur
  pinMode(11, OUTPUT);      // activation afficheur (E)
  pinMode(12, OUTPUT);      // sélection de la ligne
  pinMode(2, OUTPUT);       // 2,3,4,5,:caractères à transmettre à l'afficheur
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);       // Témoin arrosage (clignotte si arrosage suspendu)
  pinMode(7, OUTPUT);       // Témoin Marche (clignotte en cas de batterie faible)

  /*Serial.begin(9600);  // pour mise au point */

  //initialisation de l'horloge

#ifdef ARDUINO_ARCH_ESP8266
  URTCLIB_WIRE.begin(0, 2); // D3 and D4 on ESP8266
#else
  URTCLIB_WIRE.begin();
#endif

  //rtc.set(0, 30, 12, 6, 1, 1, 23); // pour mise au point
  //  RTCLib::set(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year)

  // récupération des données
  adress = 0;
  // Harr=12;
  // EEPROM.update(adress,Harr);
  EEPROM.get(adress, Harr);
  adress++;
  EEPROM.get(adress, Marr);
  adress++;
  EEPROM.get(adress, Hstop);
  adress++;
  EEPROM.get(adress, Mstop);
  adress++;
  EEPROM.get(adress, Darr);
  adress++;
  EEPROM.get(adress, Esp);
  adress++;
  EEPROM.get(adress, seuil);



  // initialisation de l'afficheur
  lcd.begin(16, 2);
  digitalWrite(13, LOW);  //allumage témoin marche
  // allumage de l'écran
  digitalWrite(13, HIGH); // allumage afficheur (HIGH ou LOW selon cablage Led)
  // Message sur l'écran LCD
  lcd.setCursor(0, 0);
  lcd.print("Ecodeau V 1.0");
  lcd.setCursor(0, 2);
  lcd.print("Bienvenue");
  digitalWrite(6, LOW); // allumage de la led si niveau bas

  delay(2000);
  affiche = 1;
}

void loop()
{
  int affiche1;
  unsigned int humid = gethumid();    // taux d'humidité

  rtc.refresh();

  // Surveillance de la tension de battereie
  // et alerte par clignottement du témoin de marche
  // et du témoin d'arrosage si arrosage activé

  bat = analogRead(6);
  //Serial.println(bat); //décommenter pour mise au point

  attention = (bat < seuilbat);


  ki++ ;
  if (ki == maxcount)
  {
    ki = 0;
  }
  clign = (ki < maxcount / 2);
  clign2 = (ki > maxcount / 8);

  if (attention) // clignottement témoins
  {
    digitalWrite(7, clign);
  }
  else
  {
    digitalWrite(7, LOW);
  }

  switch (affiche) 
  {
    case 1:          // écran1 : allumage temporaire du visuel
      marche(humid); // affiche aussi heure, humidité,  (et température selon le circuit horloge)
      break;
    case 2:          // on entre dans une séquence de réglage des paramètres écran2
      {
        lcd.clear();
        lcd.blink();
        lcd.setCursor(0, 0);
        lcd.print("ARRET PROGRAMME");
        digitalWrite(8, LOW);  // arrêt de la pompe
        digitalWrite(6, HIGH); // extinction du voyant d'arrosage
        delay(3000);
        regleHeure(); // réglage de l'heure
        // affiche = OK(affiche); // seulement pour mise au point
      }
      break;
    case 3:                      // réglage des heures d'arrosage
      heureArrosage();           // réglage de l'heure d'arrosage
      // affiche = OK(affiche);  // seulement pour mise au point
      break;
    case 4:                      // réglage de la durée et de l'espacement
      durees();   
      //  affiche = OK(affiche); // seulement pour mise au point
      break;
    case 5:                      // réglage du seuil de déclenchement  
      reglSeuil();
      //affiche = OK(affiche);   // seulement pour mise au point
      break;
    case 6:                       // fin de la séquence de réglage des paramètres
      {
        affiche = OK(affiche);    // seulement pour mise au point
        if (affiche == 7)
        {
          affiche = 1;
        }
      }
      break;
  }

  //calcul des temps d'arrosage
  // conversion en minutes

  depart = Harr * 60 + Marr;
  Fin =  Hstop * 60 + Mstop;
  tmpStop = depart + Darr;

  if (tmpStop > tmax)
  {
    tmpStop = tmpStop - tmax;
  }

  temps = rtc.hour() * 60 + rtc.minute();
  if (prog)
  {
    if (depart > temps)         // on est déjà dans la période prévue pour arrosage
    {
      depart1 = depart;         // point de démarrage
      tmpStop1 = depart + Darr; // point d'interruption
    }
    else
    {
      depart1 = temps;          // démarrage immédiat
      tmpStop1 = depart1 + Darr; // fixation du point d'arrêt
      if (tmpStop1 > tmax)       // arrêt le jour suivant : tmax est 0H:00
      {
        tmpStop1 = tmpStop1 - tmax;
      }
      //  Serial.println("prog");  // pour mise au point
    }
    prog = false;
    sauvegarde();    //Sauvegarde en EEPROM du repère temporel en cours
  }

  temps = rtc.hour() * 60 + rtc.minute();
  if (depart > temps)
  {
    depart1 = depart;
    tmpStop1 = depart + Darr;
  }
  /* Serial.print(temps);Serial.print("     ");  // décommenter pour mise au point
    Serial.print(depart1);Serial.print("     ");
    Serial.println(tmpStop1);
    delay(500);
  */
  if (depart1 == temps)
  {
    ouvre = true;
    if (Esp > 0)
    {
      depart1 = depart1 + Esp + Darr;
      if (depart1 > tmax)
      {
        depart1 = depart1 - tmax;
      }
    }
  }
  if (tmpStop1 == temps)
  {
    ouvre = false;
    tmpStop1 = depart1 + Darr;
    if (tmpStop1 > tmax)
    {
      tmpStop1 = tmpStop1 - tmax;
    }
  }

  if ( temps == Fin)
  {
    depart1 = depart;
    tmpStop1 = tmpStop;
    ouvre = false;
  }
/* pour éviter les basculements rapprochés des conditions d'arrosage, un hystérésis doit être introduit */
  if ((ouvre) && (affiche == 6))
  {
    if ((!attention) && (humid < seuil - hysteresis))
    {
      digitalWrite(8, HIGH);    // mise en route de la pompe
      digitalWrite(6, LOW);     // allumage du voyant
    }

    else if ((humid < seuil - hysteresis))
    {
      digitalWrite(8, LOW);     // la pompe ne démarre pas
      digitalWrite(6, clign2);  // le témoin d'arrosage clignotte pour indiquer l'erreur
    }
  }
  if (!ouvre )                  // arrosage sur Off
  {
    digitalWrite(8, LOW);       // la pompe ne démarre pas
    digitalWrite(6, HIGH);      // le témoin d'arrosage est éteint
  }
}


void marche(int humid)
{
  int i;
  //uint16_t temp = rtc.temp();
  lcd.clear();
  lcd.noBlink();
  digitalWrite(13, HIGH); //allumage écran
  lcd.setCursor(0, 0);
  lcd.print("MARCHE ");
  lcd.print(rtc.hour());
  lcd.print(":");
  lcd.print(rtc.minute());
  lcd.setCursor(0, 2);
  lcd.print("Hum:");
  lcd.print(humid);
  lcd.print("%  ");
  // lcd.setCursor(8, 2);  // option : affichage de la température possible
  //lcd.print("temp:");    // seulement avec une horloge DS3231
  //lcd.print(temp / 100);
  change = false;
  for (i = 1; i < 127; i++)// règle la durée d'allumage de l'afficheur
  {
    delay(100);
    // réacquisition de l'humidité
    humid = gethumid();
    lcd.setCursor(4, 2);
    lcd.print(humid);
    lcd.print("%");
    affiche = OK(affiche);
    if (affiche == 2)
    {
      i = 255;
    }
  }
  if (!change)
  {
    affiche = 6;
    digitalWrite(13, LOW); // extinction ecran
  }
}

void regleHeure()
{
  int col = 1;             // premier champ d'affichage

  M = rtc.minute();
  H = rtc.hour();
  lcd.blink();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Reglage Horloge");

  while (affiche == 2)
  {
    if (change)
    {
      lcd.setCursor(4, 2);
      lcd.print(H);
      lcd.print(" ");
      lcd.setCursor(7, 2);
      lcd.print(":");
      lcd.setCursor(9, 2);
      lcd.print(M);
      lcd.print(" ");
      lcd.setCursor(4, 2);
      change = false;
    }
    while (col == 1)
    {
      H = plus(H, 4, 4, 24);
      H = moins(H, 4, 4, 23);
      col = OK(col);
    }
    while (col == 2)
    {
      M = plus(M, 9, 9, 60);
      M = moins(M, 9, 9, 59);
      col = OK(col);
    }
    if (col == 3)affiche = 3;
  }
  rtc.set(0, M, H, 6, 1, 1, 23);
}


void heureArrosage()
{
  int col = 1;
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Heures Debut/fin");
  while (affiche == 3)
  {
    if (change)
    {
      lcd.setCursor(1, 2);
      if (Harr <= 9) lcd.print("0");

      lcd.print(Harr);
      lcd.setCursor(3, 2);
      lcd.print(":");
      lcd.setCursor(4, 2);
      if (Marr <= 9) lcd.print("0");
      lcd.print(Marr);
      lcd.setCursor(7, 2);
      lcd.print("-");
      lcd.setCursor(9, 2);
      if (Hstop <= 9) lcd.print("0");
      lcd.print(Hstop);
      lcd.setCursor(12, 2);
      lcd.print(":");
      lcd.setCursor(13, 2);
      lcd.print(Mstop);
      lcd.print("");
      lcd.setCursor(1, 2);
      change = false;
    }
    while (col == 1)
    {
      Harr = plus(Harr, 1, 4, 24);
      Harr = moins(Harr, 1, 4, 23);
      col = OK(col);
    }
    while (col == 2)
    {
      Marr = plus(Marr, 4, 4, 60);
      Marr = moins(Marr, 4, 4, 59);
      col = OK(col);
    }
    while (col == 3)
    {
      Hstop = plus(Hstop, 9, 9, 24);
      Hstop = moins(Hstop, 9, 9, 23);
      col = OK(col);
    }
    while (col == 4)
    {
      Mstop = plus(Mstop, 13, 13, 60);
      Mstop = moins(Mstop, 13, 13, 59);
      col = OK(col);
    }
    if (col == 5)affiche = 4;
  }
}

void durees()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Duree,espacement");
  lcd.setCursor(4, 2);
  int col = 1;
  int col1;
  while (affiche == 4)
  {
    if (change)
    {
      lcd.setCursor(4, 2);
      lcd.print(Darr);
      lcd.setCursor(7, 2);
      lcd.print("-");
      lcd.setCursor(9, 2);
      lcd.print(Esp);
      lcd.print(" ");

      lcd.setCursor(4, 2);
      change = false;
    }
    while (col == 1)
    {
      Darr = plus(Darr, 4, 4, 255);
      Darr = moins(Darr, 4, 4, 0);
      col = OK(col);
    }
    while (col == 2)
    {
      Esp = plus(Esp, 9, 9, 255);
      Esp = moins(Esp, 9, 9, 255);
      col = OK(col);
    }
    if (col == 3)affiche = 5;
  }
}

void reglSeuil()
{
  if (change)
  {
    int percentageHumididy = gethumid();
    int seuil1, affiche1;
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Humidite:  ");
    lcd.print(percentageHumididy);
    lcd.print("%");
    lcd.setCursor(0, 2);
    lcd.print("Stop a :");
    lcd.print(seuil);
    lcd.print("% ");
    lcd.setCursor(11, 2);
    change = false;
  }
  while (affiche == 5)
  {
    seuil = plus(seuil, 11, 11, 255);
    seuil = moins(seuil, 11, 11, 0);
    affiche = OK(affiche);    // passage à l'affichage suivant
  }
  if (affiche == 6)
  {
    prog = true;
    affiche = 1;
    lcd.noBlink();
  }
}


int OK(int chiffre) // Détection d'appui sur la touche de sélection avec anti-rebonds
// et incrémentation de la valeur passée en paramètre
{
  byte appui = digitalRead(A1);
  delay(5);
  if ((appui == LOW) && (digitalRead(A1) == LOW))
  {

    chiffre++;
    while (digitalRead(A1) == LOW)
    {
      delay(15);
    }
    change = true;    //action sur la touche de sélection détectée
  }
  return (chiffre);   // renvoi du paramètre incrémenté par l'appui
}

int gethumid()
{
  // acquisition de l'humidité
  // faisant la moyenne entre les mesures de deux sondes
  int sensorVal = analogRead(A0);
  int humid1 = map(sensorVal, wet, dry, 100, 0);
  sensorVal = analogRead(A7);
  int humid2 = map(sensorVal, wet, dry, 100, 0);
  int humid = (humid1 + humid2) / 2;
  return (humid);
}


//Incrémentation
byte plus(byte valeur, byte posit, byte curseur, byte limite)
{
  lcd.setCursor(posit, 2);
  if (digitalRead(A2) == LOW)
  {
    valeur++;
    if (valeur == limite)
    {
      valeur = 0;
    }
    if (valeur <= 9)
    {
      lcd.print("0");
    }
    lcd.print(valeur);
    lcd.print(" ");
    lcd.setCursor(posit, 2);
    delay(800);                       // temps entre incrémentations automatiques
    while (digitalRead(A2) == LOW)    // tant que l'appui sur le bouton est maintenu
    {
      valeur++;
      if (valeur == limite)
      {
        valeur = 0;
      }
      if (valeur <= 9)
      {
        lcd.print("0");
      }
      lcd.print(valeur);
      lcd.setCursor(posit, 2);
      delay(400);
    }
    change = true;
  }
  return (valeur);
}

//Décrémentation
byte moins(byte valeur, byte posit, byte curseur, byte reinit)
{
  lcd.setCursor(posit, 2);
  if (digitalRead(A3) == LOW)   // détection d'appui sur le bouton moins
  {
    valeur--;
    if (valeur == 255)
    {
      valeur = reinit;
    }
    if (valeur <= 9)
    {
      lcd.print("0");
    }
    lcd.print(valeur);
    lcd.print(" ");
    lcd.setCursor(posit, 2);
    delay(800);             // temps entre décrémentations automatiques
    while (digitalRead(A3) == LOW)  // tant que l'appui sur le bouton est maintenu
    {
      valeur--;
      if (valeur == 255)
      {
        valeur = reinit;
      }
      if (valeur <= 9)
      {
        lcd.print("0");
      }
      lcd.print(valeur);
      lcd.print(" ");
      lcd.setCursor(posit, 2);
      delay(400);
    }
    change = true;
  }
  return (valeur);
}

void sauvegarde()
{
  int adress = 0;
  EEPROM.update(adress, Harr);
  adress++;
  EEPROM.update(adress, Marr );
  adress++;
  EEPROM.update(adress, Hstop);
  adress++;
  EEPROM.update(adress, Mstop);
  adress++;
  EEPROM.update(adress, Darr );
  adress++;
  EEPROM.update(adress, Esp );
  adress++;
  EEPROM.update(adress, seuil );

}
[/code]

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.