Sommaire
Mesurez la consommation d’eau des douches avec un ESP32 et des capteurs de débit. Suivez les litres consommés, recevez alertes sonores et visuelles, et sensibilisez la famille à l’eau. Simple, pratique et écolo !
Introduction
Ce tuto partait d'un besoin simple, surtout depuis ma dernière facture d'eau : connaître la consommation des douches des kids à la maison.
J'avais 3 ans de consommation fixe, à savoir 89 m³ à l'année (pour un foyer de 4 personnes, 2 adultes et deux ados 16 et 17 ans cette année) et la dernière facture de 103 m³ m'a fait me pencher et me sensibiliser sur l'eau.
Nous avons 2 salles de bain, une utilisée par les kids, au 1er étage et une autre pour nous, les parents, au 2ᵉ étage.
J'aurais pu me connecter au compteur principal, mais ayant des difficultés, de distance et donc de portée radio et wifi (compteur à 35 m à l'extérieur de la maison) et pas de point d'énergie à proximité du compteur… J'ai abandonné l'idée. Finalement, je me concentre sur l'essentiel : l'eau des douches.
Voici donc deux vidéos courtes qui illustrent le concept :
Matériel
Le matériel utilisé est le suivant.
Mesures
- 1 ESP32 (celui qui vous plaira, un mini fera l'affaire, on a besoin de 2 GPIO pour les capteurs)
J'ai pris un ESP32-WROOM-32U avec antenne déportée pour plus de sensibilité étant donné l'endroit exigu, enfermé sous la baignoire.

- 2 capteurs / débitmètres :
- Soit la version "Cheap" Plastique : YF-S201C (Attention il y a un sens de montage) Au moment de la réalisation j'ai fait avec ce modèle, mais j'ai préféré la sureté et je suis passé peu de temps après sur le modèle laiton (cité juste après).

- Soit la version laiton (conseillée) : YF-B8.

- 1 alimentation 5V.

- Du câble et des connecteurs Dupont si vous ne voulez pas souder. C'est mon choix, car plus facile à démonter en cas de problème.
- 2 résistances de 10 Kohms pour le pull down et ainsi éviter d'avoir des impulsions parasites.
- Bonus selon votre config : 2 X Manchon laiton double Femelle 15/21 (1/2") à butée (valable pour la version plastique)
https://www.bricodepot.fr/rouen/manchon-laiton-double-femelle-1521-12-a-butee/prod78936/
Comme vous le verrez sur la vidéo, mes nourrices ont un filetage mâle et les capteurs également... donc 2 manchons à prévoir et 4 joints (2 pour chaque manchon) Mais la question ne se pose plus si vous choisissez la version laiton, puisqu'il y a une extrémité mâle et une extrémité femelle.
Il y a un sens de montage, sur le modèle plastique, vous avez une flèche bien visible. pour le modèle laiton, c'est moins flagrant, le sens de circulation de l'eau se fait dans le sens filetage femelle vers mâle.








- Bonus 2: le boitier, si vous avez une imprimante 3D...


Affichage
- 1 ESP32-S3 (celui qui vous plaira aussi, j'avais ce modèle sous le coude et je ne regrette pas, car LED embarquée : le programme pour le coup la prend en compte)

- 1 afficheur 7 segments TM1637 de la couleur de votre convenance.

- 1 alimentation 5 V (le même que pour la mesure, je prends 5V 3A pour ne jamais être embêté)
J'en avais 4 sous le coude pour un autre projet, mais le câble était trop court donc c'est parfait, en voilà 2 d'utilisées. - 1 buzzer PASSIF (récupéré pour moi certainement d'un ancien PC) Passif, car on peut faire des "mélodies" alors qu'un actif fera un son fixe continu.

- 1 capteur tactile (capacitif) TTP223.




J'ai masqué avec du scotch électricien la LED de l'afficheur 7 segments, elle n'est pas désactivable, l'autre solution serait de la dessouder...






Code
Mesures
Pour commencer, la partie pour la mesure de l'eau à l'aide des deux YF-S201C (ou les deux YF-B8, selon ce que vous aurez choisi) :
esphome:
name: debitmetre_sdb
friendly_name: Débitmètre Douche SDB
esp32:
board: esp32dev
framework:
type: arduino
logger:
api:
encryption:
key: "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
ota:
- platform: esphome
password: "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
web_server:
port: 80
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
captive_portal:
mqtt:
broker: !secret mqtt_broker
username: !secret mqtt_user
password: !secret mqtt_password
discovery: true
discovery_retain: true
topic_prefix: "esp32/debitmetre"
birth_message:
topic: esp32/debitmetre/status
payload: online
will_message:
topic: esp32/debitmetre/status
payload: offline
globals:
- id: eau_froide
type: float
restore_value: yes
initial_value: '0.0'
- id: eau_chaude
type: float
restore_value: yes
initial_value: '0.0'
sensor:
- platform: pulse_counter
pin:
number: GPIO26
mode:
input: true
pullup: false
id: debit_1
update_interval: 5s
on_raw_value:
then:
- lambda: |-
ESP_LOGI("EAU FROIDE", "Pulses reçus: %d", x);
id(eau_froide) += x * 0.00031;
# La première ligne avec les Pulses reçus permet de savoir que tout est en ordre et que vous n'avez pas de pulses parasites
# Surtout si vous ne tirez pas d'eau : les pulses doivent être à 0
- platform: pulse_counter
pin:
number: GPIO27
mode:
input: true
pullup: false
id: debit_chaude
internal_filter: 13us
update_interval: 5s
on_raw_value:
then:
- lambda: |-
ESP_LOGI("EAU CHAUDE", "Pulses reçus: %d", x);
id(eau_chaude) += x * 0.00031;
# Eau froide
- platform: template
name: "Volume Eau Froide"
id: sensor_eau_froide
unit_of_measurement: "L"
accuracy_decimals: 2
update_interval: 5s
lambda: |-
return id(eau_froide);
# Eau chaude
- platform: template
name: "Volume Eau Chaude"
id: sensor_eau_chaude
unit_of_measurement: "L"
accuracy_decimals: 2
update_interval: 5s
lambda: |-
return id(eau_chaude);
button:
- platform: template
name: "Réinitialiser Volumes"
id: bouton_reset
icon: "mdi:restart"
on_press:
then:
- lambda: |-
id(eau_froide) = 0.0;
id(eau_chaude) = 0.0;
- sensor.template.publish:
id: sensor_eau_froide
state: "0.00"
- sensor.template.publish:
id: sensor_eau_chaude
state: "0.00"
esphome: name: debitmetre_sdb friendly_name: Débitmètre Douche SDB
Une fois que vous avez flashé votre ESP et qu'il est allumé & connecté il va publier 2 valeurs sous MQTT, à savoir :
sensor_eau_froide
sensor_eau_chaude

Vous pouvez alors créer votre template dans configuration.yaml qui va additionner les valeurs de ces deux sensors :
template:
- sensor:
- name: "Consommation Eau SDB"
unique_id: consommation_eau_sdb
unit_of_measurement: "L"
device_class: water
state_class: total_increasing
state: >-
{{ (states('sensor.debitmetre_douche_sdb_volume_eau_froide') | float(0)) +
(states('sensor.debitmetre_douche_sdb_volume_eau_chaude') | float(0)) }}
template:
Affichage
Ensuite, passons à la partie affichage (dans mon cas sur mon petit cube en polycarbonate) :
esphome:
name: afficheur-eau
friendly_name: Afficheur Eau
on_boot:
priority: -100
then:
- light.turn_on:
id: led_rgb
red: 0.0
green: 0.0
blue: 1.0
- script.execute: clignote_bleu
- rtttl.play:
id: my_rtttl
rtttl: "two_short:d=4,o=5,b=100:16e6,16e6"
- delay: 10s
- lambda: |-
id(autorise_logiciel) = true;
esp32:
board: esp32-s3-devkitc-1
framework:
type: arduino
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
web_server:
port: 80
captive_portal:
logger:
api:
encryption:
key: "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
ota:
- platform: esphome
password: "zzzzzzzzzzzzzzzzzzzzzzzzzzz"
# ========================
# Afficheur TM1637
# ========================
display:
- platform: tm1637
id: tm_display
clk_pin: GPIO10
dio_pin: GPIO11
inverted: false
length: 4
lambda: |-
if (id(eau_litres).has_state()) {
int valeur = int(id(eau_litres).state);
char buf[5];
if (valeur < 10) {
snprintf(buf, sizeof(buf), " %1dL", valeur);
} else if (valeur < 100) {
snprintf(buf, sizeof(buf), " %2dL", valeur);
} else {
snprintf(buf, sizeof(buf), "%3dL", valeur);
}
it.print(buf);
} else {
it.print("---L");
}
# ========================
# LED RGB WS2812
# ========================
light:
- platform: fastled_clockless
chipset: WS2812
pin: GPIO48
num_leds: 1
rgb_order: GRB
name: "LED Statut"
id: led_rgb
restore_mode: RESTORE_DEFAULT_OFF
# ========================
# Buzzer passif avec RTTTL
# ========================
output:
- platform: ledc
pin: GPIO4
id: rtttl_out
rtttl:
output: rtttl_out
id: my_rtttl
# ========================
# Variables globales
# ========================
globals:
- id: alarme_desactivee
type: bool
restore_value: no
initial_value: 'false'
- id: autorise_logiciel
type: bool
restore_value: no
initial_value: 'false'
- id: dernier_seuil_bip
type: int
restore_value: no
initial_value: '0'
# ========================
# Script clignotement bleu
# ========================
script:
- id: clignote_bleu
mode: single
then:
- repeat:
count: 10
then:
- light.turn_on:
id: led_rgb
red: 0.0
green: 0.0
blue: 1.0
- delay: 1s
- light.turn_off: led_rgb
- delay: 500ms
- lambda: |-
// Ne rien faire ici → on laisse l’interval gérer la suite
# ========================
# Script alarme sonore
# ========================
- id: trigger_alarm
mode: single
then:
- rtttl.play:
id: my_rtttl
rtttl: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
# ========================
# Script bips dizaines
# ========================
- id: bip_1
then:
- rtttl.play:
id: my_rtttl
rtttl: "bip_1:d=4,o=5,b=100:16e6
"
- id: bip_2
then:
- rtttl.play:
id: my_rtttl
rtttl: "bip_2:d=4,o=5,b=100:16e6,16e6"
- id: bip_3
then:
- rtttl.play:
id: my_rtttl
rtttl: "bip_3:d=4,o=5,b=100:16e6,16p,16e6,16p,16e6
"
# ========================
# Bouton arrêt alarme
# ========================
binary_sensor:
- platform: gpio
pin:
number: GPIO14
mode: INPUT_PULLUP
inverted: true
name: "Bouton arrêt alarme"
on_press:
then:
- logger.log: "Alarme arrêtée manuellement"
- script.stop: trigger_alarm
- lambda: |-
id(alarme_desactivee) = true;
# ========================
# Donnée Home Assistant
# ========================
sensor:
- platform: homeassistant
id: eau_litres
entity_id: sensor.consommation_eau_sdb
internal: true
# ========================
# Logique couleur LED après init
# ========================
interval:
- interval: 1s
then:
- lambda: |-
if (!id(autorise_logiciel)) return;
float r = 0.0, g = 0.0, b = 0.0; // Déclaration en haut
if (!id(eau_litres).has_state()) {
// Couleur défaut si pas encore de données
auto call = id(led_rgb).make_call();
call.set_state(true);
call.set_rgb(0.0, 0.0, 1.0); // Bleu
call.perform();
return;
}
int litres = int(id(eau_litres).state);
// Gestion des seuils
if (litres != id(dernier_seuil_bip)) {
if (litres == 10) {
id(bip_1).execute();
id(dernier_seuil_bip) = 10;
} else if (litres == 15) {
id(bip_2).execute();
id(dernier_seuil_bip) = 15;
} else if (litres == 20) {
id(bip_3).execute();
id(dernier_seuil_bip) = 20;
}
}
id(clignote_bleu).stop();
if (litres <= 15) {
r = 0.0; g = 1.0; b = 0.0; // Vert
id(alarme_desactivee) = false;
} else if (litres <= 22) {
r = 1.0; g = 0.65; b = 0.0; // Orange
} else {
r = 1.0; g = 0.0; b = 0.0; // Rouge
if (!id(alarme_desactivee)) {
id(trigger_alarm).execute();
}
}
auto call = id(led_rgb).make_call();
call.set_state(true); // <-- Important, sinon la LED peut rester OFF
call.set_rgb(r, g, b);
call.perform();
Pour le Reset du compteur, une automatisation qui appuie sur le bouton créé précédemment, juste en fermant la porte.

Ou en YAML si vous préférez :
alias: Compteur Eau RESET
description: ""
triggers:
- type: not_opened
device_id: zzzzzzzzzzzzzzzzzzzzzzzzzzzz
entity_id: zzzzzzzzzzzzzzzzzzzzzzzzzzzzz
domain: binary_sensor
trigger: device
conditions: []
actions:
- action: button.press
metadata: {}
data: {}
target:
entity_id: button.debitmetre_douche_sdb_reinitialiser_volumes
mode: single
Conclusion
Ce projet montre qu’il est simple et amusant de suivre la consommation d’eau des douches à la maison. Avec quelques capteurs et un ESP32, on transforme des données abstraites en chiffres concrets et alertes visuelles ou sonores. Finalement, on gagne en contrôle, en conscience écologique, et on peut agir pour réduire le gaspillage. Une petite installation, mais un grand pas pour une maison plus responsable !