home castoo
chapitre electronique
Electronique reserve eau

Suivi du niveau
de ma reserve d'eau de pluie

avril 2025

Suivre le niveau de ma réserve d'eau de pluie dans mes cuves

Connaitre le niveau de son réservoir d'eau de pluie n'est certes pas indispensable, sachant que l'on ne peut pas agir sur son remplissage ! Par contre on peut réguler la vitesse à laquelle on va le vider. On peut également envisager de prévoir une augmentation de la quantité d'eau stockée en fonction de la vitesse de remplissage (Achat de nouvelles citernes.) de la réserve actuelle. Ces quelques remarques et le fait que j'aime bien concevoir des petits projets m'ont conduit à me lancer dans ce développement. Je vous partage ici tous les éléments de ce petit projet.

Vous pouvez également trouver dans d'autres page du site "Bricolages" comment réparer une cuve en PE qui est percée mais aussi "l'impression en 3D" d'un système de capteur d'eau à installer sur une gouttière avec un gros débit.

Reserve eau de pluie.

Principes de base du dev :

Mesure de la distance entre un capteur infrarouge et la surface de l'eau en fond de cuve. Le système sera autonome, alimenté par une batterie de 3v7 rechargée par un capteur solaire. Afin de limiter au maximum la consommation, le processeur sera passé en mode Deep Sleep et ne se réveillera qu'une fois toutes les heures. Le capteur est alimenté par une broche du processeur de façon à ce qu'il ne consomme que quand le processeur est actif. Le montage ne sera disponible par wifi que toutes les heures pendant cinq minutes toujours dans l'idée d'économiser la batterie.

Toutes les données des mesures effectuées toutes les heures seront sauvegardées dans des tableaux qui seront stockés dans la mémoire EEPROM du processeur de façon à assurer leur sauvegarde pendant les périodes de deep sleep.

L'horloge est mise à jour par un serveur internet régulièrement.

Le logiciel peut s'adapter à plusieurs type de configuration de cuve pourvu que le diamètre sur la hauteur soit stable le système calcul un nombre de litre au cm de hauteur donc si le diamètre n'est pas égal en haut qu'en bas le calcul sera faussé !
Il faut aussi prendre en compte que toutes les valeurs de remplissage sont des taux donc exprimés en pourcentage.

L'adresse pour se connecter au système est fixe : 192.168.1.87 (vous pouvez la modifier dans le code) par contre il ne faut pas oublier que la connexion n'est possible qu'a heure pleine et fixe pendant seulement 5 minutes (ou il faut aussi modifier le code).

Plusieurs solutions matériels ont été testées au fil des années. Plusieurs type de capteur infrarouge et même lidar. Le problème principal étant l'angle de mesure du capteur qui a tendance à buter sur les parois de la cuve plutôt que de mesurer la profondeur de l'eau. Avec le lidar il y avait des problèmes de réflechissement de la lumière au travers de la cuve. Il est certain qu'avec une cuve en forme de cube comme celle de 1000 litres couramment utilisée l'éloignement des parois facilite le travail du capteur (l'angle de mesure sans obstacle est plus important).


Reserve eau de pluie angle de mesure. Il est très important de vérifier l'angle de mesure du capteur ultrasonique. Il faut le faire, cuve vide ou avec une fine pellicule d'eau au fond. Si le capteur bute sur une cloison de la cuve la mesure sera faussée et ne donnera rien, la valeur n'évoluera plus jusqu'à la fin de la cuve. Plus la cuve est large plus ce réglage est facile. L'angle de mesure du capteur est donc important, pour le S100 Y401 il est de 15°.

Le schéma du montage est celui-ci, deux particularités :


- le petit interrupteur entre D0 et Rst qui doit être fermé pour le deep Sleep et ouvert pour la programmation de l'ESP.
- Le capteur ultrasonique a son alimentation sur la broche D1 de l'ESP (la faible consommation rend cela possible.), cela permet au montage de ne rien consommer pendant la période de deep sleep.

(il faut également modifier l'orientation du strap CMS pour utiliser l'antenne exterieur de l'ESP Wemos PRO (voir en bas de page).)

Reserve eau de pluie chema electronique

Le matériel nécessaire :

Un module ESP8266 Wemos D1 mini PRO.
Un module de charge TP4056.
Une batterie type 18650 de 3v7 / 4200
Un capteur solaire 5v 3W de 17 x 13 cm
Un capteur ultrasonique US100 Y401

Un petit jeu de LEGO en modules electronique



L'interface accessible en html est simple (IP fixe).
Adresse : http://192.168.1.87


Reserve eau de pluie Menu HTML

Après avoir saisie l'adresse dans un navigateur le menu principal suivant apparait. Il est possible ici de configurer le type de cuve et un aperçu des dernières mesures est visible (seul graphique en litre).



Reserve eau de pluie suivi 72 heures.

Graphique de suivi des 72 dernieres heures en %.



Reserve eau de pluie suivi du mois en cours.

Graphique de suivi des 31 derniers jours en %.



Reserve eau de pluie suivi des 12 derniers mois.

Graphique des 12 derniers mois en %.



Reserve eau de pluie detail memoire.

Affichage des mesures sauvegardées dans L'EEPROM du processeur.
d'abord les valeurs des 72 dernières heures puis celles des 31 derniers jour puis celles des 12 derniers mois.



Reserve eau de pluie detail mémoire.

Fin de l'affichage des mesures (ici on voit la fin des données de l'année).
En toute fin de la liste on retrouve les dix dernières mesures stockées, celle-ci non exprimées en pourcentage.
Ce sont ces 10 dernières mesures qui apparaissent sur la page principale du site.



Impression 3D

Vue du boitier à imprimer.



Vue du couvercle à imprimer.


Les fichiers STL pour l'impression sont disponibles dans la partie "Impression3D" du site.


Modif sur le module ESP32 Wemos PRO

1) Prise pour antenne externe.
2) Strap à modifier pour choisir le type d'antenne horizontale (defaut) interne vertical (notre cas) externe.
3) Prévoir un petit strap ou inter entre Rst et D0.

Reserve eau de pluie modif. esp wemos.


Le code de base qui fonctionne bien pour moi mais que je vous laisse adapter pour votre besoin...
Attention le SSID "ZZZZZZZ"et le passe "XXXXXX" doivent être adaptés à votre configuration internet.


    #include <Arduino.h>
    #include <ESP8266WiFi.h>
    #include <ESP8266WebServer.h>
    #include <EEPROM.h>
    
    //#define debug_sur_usb
    #define debug_temp
    
    int trigPin = 14; // D5 sur esp8266 wemos 
    int echoPin = 12; // D6 sur esp8266 wemos
    int le_plus = D1; // D1 est uitilisé pour alimenter le Y401 lors du deepSleep 
    #define ssid "ZZZZZZZZZZZZZZZZZZZZZZZZZ" // SSID de la box
    #define password "XXXXXXXXXXXXXXXXXXXXX" // Pass de la box
    ESP8266WebServer server(80); // Port IP par defaut du serveur HTML
    const uint64_t duree_sommeil = (60e6)*54; // 54 minutes (duree du sommeil entre deux mesures, ensuite le systeme reste 5 minutes acif).
    const unsigned long memo_temps = (1000*60)*5; // 5 minutes d'activité (mesure + acces wifi)
    unsigned long temps_qui_passe; // Mesure le delai de fonctionnement pendant lequel une cnx est possible.
    unsigned long temps_inter_h; // Mesure du temps entre deux mesure du serveur heure
    boolean top_mesure; // Si true on lance la mesure
    struct tm * timeinfo;
    // declaration structure memo memoire RTC (l'ensemble fait 4 octets) pour ne pas les perdre avec le mode deepsleep
    #define debut_RTC 65 // Début de la zone de mémoire RTC (paquet de 4 octets)
    #define fin_RTC 193 // debut_RTC + 512/4 Fin de la zone de mémoire RTC (paquet de 4 octets)
    struct { // Mémorisation d'une mesure (enregistrement de 65 à 192)
      uint8_t v_m; // mois (1 - 12)
      uint8_t v_j; // jour (1 - 31)
      uint8_t v_h; // heure (0 - 23)
      uint8_t v_pc; // % de remplissage de la reserve (0 - 100)
    } memoval;
    #define debut_TJ 65 // Début tableau Jour
    #define fin_TJ 137 // Début tableau Jour
    struct gr_jour { // Info du graph des 72 dernieres heures (enregistrement de 65 à 137)
      uint8_t v_m; // mois (1 - 12)
      uint8_t v_j; // jour (1 - 31)
      uint8_t v_h; // heure (0 - 23)
      uint8_t v_pc; // % de remplissage de la reserve (0 - 100)
    };
    gr_jour tgr_jour[74];
    #define debut_TM 138 // Début tableau Mois
    #define fin_TM 169 // Début tableau Mois
    struct gr_mois { // Info du graph des 31 derniers jours (enregistrement de 138 à 169)
      uint8_t v_a; // an (20 - 99)
      uint8_t v_m; // mois (1 - 12)
      uint8_t v_j; // jour (1 - 31)
      uint8_t v_pc; // % de remplissage de la reserve (0 - 100) (moyenne)
    };
    gr_mois tgr_mois[32];
    struct String nom_mois[13] = {"nul", "Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Aout", "Septembre", "Octobre", "Novembre", "Decembre"};
    #define debut_TA 170 // Début tableau An
    #define fin_TA 182 // Fin tableau An
    struct gr_an { // Info du graph des 12 derniers mois (enregistrement de 170 à 182)
      uint8_t v_a; // an (20 - 99)
      uint8_t v_m; // mois (1 - 12)
      uint8_t v_0; // = à 0
      uint8_t v_pc; // % de remplissage de la reserve (0 - 100) (moyenne)
    } ;
    gr_an tgr_an[13];
    #define debut_TD 183 // Début tableau Data
    #define fin_TD 192 // Fin tableau Data
    // Variables divers (enregistrement de 183 à 192) 10x4 octets ou 10 int
    struct { // Mémorisation des dernieres mesures en int (enregistrement de 183 à 192)
      int v_x;
    } memodiv;
    int tdiv[11];
    String const v_css_body = "body { background-color: grey; font-family: Sans-Serif; Color: orange; text-align: center;}";
    String const v_css_lien = "a.btn { text-decoration: none; padding: 10px; font-family: arial; font-size: 1em; color: #FFFFFF; background-color: #b2a2c7; border-radius: 24px;-webkit-border-radius: 24px; -moz-border-radius: 24px; border: 4px solid #ffffff; box-shadow: 3px 3px 8px #444444; -webkit-box-shadow: 3px 3px 8px #444444; -moz-box-shadow: 3px 3px 8px #444444; }";
    String const v_css_lien_vol = "a.btn:hover { padding: 10px; color: #ffff00; background-color: #5f497a; border: 4px solid #fbd5b5; box-shadow: 1px 1px 4px #777777; -webkit-box-shadow: 1px 1px 4px #777777; -moz-box-shadow: 1px 1px 4px #777777; }";
    // Structure des infos sauvegardées dans l'eeprom de l'esp
    struct  {
        int h_cuve;
        int c_cuve;
        char v_ok;
    } eeprom_esp;
    char str_mesure[15];
    int mesure;
    int vh_cuve; // Hauteur de la cuve
    int vc_cuve; // Capacité de la cuve
    void init_RTC_complet();
    void charge_tableaux();
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    // Ecriture d'une valeur en mémoire 4 octets dif
    void set_val_mem(uint8_t v_adr){
      system_rtc_mem_write(v_adr, &memoval, sizeof(memoval));
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    // Ecriture d'une valeur en mémoire 1 int de 4 octets
    void set_val_div(uint8_t v_adr){
      system_rtc_mem_write(v_adr, &memodiv, sizeof(memodiv));
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    // Lecture d'une valeur en mémoire 4 octets dif
    void get_val_mem(uint8_t v_adr){
      system_rtc_mem_read(v_adr, &memoval, sizeof(memoval));
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    // Lecture d'une valeur en mémoire 1 int de 4 octets
    void get_val_div(uint8_t v_adr){
      system_rtc_mem_read(v_adr, &memodiv, sizeof(memodiv));
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    // dernieres mesures
    String der_mes(){
      String page = "<!DOCTYPE html><html dir='ltr' lang='fr'><head><meta http-equiv='content-type' content='text/html; charset=UTF-8'>";
      page += "<title>Gestion Reserve d'eau Graph dernieres valeurs</title>";
      page += "<style> " + v_css_body + v_css_lien + v_css_lien_vol + " </style>";
      page += "<script type='text/javascript' src='https://www.gstatic.com/charts/loader.js'></script>";
      page += "<script type='text/javascript'>";
      page += "google.charts.load('current', {'packages':['corechart']});";
      page += "google.charts.setOnLoadCallback(drawChart);";
      page += "function drawChart() { var data = google.visualization.arrayToDataTable([['',''],";
      int vadr=0; while (vadr < 9){ page += "['H" + String(vadr+-9) + "', "; page += String((tdiv[vadr] == 1024) ? 0 : tdiv[vadr]) + "],"; vadr++; }
      page += "['H', " + String((tdiv[9] == 1024) ? 0 : tdiv[9]) + "]]); var options = {title: 'Reserve eau (dernieres valeurs en litres)',";
      page += "curveType: 'function',legend: { position: 'bottom' }}; ";
      page += "var chart = new google.visualization.LineChart(document.getElementById('curve_chart'));";
      page += "chart.draw(data, options);}</script></head><body>";
      page += "<div align='center'>";
      page += "<h1>Gestion réserve d'eau</h1>";
      page += "Cuve(s) d'une hauteur de " + String(vh_cuve) + " cm et d'une capacité de " + String(vc_cuve) + " litres.";
      page += " Nous sommes le " + String(timeinfo->tm_mday) + "/" + String(timeinfo->tm_mon + 1) + "/" +  String(timeinfo->tm_year + 1900);
      page += " il est " + String(timeinfo->tm_hour) + " H " + String(timeinfo->tm_min) + " <br/>";
      page += "<h2>Profondeur enregistrée à la derniere mesure " + String(str_mesure) +  " soit " + String(int(vc_cuve/vh_cuve) * (vh_cuve-mesure)) + " litres" + "</h2>";
      page += "</div>";
      page += "<table align='center'>";
      page += "<tr><td width='60%'><form action='/mod_param' method='POST'>";
      page += "<H3>PARAMETRES</H3><br/>";
      page += "Hauteur maximum de la ou des cuve(s) :  <input type='number' id='h_cuve' name='h_cuve' min='10' max='200' value='" + String(vh_cuve) + "'/> cm<br/><br/>";
      page += "Capacité maximum de la ou des cuve(s) : <input type='number' id='c_cuve' name='c_cuve' min='10' max='10000' value='" + String(vc_cuve) + "'/> litres<br/><br/><br/>";
      page += "Soit " + String(int(vc_cuve/vh_cuve)) + " litres / cm <br/><br/> Soit " + String(int(vc_cuve/100)) + " litres / %";
      page += "<br/><br/><br/><button>Valider les modifications</button> <input type='reset' value='Annuler les modifications' /></form>";
      page += "<br/><br/>Attention en cas de modification de ces valeurs les % mémorisés seront faux, une réinitialisation de toutes les mesures est à prévoir...</td>";
      page += "<td><div id='curve_chart' style='width: 400px; height: 400px'></div></td></tr>";
      page += "<tr><td>";
      page += "<br><p> <a class='btn' target='_self' href='/mem_RTC'>Affichage de toutes les données en mémoire</a> </p>";
      page += "<br><p> <a class='btn' target='_self' href='/raz_data'>Réinitialiser toutes les mesures</a> </p>";
      page += "</td>";
      page += "<td align='center'>";
      page += "<br><p><a class='btn' target='_self' href='/graph_jour'>Graphique des 72 dernieres heures en %</a></p>";
      page += "<br><p><a class='btn' target='_self' href='/graph_mois'>Graphique des 31 derniers jours en %</a></p>";
      page += "<br><p><a class='btn' target='_self' href='/graph_an'>Graphique des 12 derniers mois en %</a></p>";
      page += "</td></tr></table>";
      page += "<br><p><a class='btn' href='https://www.castoo.fr' target='_blank'>Visiter le site https://www.castoo.fr</a></p>";
      page += "</body></html>";
      return page;
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    // Construction de la page HTML Graphique Jour 72 heures
    String page_grjour(){
      String page = "<!DOCTYPE html><html dir='ltr' lang='fr'><head><meta http-equiv='content-type' content='text/html; charset=UTF-8'>";
      page += "<title>Gestion Reserve d'eau Graph 72 heures</title>";
      page += "<style> " + v_css_body + v_css_lien + v_css_lien_vol + " </style>";
      page += "<script type='text/javascript' src='https://www.gstatic.com/charts/loader.js'></script>";
      page += "<script type='text/javascript'>";
      page += "google.charts.load('current', {'packages':['corechart']});";
      page += "google.charts.setOnLoadCallback(drawChart);";
      page += "function drawChart() { var data = google.visualization.arrayToDataTable([['Heure',";
      page += "'Derniere date " + String(tgr_jour[72].v_j) + " " + nom_mois[tgr_jour[72].v_m] + " à " + String(tgr_jour[72].v_h) + "H '],";
      int vadr=0;
      while (vadr <= 72){
        page += "['" + String(tgr_jour[vadr].v_j) + "/" + String(tgr_jour[vadr].v_h) + "h', ";
        page += String((tgr_jour[vadr].v_pc == 255) ? 0 : tgr_jour[vadr].v_pc) + "],";
        vadr++;
      }
      page += "['" + String(tgr_jour[vadr].v_h) + "h', " + String((tgr_jour[vadr].v_pc == 255) ? 0 : tgr_jour[vadr].v_pc) + "]";
      page += "]); var options = {";
      page += "title: 'Evolution de la réserve d eau sur les 72 dernieres heures  en % de remplissage',";
      page += "curveType: 'function',";
      page += "legend: { position: 'bottom' }";
      page += "}; var chart = new google.visualization.LineChart(document.getElementById('curve_chart'));";
      page += "chart.draw(data, options);}</script></head><body><div align='center'><div id='curve_chart' style='width: 1000px; height: 500px'></div>";
      page += "<br><p align='center'><a class='btn' target='_self' href='/'>Retour Accueil</a> ";
      page += " <a class='btn' href='https://www.castoo.fr' target='_blank'>Visiter le site https://www.castoo.fr</a></p>";
      page += "</div></body></html>";
      return page;
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    // Construction de la page HTML Graphique Mois 31 jours
    String page_grmois(){
      String page = "<!DOCTYPE html><html dir='ltr' lang='fr'><head><meta http-equiv='content-type' content='text/html; charset=UTF-8'>";
      page += "<title>Gestion Reserve d'eau Graph 31 jours</title>";
      page += "<style> " + v_css_body + v_css_lien + v_css_lien_vol + " </style>";
      page += "<script type='text/javascript' src='https://www.gstatic.com/charts/loader.js'></script>";
      page += "<script type='text/javascript'>";
      page += "google.charts.load('current', {'packages':['corechart']});";
      page += "google.charts.setOnLoadCallback(drawChart);";
      page += "function drawChart() { var data = google.visualization.arrayToDataTable([['Jour',";
      page += "'(début analyse) Mois de " + nom_mois[tgr_mois[0].v_m] + " " + String(tgr_mois[0].v_a + 1900) + "'],";
      int vadr=0;
      while (vadr <= 30){
        page += "['" + String(tgr_mois[vadr].v_j) + "/" + String(tgr_mois[vadr].v_m) + "', ";
        page += String((tgr_mois[vadr].v_pc == 255) ? 0 : tgr_mois[vadr].v_pc) + "],";
        vadr++;
      }
      page += "['" + String(tgr_mois[vadr].v_j) + "/" + String(tgr_mois[vadr].v_m) + "', " + String((tgr_mois[vadr].v_pc == 255) ? 0 : tgr_mois[vadr].v_pc) + "]";
      page += "]); var options = {title: 'Evolution de la réserve eau sur les 31 derniers jours en % de remplissage',";
      page += "curveType: 'function',legend: { position: 'bottom' }}; ";
      page += "var chart = new google.visualization.LineChart(document.getElementById('curve_chart'));";
      page += "chart.draw(data, options);}</script></head><body><div align='center'><div id='curve_chart' style='width: 900px; height: 500px'></div>";
      page += "<br><p align='center'><a class='btn' href='/' target='_self'>Retour Accueil</a> ";
      page += " <a class='btn' href='https://www.castoo.fr' target='_blank'>Visiter le site https://www.castoo.fr</a></p>";
      page += "valeur 31 => " + String(tgr_mois[31].v_pc);
      page += "</div></body></html>";
      return page;
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    // Construction de la page HTML Graphique An 12 mois
    String page_gran(){
      String page = "<!DOCTYPE html><html dir='ltr' lang='fr'><head><meta http-equiv='content-type' content='text/html; charset=UTF-8'>";
      page += "<title>Gestion Reserve d'eau Graph 12 mois</title>";
      page += "<style> " + v_css_body + v_css_lien + v_css_lien_vol + " </style>";
      page += "<script type='text/javascript' src='https://www.gstatic.com/charts/loader.js'></script>";
      page += "<script type='text/javascript'>";
      page += "google.charts.load('current', {'packages':['corechart']});";
      page += "google.charts.setOnLoadCallback(drawChart);";
      page += "function drawChart() { var data = google.visualization.arrayToDataTable([['Jour',";
      page += "'(début analyse) Année " + String(tgr_an[0].v_a + 1900) + "'],";
      int vadr=0;
      while (vadr <= 11){
        page += "['" + nom_mois[tgr_an[vadr].v_m] + "', ";
        page += String((tgr_an[vadr].v_pc == 255) ? 0 : tgr_an[vadr].v_pc) + "],";
        vadr++;
      }
      page += "['" + nom_mois[tgr_an[vadr].v_m] + "', " + String((tgr_an[vadr].v_pc == 255) ? 0 : tgr_an[vadr].v_pc) + "]";
      page += "]); var options = {title: 'Evolution de la réserve eau sur les 12 derniers mois en % de remplissage',";
      page += "curveType: 'function',legend: { position: 'bottom' }}; ";
      page += "var chart = new google.visualization.LineChart(document.getElementById('curve_chart'));";
      page += "chart.draw(data, options);}</script></head><body><div align='center'><div id='curve_chart' style='width: 900px; height: 500px'></div>";
      page += "<br><p align='center'><a class='btn' href='/' target='_self'>Retour Accueil</a> ";
      page += " <a class='btn' href='https://www.castoo.fr' target='_blank'>Visiter le site https://www.castoo.fr</a></p>";
      page += "valeur 12 => " + String(tgr_an[12].v_pc);
      page += "</div></body></html>";
      return page;
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    // -------------------------- Afficher toutes les valeurs de la mémoire RTC ----------------------
    String aff_toutes_valeurs_RTC(){
      String page = "<!DOCTYPE html><html dir='ltr' lang='fr'><head><meta http-equiv='content-type' content='text/html; charset=UTF-8'>";
      page += "<style> " + v_css_body + v_css_lien + v_css_lien_vol + " </style>";
      page += "<title>mémoire RTC</title>";
      page += "<style> body { background-color: grey; font-family: Sans-Serif; Color: orange; }</style>";
      page += "</head><body><div align='center'><h1>Gestion reserve eau</h1>";
      page += "<p>Liste des variables mémorisées dans la mémoire RTC :</p>";
        for(int vadr=debut_RTC; vadr <= fin_TA; vadr++){
          get_val_mem(vadr);
          page += "<p align='center'> val : " + String(vadr) + " => mois :" + String(memoval.v_m) + " => jour :" + String(memoval.v_j) + " => heure :" + String(memoval.v_h) + " => % :" + String(memoval.v_pc) + "</p>";
        }
        for(int vadr=debut_TD; vadr <= fin_TD; vadr++){
          get_val_div(vadr);
          page += "<p align='center'> val : " + String(vadr) + " RTC x1 :" + String(memodiv.v_x) + "</p>";
        }
      page += "<br><p align='center'><a class='btn' href='/' target='_self'>Retour Accueil</a> ";
      page += " <a class='btn' href='https://www.castoo.fr' target='_blank'>Visiter le site https://www.castoo.fr</a></p>";
      page += "</body></html>";
      return page;
    }
    
    // ---------------------------------------------------------------
    // --------- Demande de page inexistante ------
    void page_inexistante() {
      String page_inexist = "Page inexistante";
      page_inexist += "<br/>URL: ";
      page_inexist += server.uri();
      page_inexist += "<br/>Method: ";
      page_inexist += (server.method() == HTTP_GET) ? "GET" : "POST";
      page_inexist += "<br/>Arguments: ";
      page_inexist += server.args();
      page_inexist += "<br/>";
      for (uint8_t i = 0; i < server.args(); i++) { page_inexist += " " + server.argName(i) + ": " + server.arg(i) + "<br/>"; }
      server.send(404, "text/plain", page_inexist);
      #ifdef debug_sur_usb
        Serial.println("Envoi : page_inexistante");
      #endif
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    // -------------------------- Mesure de la distance ----------------------
    float distanceM(){
      digitalWrite(trigPin, LOW);
      delayMicroseconds(3);
      digitalWrite(trigPin, HIGH);
      delayMicroseconds(10);
      digitalWrite(trigPin, LOW);
      float distance = pulseIn(echoPin, HIGH);
      distance = (distance * 0.0343) / 2; // Calcul de la distance
      return distance;
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    // ---------------- Calcul de la moyenne des mesures --------------------
    float moyenneTableau(float tableau[], float tailleTableau){
        float somme=0;
        for(int i=0; i < tailleTableau; i++){
            somme=somme+tableau[i];
        }
      somme = somme / (tailleTableau);
        return somme;
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    // Remise à 0 de toutes les variables
    void raz_toutes_les_donnees(){
      init_RTC_complet();
      charge_tableaux();
      server.send(200, "text/html", der_mes());
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    // Enregistrement d'une nouvelle valeur dans la mémoire RTC
    void enr_nouvelle_valeur(int v_val){
      int vadr, van, vmois, vjour, vheure;
      vjour = timeinfo->tm_mday;
      vmois = timeinfo->tm_mon + 1;
      vheure = timeinfo->tm_hour;
      van = timeinfo->tm_year;
      int vnbh = 1;
      float v_temp = (vh_cuve - v_val)*1.0 / vh_cuve * 100.0;
      int v_val_pc = int(v_temp);
      if(v_val_pc > 100){v_val_pc = 100;} // En cas d'erreur (lors du dev.)
      #ifdef debug_sur_usb
        Serial.println("vh_cuve : " + String(v_temp));
        Serial.println("v_val : " + String(v_val));
        Serial.println("Enregistrement mesure : " + String(v_val_pc));
      #endif
      int vcumulh = v_val_pc;
      // ------------------------- Enr. dans RTC jour on place la val en position RTC-72(+65) et on recopie TJour de 2 à 72 vers RTC-1(+65) à RTC-71(+65)
      // memo 
      for(vadr=debut_TJ; vadr <= fin_TJ-1; vadr++){
        memoval.v_m =  tgr_jour[vadr-debut_TJ+1].v_m; tgr_jour[vadr-debut_TJ].v_m = tgr_jour[vadr-debut_TJ+1].v_m;
        memoval.v_j =  tgr_jour[vadr-debut_TJ+1].v_j; tgr_jour[vadr-debut_TJ].v_j = tgr_jour[vadr-debut_TJ+1].v_j;
        memoval.v_h =  tgr_jour[vadr-debut_TJ+1].v_h; tgr_jour[vadr-debut_TJ].v_h = tgr_jour[vadr-debut_TJ+1].v_h;
        memoval.v_pc = tgr_jour[vadr-debut_TJ+1].v_pc; tgr_jour[vadr-debut_TJ].v_pc = tgr_jour[vadr-debut_TJ+1].v_pc;
        // selection des données pour calcul de la moyenne du jour
        if(memoval.v_j==vjour && memoval.v_pc!=255){
          vnbh++;
          vcumulh+=memoval.v_pc;
        }
        set_val_mem(vadr);
      }
      // memo 72eme heure
      memoval.v_m = vmois; tgr_jour[72].v_m = vmois;
      memoval.v_j = vjour; tgr_jour[72].v_j = vjour;
      memoval.v_h = vheure; tgr_jour[72].v_h = vheure;
      memoval.v_pc = v_val_pc; tgr_jour[72].v_pc = v_val_pc;
      set_val_mem(fin_TJ);
     // -------------------------------------------- Enr. dans RTC mois
      // calcul de la moyenne du jour
      int moyjour=0;
      moyjour = int(vcumulh/vnbh);
      int vnbj = 1;
      int vcumulj = moyjour;
      // Si nouvelle journée alors on decale les 30 premiers jours
      if(tgr_mois[fin_TM-debut_TM].v_j != vjour){
        for(vadr=debut_TM; vadr <= fin_TM-1; vadr++){
          memoval.v_m = tgr_mois[vadr-debut_TM+1].v_a; tgr_mois[vadr-debut_TM].v_a = tgr_mois[vadr-debut_TM+1].v_a;
          memoval.v_j = tgr_mois[vadr-debut_TM+1].v_m; tgr_mois[vadr-debut_TM].v_m = tgr_mois[vadr-debut_TM+1].v_m;
          memoval.v_h = tgr_mois[vadr-debut_TM+1].v_j; tgr_mois[vadr-debut_TM].v_j = tgr_mois[vadr-debut_TM+1].v_j;
          memoval.v_pc = tgr_mois[vadr-debut_TM+1].v_pc; tgr_mois[vadr-debut_TM].v_pc = tgr_mois[vadr-debut_TM+1].v_pc;
          // selection des données pour calcul de la moyenne du mois
          if(memoval.v_j==vmois && memoval.v_pc!=255){
            vnbj++;
            vcumulj+=memoval.v_pc;
          }
          set_val_mem(vadr);
        }
      }
      // memo 31em jour
      memoval.v_m = van; tgr_mois[31].v_a = van;
      memoval.v_j = vmois; tgr_mois[31].v_m = vmois;
      memoval.v_h = vjour; tgr_mois[31].v_j = vjour;
      memoval.v_pc = moyjour; tgr_mois[31].v_pc = moyjour;
      set_val_mem(fin_TM);
      // ---------------------------- Enr. dans RTC an
      // Calcul moyenne mois
      int moymois=0;
      moymois = int(vcumulj/vnbj);
      // Si nouveau mois alors on decale les 11 premiers mois
      if(tgr_an[fin_TA-debut_TA].v_m != vmois){
        for(vadr=debut_TA; vadr <= fin_TA-1; vadr++){
          memoval.v_m = tgr_an[vadr-debut_TA+1].v_a; tgr_an[vadr-debut_TA].v_a = tgr_an[vadr-debut_TA+1].v_a;
          memoval.v_j = tgr_an[vadr-debut_TA+1].v_m; tgr_an[vadr-debut_TA].v_m = tgr_an[vadr-debut_TA+1].v_m;
          memoval.v_h = tgr_an[vadr-debut_TA+1].v_0; tgr_an[vadr-debut_TA].v_0 = tgr_an[vadr-debut_TA+1].v_0;
          memoval.v_pc = tgr_an[vadr-debut_TA+1].v_pc; tgr_an[vadr-debut_TA].v_pc = tgr_an[vadr-debut_TA+1].v_pc;
          set_val_mem(vadr);
        }
      }
      // memo 12em mois
      memoval.v_m = van; tgr_an[12].v_a = van;
      memoval.v_j = vmois;  tgr_an[12].v_m = vmois;
      memoval.v_h = 0;  tgr_an[12].v_0 = 0;
      memoval.v_pc = moymois;  tgr_an[12].v_pc = moymois;
      set_val_mem(fin_TA);
      // ------------------------- Enr. dans RTC divers
      // On decale dans tous les cas les 9 dernieres valeurs
      for(vadr=1; vadr <= 9; vadr++){
        memodiv.v_x = tdiv[vadr];
        set_val_div((vadr-1)+debut_TD);
      }
      // Mémo derniere valeur
      tdiv[9] = int(vc_cuve / vh_cuve) * (vh_cuve - v_val);
      memodiv.v_x = tdiv[9];
      set_val_div(fin_TD-1); // fin_TD);
      #ifdef debug_sur_usb
        Serial.println("Enregistrement mesure : " + String(tdiv[9]));
      #endif
    
      //-----------------------------
      // a enlever quand le deepsleep sera en route
        //charge_tableaux();
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    // Chargement des tableaux (jour, mois, an, divers)
    void charge_tableaux(){
      int vadr;
      // Chargement tableau Jour
      #ifdef debug_sur_usb
        Serial.println("Chargement tableau Jour");
      #endif
      for(vadr=debut_TJ-1; vadr <= fin_TJ; vadr++){
        get_val_mem(vadr);
        tgr_jour[vadr-debut_TJ].v_m = memoval.v_m;
        tgr_jour[vadr-debut_TJ].v_j = memoval.v_j;
        tgr_jour[vadr-debut_TJ].v_h = memoval.v_h;
        tgr_jour[vadr-debut_TJ].v_pc = memoval.v_pc;
        #ifdef debug_sur_usb
          Serial.println( "val : " + String(vadr-debut_TJ) + " => mois :" + String(tgr_jour[vadr-debut_TJ].v_m) + " => jour :" + String(memoval.v_j) + " => heure :" + String(memoval.v_h) + " => % :" + String(tgr_jour[vadr-debut_TJ].v_pc) );
        #endif
      }
      
      // Chargement tableau Mois
      #ifdef debug_sur_usb
        Serial.println("Chargement tableau Mois");
      #endif
      for(vadr=debut_TM; vadr <= fin_TM; vadr++){
        get_val_mem(vadr);
        tgr_mois[vadr-debut_TM].v_a = memoval.v_m;
        tgr_mois[vadr-debut_TM].v_m = memoval.v_j;
        tgr_mois[vadr-debut_TM].v_j = memoval.v_h;
        tgr_mois[vadr-debut_TM].v_pc = memoval.v_pc;
        #ifdef debug_sur_usb
          Serial.println( "val : " + String(vadr) + " => mois :" + String(memoval.v_m) + " => jour :" + String(memoval.v_j) + " => heure :" + String(memoval.v_h) + " => % :" + String(memoval.v_pc) );
        #endif
     }
      // Chargement tableau An
      #ifdef debug_sur_usb
        Serial.println("Chargement tableau An");
      #endif
      for(vadr=debut_TA; vadr <= fin_TA; vadr++){
        get_val_mem(vadr);
        tgr_an[vadr-debut_TA].v_a = memoval.v_m;
        tgr_an[vadr-debut_TA].v_m = memoval.v_j;
        tgr_an[vadr-debut_TA].v_0 = memoval.v_h;
        tgr_an[vadr-debut_TA].v_pc = memoval.v_pc;
        #ifdef debug_sur_usb
          Serial.println( "val : " + String(vadr) + " => mois :" + String(memoval.v_m) + " => jour :" + String(memoval.v_j) + " => heure :" + String(memoval.v_h) + " => % :" + String(memoval.v_pc) );
        #endif
      }
      // Chargement tableau Divers
      #ifdef debug_sur_usb
        Serial.println("Chargement tableau Divers");
      #endif
      for(vadr=debut_TD; vadr <= fin_TD; vadr++){
        get_val_div(vadr);
        tdiv[vadr-debut_TD] = memodiv.v_x;
        #ifdef debug_sur_usb
          Serial.println( "val : " + String(vadr) + " => x : " + String(memodiv.v_x)  );
        #endif
      }
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    // Sauve param dans eeprom esp
    void sauve_data_eeprom_esp(){
        eeprom_esp.h_cuve = vh_cuve;
        eeprom_esp.c_cuve = vc_cuve;
        eeprom_esp.v_ok = 'O';
        EEPROM.put(0,eeprom_esp);
        EEPROM.commit();
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    // Les parametres ont été modifiés
    void modif_param(){
      if ( server.hasArg("h_cuve")) { vh_cuve = server.arg("h_cuve").toInt(); sauve_data_eeprom_esp();}
      if ( server.hasArg("c_cuve")) { vc_cuve = server.arg("c_cuve").toInt(); sauve_data_eeprom_esp();}
      server.send ( 200, "text/html", der_mes() );
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    void setup() {
      #ifdef debug_sur_usb
        Serial.begin(9600);
      #endif
      #ifdef debug_temp
        Serial.begin(9600);
      #endif
      pinMode(trigPin, OUTPUT); // Broche triger en sortie
      pinMode(echoPin, INPUT); // Broche echo en entree
      pinMode(le_plus, OUTPUT); // Le plus du S100
      digitalWrite(le_plus, LOW);
      IPAddress ip(192, 168, 1, 87); // Declaration adr IP fixe
      IPAddress dns(192,168,1,1);
      IPAddress gateway(192,168,1,1);
      IPAddress subnet(255, 255, 255, 0);
      WiFi.mode(WIFI_STA);
      WiFi.config(ip, gateway, subnet, dns);
      WiFi.begin(ssid, password);
      //wifiMulti.addAP(ssid, password);
      #ifdef debug_sur_usb
        Serial.println("Init WiFi : ");
      #endif
      //while (wifiMulti.run() != WL_CONNECTED) {
      while (WiFi.status() != WL_CONNECTED) {
        #ifdef debug_sur_usb
          Serial.print(".");
        #endif
        delay ( 100 );
      }
      #ifdef debug_sur_usb
        Serial.println("Init TIME_ZONE pool.ntp.org : ");
      #endif
      //configTime(TIME_ZONE, "pool.ntp.org", "time.nis.gov");
      configTime(0, 0 , "pool.ntp.org", "time.nis.gov"); // 7200 heure hiver / 3600 heure ete
      setenv("TZ", "CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00", 1);
      while (time(nullptr) < 1000000000ul) {
        delay(100);
        #ifdef debug_sur_usb
          Serial.print(".");
        #endif
      }
      time_t now = time(nullptr);
      timeinfo = localtime (&now);
      char buffer [80];
      strftime (buffer,80,"Local time: %H:%M.",timeinfo);
      #ifdef debug_sur_usb
        Serial.println(buffer);
      #endif
      time(&now);
      temps_qui_passe = millis();
      temps_inter_h = temps_qui_passe;
      top_mesure = true;
      EEPROM.begin(sizeof(eeprom_esp)+1); //eeprom esp
      EEPROM.get(0, eeprom_esp);
      if(eeprom_esp.v_ok == 'O'){ // l'enregistrement est valide alors on charge les variables de l'eeprom
        vh_cuve = eeprom_esp.h_cuve;
        vc_cuve = eeprom_esp.c_cuve;
      } else {
        vh_cuve = 170;  // valeur par defaut pour hauteur
        vc_cuve = 2000; // valeur par defaut pour capacité
        sauve_data_eeprom_esp();
      }
      charge_tableaux(); // Chargement des tableaux (jour, mois, an, divers) qui ont été sauvegardés pendant le deepsleep
      server.on ( "/"          ,  [](){ server.send(200, "text/html", der_mes()); });
      server.on ( "/graph_jour",  [](){ server.send(200, "text/html", page_grjour()); });
      server.on ( "/graph_mois",  [](){ server.send(200, "text/html", page_grmois()); });
      server.on ( "/graph_an"  ,  [](){ server.send(200, "text/html", page_gran()); });
      server.on ( "/mem_RTC"   ,  [](){ server.send(200, "text/html", aff_toutes_valeurs_RTC()); });
      server.on ( "/raz_data"  , raz_toutes_les_donnees);
      server.on ( "/mod_param" , modif_param); // Retour des parametres (hauteur cuve et capacité cuve)
      server.onNotFound(page_inexistante);
      server.begin();
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    // Initialise la totalité des tableaux de variables
    void init_RTC_complet(){
      int vadr, van, vmois, vjour, vheure;
      // RAZ memo jour (72 heures)
      vjour = timeinfo->tm_mday;
      vmois = timeinfo->tm_mon + 1;
      vheure = timeinfo->tm_hour;
      for(vadr=debut_TJ; vadr <= fin_TJ; vadr++){
        memoval.v_m = vmois;
        memoval.v_j = vjour;
        memoval.v_h = vheure;
        vheure++;
        if(vheure==24){
          vheure=0;
          vjour++; 
          if(vjour==31){ 
            vjour=1; 
            vmois++;
            if(vmois==13) vmois=1;
          } 
        }
        memoval.v_pc = 255;
        set_val_mem(vadr);
      }
      // RAZ memo mois (31 jours)
      van = timeinfo->tm_year;
      vmois = timeinfo->tm_mon + 1;
      vjour = timeinfo->tm_mday;
      for(vadr=debut_TM; vadr <= fin_TM; vadr++){
        memoval.v_m = van;
        memoval.v_j = vmois;
        memoval.v_h = vjour;
        vjour++;
        if(vjour==31){
          vjour=1;
          vmois++; 
          if(vmois==13){ 
            vmois=1; 
            van++;
          } 
        }
        memoval.v_pc = 255;
        set_val_mem(vadr);
      }
      // RAZ memo an (12 mois)
      van = timeinfo->tm_year;
      vmois = timeinfo->tm_mon + 1;
      for(vadr=debut_TA; vadr <= fin_TA; vadr++){
        memoval.v_m = van;
        memoval.v_j = vmois;
        vmois++;
        if(vmois==13){
          vmois=1;
          van++; 
        }
        memoval.v_h = 0;
        memoval.v_pc = 255;
        set_val_mem(vadr);
      }
      // RAZ memo variables divers
      for(vadr=debut_TD; vadr <= fin_TD; vadr++){
        memodiv.v_x = 1024;
        set_val_div(vadr);
      }
    }
    
    // -----------------------------------------------------------------------------------------------------------------------------------
    void loop() {
      server.handleClient();
      if(top_mesure){ // On vient de demarrer alors on lance une mesure
        #ifdef debug_sur_usb
          Serial.println("Lancement de la mesure suite demarrage...");
        #endif
        digitalWrite(le_plus, HIGH); // On alimente le capteur
        delay(5);
        float vmesure[10];
        float resultat = 0;
        for(int cpt = 0; cpt < 10; cpt++){
          vmesure[cpt] = distanceM();
          delay(10);
        }
        digitalWrite(le_plus, LOW); // On coupe l'alim du capteur
        resultat = moyenneTableau(vmesure, 10);
        mesure = int(resultat);
        snprintf (str_mesure, 50, " : %d cm", mesure);
        enr_nouvelle_valeur(mesure);
        top_mesure = false; // On ne relancera pas la mesure avant une heure
      }
      if((millis() - temps_inter_h) >= 60000){ // on ne lance une interrogation serveur horaire qu'une fois toutes les minutes
        time_t now = time(nullptr);
        timeinfo = localtime (&now);
        temps_inter_h += 60000;
        #ifdef debug_sur_usb
          Serial.println(String(timeinfo->tm_hour) + "H" + String(timeinfo->tm_min));
          Serial.println("Lancement interrogation serveur heure...");
        #endif
      }
      if((millis() - temps_qui_passe) >= memo_temps){ // On ne laisse que 5 minutes en route pour faire la mesure et se connecter au wifi.
        #ifdef debug_sur_usb
          Serial.println(String(timeinfo->tm_hour) + "H" + String(timeinfo->tm_min));
          Serial.println("Les 5 minutes sont en cours...");
        #endif
        if(timeinfo->tm_min > 5){ // Si on est bien sur un nombre de minute > 5 on coupe l'esp
          #ifdef debug_sur_usb
            Serial.println(String(timeinfo->tm_hour) + "H" + String(timeinfo->tm_min));
            Serial.println("Les 5 minutes sont en passées, arret de l'esp !!!!!-------!!!!!!");
          #endif
          // on essai de partir sur un arret de l'esp jusqu'à la prochaine heure pleine.
          if(timeinfo->tm_min == 6){
            ESP.deepSleep(duree_sommeil); // On coupe l'esp pendant 54 minutes (impossible de le joindre pendant cette période)
          }else{
            int nb_min = 60 - timeinfo->tm_min;
            ESP.deepSleep((60e6)*nb_min); // On coupe l'esp jusqu'à la prochaine heure pleine (impossible de le joindre pendant cette période)
          }
        }
      }
      delay(100);
    }
  



accueil electronique

Bricolage Robotique Informatique Peinture Voyage
Téléc. portail Le robot "mécano" Astuces informatique Henri Bertrou Auvergne
Bat. Iphone 6S Le robot "solaire" Réseau couche app. Jean-Michel Castille Floride
Robot piscine Servo et IR" Réseau Les couches New York
Xiaomi M365 Le robot "thymio" Réseaux Outils L'Ouest américain
Mac Mini Le robot "Rovio" Unités grandeur inf. L'Ile Maurice
Putty SSH Windows L'Italie / Venise
Bases Raspberry Tunisie
Termius IPhone/IPad Grece
Le vieux ZX 81
...
Navigation La Rochelle CNC / Imp3D Electronique Programmation
Rencontre dauphins Les Minimes Construction CNC Alim. TPL 5110 Doc. programme
Analyse NMEA 0183 Le Vieux port CNC du commerce Carte ESP8266 Indent programme
graph. NMEA 0183 L'Ile de Ré Martyr CNC ESP8266 1 relai Prog. objet
Analyse trames AIS A visiter Réa. imp. 3D ESP8266 Alarme Prog. procédurale
Analyse AIS TCP-IP Cura impression 3D ESP8266 MQTT
Sortie en ketch Plateau CR10 ESP8266 Temp.
Echange GPS C80 Anémomètre.
HP Sun-Odyssey CNC / 3D en vrac MCP9808 Librairie
LCD yanmar Saisie Oled
Testeur nmea esp1 i2c