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]