ESP32 : Micro-irrigation Connectée (3ème partie)
Cet article fait suite à ceux-ci :
ESP32 : Micro-irrigation Connectée (1ère partie)
ESP32 : Micro-irrigation Connectée (2ème partie)
Pour rappel, le projet se trouve ici :
https://bitbucket.org/henri_bachetti/esp32-sprinkle-timer
1. Le hardware
Du côté matériel, le capteur d'humidité et le capteur de débit sont présents :
- capteur d'humidité : GPIO36 (broche SVP)
- capteur de débit : GPIO39 (broche SVN)
Contrairement à ce que je pensais, l'ESP32 est tout à fait capable de mesurer une tension analogique de 3.3V, ou plutôt la librairie ARDUINO fait ce qu'il faut pour que cela soit possible. Le pont diviseur R3 R5 disparaît du schéma (paragraphe 5).
En ce qui concerne le capteur de débit, la mesure renvoie des valeurs non nulles pour un débit de zéro. Il faut que je fasse des essais afin de voir s'il ne s'agirait pas d'un problème d'alimentation. Il est alimenté par la broche 5V de l'ESP32 et celui-ci est alimenté par l'USB. Comme il ne reste que 4.7V, ce n'est peut-être pas suffisant.
L'écran OLED est connecté, ainsi que le bouton fonction et arrosage manuel:
- SDA : GPIO21
- SCL : GPIO22
- bouton fonction : GPIO35
- bouton arrosage manuel : GPIO34
Comme il n'y a pas de résistance de PULLUP interne sur les GPIOS 35 et 34, il faut en ajouter une sur le schéma entre GPIO35, GPIO34 et 3.3V : 10KΩ.
2. La configuration
2.1. config.ini
Le paramétrage des capteurs a été ajouté :
[moisture]
sensor=36
max=50
[flow]
sensor=39
max=10
2.2 Schedule.ini
Ce fichier reste inchangé, mis à part que deux périodes d'arrosage ont été ajoutées :
[courges.voie1]
schedule1=08:00,15
[massif.voie1]
schedule1=08:30,15
3. L'interface HTML
La page d'accueil comporte maintenant des boutons permettant de modifier les périodes d'arrosage et même d'en ajouter :
Cliquer sur l'un des boutons CONFIGURE permet d'accéder à un formulaire :
Il suffit de modifier les valeurs et de cliquer sur OK pour enregistrer les changements. Les changements sont stockés dans le fichier schedule.ini.
En cliquant sur le bouton AJOUTER on accède à un formulaire à peine différent :
Le formulaire propose une liste des zones existantes.
Attention il n'y a pas pour l'instant de contrôle de validité sur ces deux formulaires. Il faut bien respecter le format de saisie HH:MM.
Les durées sont exprimées en minutes.
Le bouton maintenance permet d'accéder au menu suivant :
Les actions suivantes sont possibles :
- tester les relais
- afficher le fichier config.ini
- afficher le fichier schedule.ini
Il est donc possible de sauvegarder ces deux fichiers avant une mise à jour des fichiers HTML. Les fichiers HTML et les fichiers de configuration (.ini) sont situés dans le même répertoire data du projet. Les fichiers de configuration seraient donc écrasés à la prochaine mise à jour si l'on ne prend pas de précautions.
4. L'écran OLED
L'afficheur montre la date et l'heure, ainsi que l'humidité et le débit :
Un appui sur le bouton fonction permet l'affichage de l'adresse IP de l'ESP32 pendant 4 secondes, puis de l'heure du prochain arrosage pendant 4 secondes, puis l'affichage revient à la normale.
Un appui sur le bouton d'arrosage manuel provoque l'affichage du premier arrosage manuel possible et de sa durée :
En appuyant plusieurs fois sur ce bouton on peut faire défiler les noms des voies. Un appui sur le bouton fonction déclenche l'arrosage manuel sur la voie choisie.
5. Rappels
Ce logiciel a besoin d'être compilé en utilisant les librairies suivantes :
ESPAsyncWebServer : https://github.com/me-no-dev/ESPAsyncWebServer
SPIFFSIniFile : https://github.com/yurilopes/SPIFFSIniFile
SSD1306 : https://github.com/adafruit/Adafruit_SSD1306
GFX : https://github.com/adafruit/Adafruit-GFX-Library
ezButton : https://github.com/ArduinoGetStarted/button
Quelques constantes peuvent être modifiées dans le code :
On voit par exemple que le nombre d'arrosages par voie est limité à 4 par jour, ce qui me semble amplement suffisant. On peut bien entendu ajuster certaines valeurs, mais attention, celles-ci ne sont pas modifiables ultérieurement par l'interface HTML.
Pour le reste, relire l'article précédent : 5. Essayer
6. Le code
Le code, comme on peut s'en douter, n'est pas constitué d'un seul sketch, il serait énorme et illisible.
Voici une liste des fichiers :
Je veux bien admettre que ce code commence à devenir très conséquent, mais la complexité ne se gère pas avec simplicité. En opérant un découpage en modules simples, on peut toutefois réduire cette complexité et améliorer la lisibilité.
7. Conclusion
Les difficultés sont immenses car ce logiciel est hyper-paramétrable, et le temps manque. En effet, on peut difficilement s'occuper d'un jardin de 45m², récolter, cuisiner, faire des conserves, gérer un poulailler, aménager une nouvelle habitation, modifier son circuit électrique, et faire du développement logiciel en parallèle.
La période hivernale sera certainement plus productive en lignes de code. Patience ...
Cordialement
Henri
Oui, le pont diviseur peut être modifié comme suit :
RépondreSupprimerR3 à remplacer par 0Ω, un bout de fil.
R5 à laisser vide
C'est vrai qu'un SSD1327 offre plus de surface d'affichage qu'un SSD1306.
Merci pour le retour.
RépondreSupprimerJ'ai testé une autre solution qui fonctionne bien même si effectivement elle n'a pas réellement d'intérêt...
Les valeurs du pont diviseur mettait l'entrée GPIO toujours à la masse car un courant trop fort circulait et par conséquent aucune lecture sur la broche... En employant des valeurs de 39K et 100K ça fonctionne parfaitement. J'ai en outre modifié l'atténuation de l'ADC sur 0db comme cela on une mesure plus précise (4096 points sur 1,1V env).
Avez-vous pu résoudre/comprendre le problème sur l'entrée du capteur de débit qui n'affichait pas 0 ? Peut -être également des valeurs du pont diviseur trop faibles ?
Bonne journée
Tout dépend certainement du modèle de capteur.
RépondreSupprimerJe n'ai pas encore eu le temps de voir côté capteur de débit.
Bonjour Henri, je souhaiterai tout comme vous avez prévu d'ajouter une irrigation à une voie, pouvoir également en "supprimer" une par un bouton.
RépondreSupprimerMa question est donc comment supprimer une des 4 irrigations d'une voie. Faut-il tout simplement mettre la valeur m_duration à 0 ou y a t il d'autres variables à réinitialiser ?
Autre question :
J'ai par exemple 3 irrigations pour la voie Jardin.Potager :
Irrigation N°1 - Index[0] Jardin.Potager 06:00 15min
Irrigation N°2 - Index[1] Jardin.Potager 20:00 15min
Irrigation N°3 - Index[2] Jardin.Potager 22:00 15min
Je supprime l'irrigation N°2. Il me reste donc :
Irrigation N°1 - Index[0] Jardin.Potager 06:00 15min
Irrigation N°3 - Index[2] Jardin.Potager 22:00 15min
Comment faire pour "copier" ou "déplacer" l'irrigation N°3 avec l'index[2] en Irrigation N°2 - Index[1] Jardin.potager 22:00 15min.
Je pose cette question car contrairement à vous mon interface WEB ouvre et affiche toutes les irrigations d'une voie lorsque je "configure" une voie. Et surtout, mon code lit les irrigations dans l'ordre. D'où mon souhait de ne pas laisser de trous entre les index des irrigations.
Je ne sais pas si je suis bien clair...
En fait j'ai reproduit l'application Rain Bird : https://play.google.com/store/apps/details?id=com.rainbird&hl=en_US
Merci beaucoup.
Classiquement, dans un tableau, si l'on veut déplacer un élément on utilise memcpy(destination, source, taille), ensuite memset(source, 0, taille) pour effacer l'élément source.
SupprimerMerci je vais essayer.
SupprimerBon je pense comprendre le principe, mais je n'arrive pas à mettre en oeuvre concrètement vos explications... Je bute avec des erreurs sur les fonctions memcpy et memset...
SupprimerSi je reprends mon exemple avec 3 irrigations pour la voie Jardin.Potager :
Irrigation N°1 - Index[0] Jardin.Potager 06:00 15min
Irrigation N°2 - Index[1] Jardin.Potager 20:00 15min
Irrigation N°3 - Index[2] Jardin.Potager 22:00 15min
Irrigation N°4 - Index[3] Jardin.Potager 00:00 0min
Si je veux supprimer l'irrigation N°2, comment est ce que je fais :
1- pour supprimer l'irrigation N°2 Index[1]
2- pour décaler les irrigations N°3 Index[2] et N°4 Index[3] vers leur nouvelle position Index[1] et Index[2]
3- "recréer" une irrigation libre pour l'index[3]
Auriez-vous un peut bout de code pour me mettre sur la piste (au moins comment supprimer et copier une irrigation d'une voie).
Même si j'avais un peu programmé avant, je n'avais jamais fait de C auparavant et du coup c'est compliqué pour moi de tout comprendre... J'arrive à adapter votre code, mais pas toujours à créer des nouvelles fonctions...
Avec mes remerciements et toutes mes excuses.
Il faudrait avoir au moins la structure d'une irrigation. Est-ce une structure, une classe ?
SupprimerBonjour, je n'ai pas modifié votre code de ce côté là. J'ai juste adapté quelques fonctions pour mon affichage et mes gestions de dates.
SupprimerDonc si j'ai bien compris "mes" irrigations sont "vos" waterings, avec pour chaque way -> 4 irrigations (MAX_SCHEDULE). Donc à priori ce sont des classes avec un tableau[4 ou MAX_SCHEDULE] pour chaque way ?
Il vaudrait mieux donc implémenter un opérateur de recopie.
SupprimerWatering::Watering(const Watering& w) :
m_way(w.m_way),
m_hour(w.m_hour), m_minute(w.m_minute),
m_duration(w.m_duration),
m_always(w.m_always)
{
}
Mais c'est un peu compliquer les choses. Dans mon cas, à partir du moment où m_duration vaut zéro, je n'affiche pas le watering en question, donc je considère qu'il est libre.
Bonjour Henri, merci pour votre aide. Grâce à vous j’ai réussi à faire ce que je souhaiterais... Je continue à suivre votre projet avec grand intérêt...
SupprimerBonjour Henri, pour info mes boutons ne marchaient pas sur la carte que j'ai conçue (d'après votre plan). Il faut faire une petite correction. En effet après avoir consulté la datasheet les entrées GPIO 34 et 35 ne possèdent pas de résistances PULLUP intégrées. Il faut donc les prévoir en externe. J'ai mis des 10K et ça fonctionne maintenant parfaitement.
RépondreSupprimerOui, c'est bien ce que j'ai précisé en fin de paragraphe 1.
SupprimerDésolé cela m'a échappé...
SupprimerBonjour Henri, j’ai repris la suite de mon système d’arrosage après avoir installé les tuyaux et autres arroseurs.
RépondreSupprimerJ’ai repris votre programme que j’ai essayé d’adapter avec des jours pairs impairs et personnalisés...
J’ai profité de cet hiver pour m’améliorer en C (car j’étais complètement débutant) et je commence à avoir un résultat sympa. Je vais encore essayer d’implémenter une fonction pour rendre une voie inactive temporairement (pendant un barbecue par exemple) et une autre pour ajuster la durée d’arrosage en fonction de la saison (25% à 200%). Si cela vous intéresse je pourrai vous transmettre mon code (que j’ai pu compléter grâce à votre travail initial dont je vous remercie au passage). J’ai juste utilisé un écran SSD1327 au lieu du SSD1306...
Par contre je rencontre le même problème que vous pour le capteur de débit... À l’arrêt il n’indique pas forcément 0. Avez-vous pu trouver la cause ?
Enfin dans votre code sauf erreur de ma part, je pense que la fonction watering:run à un plusieurs bugs. Elle ne démarre pas quant il faut et lorsqu’elle le fait, toute les minutes, le(s) relais concernés s’éteignent et se rallument. En tout cas sur ma carte électronique c’est ce qu’il se passe... J’ai modifié la fonction et maintenant ça fonctionne, mais je pense que vous devriez y jeter un coup d’œil pour confirmer ou infirmer mes dires.
Avez-vous pu avancer de votre côté cet hiver ?
Encore merci et au plaisir de vous lire.
Bonjour.
SupprimerOui j'ai pu avancer un peu. J'ai aussi ajouté une possibilité d'arroser tous les X jours.
Bonjour Rodolphe,
SupprimerJ'ai le même problème pour l'arrosage auto. Vous pouvez me donner vos modifications svp ?
Cdlt
Bonjour, mon code est très largement modifié car j'ai intégré de nombreuses fonctions supplémentaires ainsi qu'une interface WEB entièrement nouvelle. Du coup je veux bien vous le transmettre mais il vous faudra tout reprendre à zéro. D'après mon analyse (de petit programmeur) le problème vient de startTime. J'ai utilisé une autre approche pour le déterminer à savoir endTime - wateringTime ce qui évite au code d'Henry de passer à J+1 dès que l'heure de démarrage est atteinte.
SupprimerJe veux bien regarder votre solution. (messenger : m.me/bertrand.muller.96).
Supprimerhttps://github.com/rgodin974/ESP32_sprinkler_timer
Supprimerok merci
SupprimerJe ne trouve pas le programme principal "".ino
SupprimerC'est normal c'est codé avec VS Code en C/C++ classique... Donc c'est dans le fichier main.cpp
SupprimerJ'ai trouvé la solution au pb d'arrosage auto... voir plus loin.
SupprimerY a-t-il un moyen de vous envoyer des photos de mon interface WEB que je me suis amusé à bien finaliser ?
RépondreSupprimerIl y a différents moyens :
Supprimer- google photos, puis partager avec moi
- facebook (chercher Henri Bachetti) mais il faut avoir un compte.
- etc.
Bonjour je vous ai envoyé les photos par Messenger...
SupprimerBonjour,
RépondreSupprimerJ'utilise votre projet que je trouve super ! J'ai rajouté la gestion de remplissage d'une cuve via une pompe déclenchée en 433 MHZ et le déclenchement de l'arrosage via une deuxième pompe.
J'ai créé un point d'accès pour l'ESP32 car j'ai pas internet au jardin, j'aimerai utiliser un module RTC DS1302 en cas de coupure de courant. Comment faire en sorte que votre projet fonctionne sans internet et donc sans serveur sntp, et comme vous avez tout codé avec time()... Avez-vous possibilité de m'aider svp.
Merci d'avance.
Tout d'abord j'utiliserais plutôt un DS3231, plus stable et compensé en température.
SupprimerPour la mise à l'heure du RTC, quelques lignes suffisent, en utilisant RTClib :
#include "RTClib.h"
RTC_DS3231 rtc;
void setup()
{
if (!m_rtc.begin()) {
log_println(F("Couldn't find RTC"));
}
m_rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
void loop() {}
Ensuite dans mon code il suffit de remplacer time() par rtc.now().unixtime().
ok merci !
SupprimerPar ailleurs l'arrosage automatique ne fonctionne pas, à l'heure programmée d'arrosage auto, on passe à J+1, et la vanne ne s'ouvre pas.
Des pistes ? Merci.
Pas vraiment. Je vais essayer de voir pour intégrer la possibilité de se passer de NTP, à l'aide d'une option.
Supprimerj'ai le même problème avec ton programme non modifié.
Supprimer12:34:31.583 -> aromatique.aromatique: next start 12/07/2024 12:33:00
Supprimer12:34:31.583 -> aromatique.aromatique next stop 11/07/2024 12:35:00
12:34:31.583 -> t watering = 1720701268
12:34:31.583 -> startTime = 1720787580
12:34:31.583 -> stopTime = 1720701300
12:34:31.583 -> aromatique.aromatique: before time, schedule in 23:58:32
Y aurait-il un pb d'arrosage auto dans ton programme original ?
Supprimerj'ai fait quelques modif dans watering.cpp et l'arrosage auto se met bien en route puis s'arrête à l'heure de fin, et on peut mettre plusieurs plages par voie "way" :
Supprimer// run the watering
bool Watering::run(time_t t)
{
struct tm *pTime;
Serial.println();
pTime = localtime(&t);
int toDay = pTime->tm_mday;
for (int i = 0 ; i < MAX_WATERING ; i++) {
Watering *w = &m_watering[i];
if (w->getDuration() != 0) {
char buffer[MAX_BUF];
time_t startTime = w->getStartTime(t);
pTime = localtime(&startTime);
strftime(buffer, MAX_BUF, "%d/%m/%Y %H:%M:%S", pTime);
Serial.printf("%s: next start %s\n", w->getWayName(), buffer);
time_t stopTime = w->getStopTime(t);
pTime = localtime(&stopTime);
strftime(buffer, MAX_BUF, "%d/%m/%Y %H:%M:%S", pTime);
Serial.printf("%s next stop %s\n", w->getWayName(), buffer);
if (t < startTime) {
time_t d = startTime - t;
int h = d / 3600;
d %= 3600;
int m = d / 60;
d %= 60;
int s = d;
if (toDay != pTime->tm_mday) {
Serial.printf("%s: after time, schedule in %02d:%02d:%02d\n", w->getWayName(), h, m, s);
// w->autoStop();
}
else {
Serial.printf("%s: before time, schedule in %02d:%02d:%02d\n", w->getWayName(), h, m, s);
}
}
if (toDay == pTime->tm_mday && t >= startTime && t <= stopTime)
{
Serial.printf("In time, open relay %s\n", w->m_way->getRelay()->getName());
w->autoStart();
}
if (toDay == pTime->tm_mday && t > stopTime)
{ w->autoStop();
}
}
}
if (isAnyWateringRunning() == false)
{
Valve::getMainValve()->close();
}
else
{
Valve::getMainValve()->open();
}
return true;
}
et ceci :
// Exemple de fonction autoStart avec des journaux de débogage
void Watering::autoStart() {
int moisture;
Serial.printf("Watering::autoStart %s: %ld\n\n", getWayName(), m_duration);
m_autoStarted = now();
if (m_moisture == 0) {
m_moisture = getSoilMoisture(&moisture);
if (m_moisture == HUMIDITY_DRY) {
Serial.printf("Watering::autoStart: moisture %x (DRY)\n", moisture);
m_way->open();
} else {
Serial.printf("Watering::autoStart: moisture %x (WET)\n", moisture);
}
}
}
void Watering::autoStop() {
Serial.printf("Watering::autoStop %s\n\n", getWayName());
m_autoStarted = 0;
m_moisture = 0;
if (!m_way->manualStarted(NULL)) {
Serial.printf("close relay %s\n", m_way->getRelay()->getName());
m_way->close();
} else {
Serial.printf("Relay %s remains open (manual start detected)\n", m_way->getRelay()->getName());
}
}
// return the watering's start time
Supprimertime_t Watering::getStartTime(time_t now) {
struct tm *pTime;
pTime = localtime(&now);
// Serial.printf("Current hour: %d, Current minute: %d\n", pTime->tm_hour, pTime->tm_min);
pTime->tm_hour = m_hour;
pTime->tm_min = m_minute;
pTime->tm_sec = 0;
time_t at = mktime(pTime);
if (at+ (m_duration*60) < now) {
at += DAY_DURATION;
}
// Serial.printf("Start time: %ld\n", at);
return at;
}
// return the watering's stop time
time_t Watering::getStopTime(time_t now) {
struct tm *pTime;
pTime = localtime(&now);
pTime->tm_hour = m_hour + (m_duration / 60);
pTime->tm_min = m_minute + (m_duration % 60);
pTime->tm_sec = 0;
time_t at = mktime(pTime);
if (at+10 < now) {
at += DAY_DURATION;
}
// Serial.printf("Stop time: %ld\n", at);
return at;
}
Salut.
SupprimerIl m'est assez difficile de comparer mon code avec le tien, étant donné que le mien a également évolué de son côté.
J'ai en effet ajouté une notion d'intervalle d'arrosage en jours, ce qui permet des arrosages tous les 2 jours par exemple.
Salut,
SupprimerJ'ai uniquement changé 3, 4 lignes de ton code.
Peux tu actualiser ton code sur le site stp.
Merci et a+