Un minuteur de douche avec un module ESP8266
Les papas, eux aussi, aspirent à ressentir encore le réconfort de |'eau chaude sur leur corps lors de la douche matinale… et ce, sans devoir se lever avant les prémices de l’aube pour pouvoir en bénéficier. Ma maison étant le foyer de deux adolescentes, et la nécessité est la mère de l’invention, je me suis résolu à installer une minuterie de douche sur mon réservoir d’eau chaude afin de briser la malédiction de la douche froide.

<h2>Principe de fonctionnement</h2><p>Une vanne à bille motorisée est installée sur la sortie d’eau chaude du chauffe-eau. Elle est accouplée avec un débitmètre électronique. Grâce à ce dernier, un petit dispositif à base de microcontrôleur surveille la consommation d’eau chaude et quand une certaine période d’utilisation continue est atteinte, il actionne à plusieurs reprises la fermeture et l’ouverture de l’électrovanne afin d’envoyer le message clair à la personne sous la douche que son temps autorisé est dépassé.</p><h2>Matériel requis</h2><p>Pour ce projet, j’ai choisi d’utiliser une carte Adafruit Huzzah ESP8266. Son prix est modeste et elle propose plein de fonctionnalités intéressantes à utiliser. En premier lieu, c’est une carte WiFi, ce qui autorise son contrôle à distance. Elle peut ainsi être initialisée comme point d’accès, permettant de s’y connecter et de la configurer pour qu’elle rejoigne le réseau de la maison. En second lieu, elle peut faire tourner un serveur Web et accepte les mises à jour distantes de son programme. Une fois que le programme (croquis/sketch) est installé, il n’est donc plus nécessaire de se connecter via son port USB pour le mettre à jour. Enfin, l’ESP8266 vient avec un régulateur de tension intégré. On peut abaisser le courant sortant de l’alimentation de la valve (5 V) à un niveau pouvant être géré par les broches GPIO.</p><ul> <li><picture><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-01.400x0.webp" media="(max-width: 599px)"><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-01.760x0.webp" media="(max-width: 999px)"><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-01.1039x0.webp" media="(min-width: 1000px)"><img src="https://mirror.spiria.com/site/assets/files/2016/mrshower-01.jpg" alt="Adafruit HUZZAH ESP8266." title="Adafruit HUZZAH ESP8266."></source></source></source></picture> <a href="https://www.adafruit.com/product/2471">Adafruit HUZZAH ESP8266</a>.</li> <li style="clear: both;"><picture><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-02.400x0.webp" media="(max-width: 599px)"><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-02.760x0.webp" media="(max-width: 999px)"><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-02.1039x0.webp" media="(min-width: 1000px)"><img src="https://mirror.spiria.com/site/assets/files/2016/mrshower-02.jpg" alt="USB to TTL Serial Cable." title="USB to TTL Serial Cable."></source></source></source></picture> <a href="https://www.adafruit.com/products/954">Câble USB vers TTL (série)</a>.</li> <li style="clear: both;"><picture><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-03.400x0.webp" media="(max-width: 599px)"><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-03.760x0.webp" media="(max-width: 999px)"><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-03.1039x0.webp" media="(min-width: 1000px)"><img src="https://mirror.spiria.com/site/assets/files/2016/mrshower-03.webp" alt="MISOL 3/4” DN20 Electronic Flow Meter." title="MISOL 3/4” DN20 Electronic Flow Meter."></source></source></source></picture> Un <a href="https://www.aliexpress.com/popular/electronic-flow-meter.html">débitmètre électronique 3/4"</a> (AliExpress).</li> <li style="clear: both;"><picture><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-04.400x0.webp" media="(max-width: 599px)"><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-04.760x0.webp" media="(max-width: 999px)"><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-04.1039x0.webp" media="(min-width: 1000px)"><img src="https://mirror.spiria.com/site/assets/files/2016/mrshower-04.webp" alt="Motorized Ball Valve - CR02 wiring." title="Motorized Ball Valve - CR02 wiring."></source></source></source></picture>Une <a href="https://www.aliexpress.com/popular/motorized-ball-valve.html">vanne à bille motorisée, 5 V, 3/4"</a> (AliExpress).</li> <li style="clear: both;"><picture><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-05b.400x0.webp" media="(max-width: 599px)"><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-05b.760x0.webp" media="(max-width: 999px)"><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-05b.1039x0.webp" media="(min-width: 1000px)"><img src="https://mirror.spiria.com/site/assets/files/2016/mrshower-05b.webp" alt="5V 2A DC Power Supply." title="5V 2A DC Power Supply."></source></source></source></picture>Une <a href="https://www.amazon.ca/s/ref=nb_sb_noss_2?url=search-alias%3Daps&field-keywords=5+Volt+Power+Supply">alimentation 5 V</a> (Amazon).</li> <li style="clear: both; padding-top: 2em;">Une plaque d’essai (breadboard), du fil électrique, un boîtier de projet.</li></ul><h2>Schéma de câblage</h2><p><picture><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-06.400x0.webp" media="(max-width: 599px)"><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-06.760x0.webp" media="(max-width: 999px)"><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-06.1039x0.webp" media="(min-width: 1000px)"><img src="https://mirror.spiria.com/site/assets/files/2016/mrshower-06.webp" alt="Wiring Diagram." title="Wiring Diagram."></source></source></source></picture></p><h2>Installation</h2><p><picture><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-07.400x0.webp" media="(max-width: 599px)"><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-07.760x0.webp" media="(max-width: 999px)"><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-07.1039x0.webp" media="(min-width: 1000px)"><img src="https://mirror.spiria.com/site/assets/files/2016/mrshower-07.webp" alt="Installation." title="Installation."></source></source></source></picture></p><h2>Programme</h2><p>La logique est assez simple. Une routine d’interruption surveille les impulsions générées par le débitmètre. Ces impulsions sont accumulées et une minuterie, chaque minute, appelle une routine qui stocke les impulsions accumulées dans un tableau pouvant contenir jusqu’à 15 minutes de données. Si toutes les cellules du tableau contiennent une valeur autre que 0, le contrôleur ferme puis ouvre la vanne.</p><h2>Code source</h2><pre><code>// MrShower// Created by Patrick Walsh.//// Hot Water monitoring system installed at the intake of a hot water tank.// When hot water has been running for 15 minutes an electrical ball valve will cycle off and on every minute until usage ends.( Shower ) an elc//#include <esp8266httpupdateserver.h>#include <esp8266mdns.h>#include <esp8266wifi.h>#include <ticker.h> #include "WiFiManager.h"#define SKETCH_VERSION 1.0#define HAL_SENSOR_PIN 15 // Flowmeter Hal sensor is connected to this pin #define MONITOR_MINS 15 // Showers can last a maximum of 15 mins before warning kicks in#define SHOWER_SHUTOFF_WARNING_TIME 5.0 // Duration valve will be closed to warn time is up#define TIMER_PERIOD 60.0 // Period required to calculate RPM of Hal effect sensor#define VALVE_CLOSE_CTRL_PIN 12 // Relay connected to the electrical valve Open Wire #define VALVE_OPEN_CTRL_PIN 14 // Relay connected to the electrical valve Close Wire #define VALVE_TRANSIT_DELAY 2.0 // Time taken for valve to move from closed state ot open state//// Global variables//const char* g_HostName = "MrShower"; // DuhESP8266WebServer g_HttpServer(80); // Http server we will be providingESP8266HTTPUpdateServer g_HttpUpdater(false); // A OverTheAir update service. Http://MrShower.local/updateTicker g_MonitoringTimer; // Interupt timer used for counting minutesunsigned g_Rpms[MONITOR_MINS]; // History of rpms in the last "n" minutes// global variables accessed from ISR that need to be protectedvolatile bool g_FlashWater = false; // True if valve needs to be closed temporarilyvolatile unsigned g_HallSensorPulses = 0; // FlowMeter pulses that have occurred in the current minutevolatile bool g_WaterOn = false; // State of Valve //// Forward Declarations//void closeWaterValve();void openWaterValve();void onHallEffect();void onTimerTick();void webServerHandleRoot();void webServerHandleNotFound();void webServerHandleOpenCmd();void webServerHandleCloseCmd();void webServerHandleResetCmd();void webServerHandleCycleCmd();/////////////////////////////////////////////////////////////////////// Setup is called at initialization///////////////////////////////////////////////////////////////////////void setup(){ // Initialize Relay control pinMode(VALVE_CLOSE_CTRL_PIN, OUTPUT); pinMode(VALVE_OPEN_CTRL_PIN, OUTPUT); // Make sure the water valve is open. openWaterValve(); g_HallSensorPulses = 0; // Setup an Access point in order to allow network setup WiFiManager wifiManager; wifiManager.autoConnect( g_HostName ); WiFi.mode( WIFI_STA ); WiFi.softAPdisconnect( true ); // Establish a connection with our configured access point while( WiFi.waitForConnectResult() != WL_CONNECTED ) { WiFi.begin(); } // install web serv handlers. g_HttpServer.on("/", webServerHandleRoot ); g_HttpServer.onNotFound( webServerHandleNotFound ); g_HttpServer.on("/open", webServerHandleOpenCmd); g_HttpServer.on("/close", webServerHandleCloseCmd ); g_HttpServer.on("/reset", webServerHandleResetCmd ); g_HttpServer.on("/cycle", webServerHandleCycleCmd ); // Add OTA update service provided by library "/update" command g_HttpUpdater.setup( &g_HttpServer ); g_HttpServer.begin( ); // Register web services we expose with Bonjour MDNS.begin( g_HostName ); MDNS.addService( "http", "tcp", 80 ); // Clear the array of rpms memset( g_Rpms, 0, sizeof( g_Rpms ) ); // Attach Hall Sensor to HAL_SENSOR_PIN. // ISR called when voltage rises to match one // revolution of the flowmeter turbine attachInterrupt( HAL_SENSOR_PIN, onHallEffect, RISING ); // Start a timer which will treat the reading of the hall sensor and calculate RPM g_MonitoringTimer.attach( TIMER_PERIOD, onTimerTick );}/////////////////////////////////////////////////////////////////////// Code in the loop method will be run repeatedly/////////////////////////////////////////////////////////////////////void loop() { // Allow webserver to process queue of requests sent to it. g_HttpServer.handleClient(); if( g_FlashWater ) { // Need to cycle the hot water valve closeWaterValve( ); delay( SHOWER_SHUTOFF_WARNING_TIME * 1000 ); openWaterValve( ); g_FlashWater = false; }}/////////////////////////////////////////////////////////////////////// Close water valve by controlling 2 relays connected// to ball valve motor/////////////////////////////////////////////////////////////////////void closeWaterValve(){ digitalWrite( VALVE_OPEN_CTRL_PIN, HIGH ); digitalWrite( VALVE_CLOSE_CTRL_PIN, LOW ); delay( VALVE_TRANSIT_DELAY * 1000 ); digitalWrite(VALVE_CLOSE_CTRL_PIN, HIGH ); g_WaterOn = false;}/////////////////////////////////////////////////////////////////////// Open water valve by controlling 2 relays connected// to ball valve motor/////////////////////////////////////////////////////////////////////void openWaterValve(){ digitalWrite( VALVE_CLOSE_CTRL_PIN, HIGH ); digitalWrite( VALVE_OPEN_CTRL_PIN, LOW ); delay( VALVE_TRANSIT_DELAY * 1000 ); digitalWrite( VALVE_OPEN_CTRL_PIN, HIGH ); g_WaterOn = true;}/////////////////////////////////////////////////////////////////////// Interrupt routine called when FlowMeter is generating Hal Effect pulses./////////////////////////////////////////////////////////////////////void onHallEffect(){ // Increase the number of pulses detected by the HallEffect sensor // of the water flowmeter g_HallSensorPulses++;}/////////////////////////////////////////////////////////////////////// Timer-driven callback routine called every minute./////////////////////////////////////////////////////////////////////void onTimerTick(){ // At every clock tick, once per minute, interrupts temporarily disabled while // accumulated pulses generated by flowmeter sensor are read and reset. This gives us a rpm value. cli( ); // disable interupts long l_rpm = g_HallSensorPulses; // copy rpm value locally g_HallSensorPulses = 0; // sensor reading done for this minute sei( ); // reenable interrupts // shift the content of the array to the right 1 memmove( &g_Rpms[1], &g_Rpms[0], sizeof( g_Rpms ) - sizeof( g_Rpms[0] ) ); // Set the latest reading in array g_Rpms[0] = l_rpm; int l_numUsedTicks = 0; for( int idx = 0; idx < MONITOR_MINS; idx++ ) { if( g_Rpms[idx] ) { l_numUsedTicks++; } } // Do we need to flash hot water if( l_numUsedTicks >= MONITOR_MINS && !g_FlashWater ) { g_FlashWater = true; // Set last element of array to 0 to delay next hot water valve cycle by one minute g_Rpms[ MONITOR_MINS - 1 ] = 0; }}/////////////////////////////////////////////////////////////////////// Web Server section/////////////////////////////////////////////////////////////////////void webServerHandleRoot() { String message; message += g_HostName; message += "Version "; message += SKETCH_VERSION; message += " \n"; message += "FlowMeter revolutions this minute "; message += g_HallSensorPulses; message += " \n"; message += g_WaterOn == true ? "Valve is Opened" : "Valve is Closed"; message += " : "; for(int idx = 0; idx < MONITOR_MINS; idx++) { message += g_Rpms[idx]; message += " "; } message += " \n"; message += "Commands : /open /close /update /cycle /reset\n"; g_HttpServer.send(200, "text/plain", message ); }/////////////////////////////////////////////////////////////////////// Unknown page requested/////////////////////////////////////////////////////////////////////void webServerHandleNotFound() { String message = "File Not Found\n\n"; message += "URI: "; message += g_HttpServer.uri(); message += "\nMethod: "; message += (g_HttpServer.method() == HTTP_GET) ? "GET" : "POST"; message += "\nArguments: "; message += g_HttpServer.args(); message += "\n"; for (uint8_t i = 0; i < g_HttpServer.args(); i++) { message += " " + g_HttpServer.argName(i) + ": " + g_HttpServer.arg(i) + "\n"; } g_HttpServer.send(404, "text/plain", message);}/////////////////////////////////////////////////////////////////////// Open requested/////////////////////////////////////////////////////////////////////void webServerHandleOpenCmd(){ g_HttpServer.send(200, "text/plain", "Opening Valve"); openWaterValve();} /////////////////////////////////////////////////////////////////////// Close requested/////////////////////////////////////////////////////////////////////void webServerHandleCloseCmd(){ g_HttpServer.send(200, "text/plain", "Closing Valve"); closeWaterValve();}/////////////////////////////////////////////////////////////////////// Reset requested/////////////////////////////////////////////////////////////////////void webServerHandleResetCmd(){ memset( g_Rpms, 0, sizeof( g_Rpms ) ); openWaterValve(); g_HttpServer.send(200, "text/plain", "System was reset" );}/////////////////////////////////////////////////////////////////////// Cycle the valve off and on/////////////////////////////////////////////////////////////////////void webServerHandleCycleCmd(){ g_HttpServer.send(200, "text/plain", "Cycling water valve" ); // Set the Flash water variable to true. // The loop routine, when true, will cycle the water valve, g_FlashWater = true;}</ticker.h></esp8266wifi.h></esp8266mdns.h></esp8266httpupdateserver.h></code></pre><p><picture><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-08.400x0.webp" media="(max-width: 599px)"><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-08.760x0.webp" media="(max-width: 999px)"><source type="image/webp" srcset="https://mirror.spiria.com/site/assets/files/2016/mrshower-08.1039x0.webp" media="(min-width: 1000px)"><img src="https://mirror.spiria.com/site/assets/files/2016/mrshower-08.png" alt="Web Server." title="Web Server."></source></source></source></picture></p><p style="display: block;padding: 0.5em 2em 0.7em 2em;margin: 1em 0 2em 0;background-color: #fff;border-radius:4px;box-shadow: 1px 7px 15px 0 rgba(47,57,64,0.2);text-align: center;"><b><i>Le saviez-vous ?</i></b><br>Les équipes de Spiria ont une longue expérience en <a href="https://www.spiria.com/fr/services/developpement-axe-performance/developpement-logiciel-sur-mesure/">développement de logiciel sur mesure</a> et peuvent vous aider sur tout projet d’envergure.</p><h2>Idées d'améliorations</h2><ul> <li>Enregistrer la consommation d’eau chaude par mois ou par année.</li> <li>Fournir une interface utilisateur pour faciliter la configuration (temps de douche autorisé, temporisation des cycles de la vanne).</li> <li>Ajouter un capteur d’eau au sol pour détecter les fuites accidentelles du chauffe-eau.</li> <li>Prise en charge d’Apple Homekit ou d’un autre réseau domotique.</li></ul>
Travaillons ensemble
