[TUTO] Compteur d'eau avec afficheur 7 segments

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 m3 à 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 m3 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ème étage.
J’aurai pu me connecter au compteur principal, mais ayant des difficultés, de distance et donc de porté radio & wifi (compteur à 35m à l’extérieur de la maison) et pas de point d’énergie à proximité du compteur… J’ai abandonné l’idée. Au final je me concentre sur l’essentiel : l’eau des douches.

Voici donc deux vidéos courtes qui illustrent le concept

Le matériel utilisé est le suivant.

A- Pour la mesure:










B- Pour l’affichage



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…






La partie code arrive…
Je sépare volontairement en 2 posts. :wink:

4 « J'aime »

LA PARTIE CODE

A- Pour commencer, la partie pour la mesure de l’eau à l’aide des deux YF-S201C

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"   

Un 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)) }}

B- Ensuite 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
2 « J'aime »

Top le projet.
J’etais justement en train de regarder mais hésitation entre ce type de debitmetre à effet hall ou giaonola.

Par contre j’etais plus parti sur un systeme avec les lumieres de la sdb (genre orange si volume > x puis lumiere rouge).

1 « J'aime »

Ha oui c’est une idée aussi ça. (Dans mon cas je n’ai que de l’halogène. 6 spots TBT)
Le secret dans ce projet pas cher, plutôt cheap avouons le, c’est l’étalonnage. Une fois bien calibré, comme je le dis dans la vidéo, c’est très précis.

Beau projet qui me donne des idées. A la base j’avais commencé par des pommeaux hydrao, mais ils ont rendu l’âme juste a l’expiration de la garantie…
La grande est bientôt sur le départ, mais il en reste une.
A voir si Mme acceptera pour la seconde douche…
Question complémentaire : pas trop pénible niveau 'plomberien???

Non ça va, j’ai omis de le dire, je vais le préciser il faut une paire de manchon laiton double Femelle 15/21 (1/2") à butée. Mes nourrices étant en filetage mâle comme les capteurs…
et deux joints caoutchouc pour chaque manchon. Y’a rien de sorcier. Si ce n’est la position un peu pénible oui, allongé sur le ventre dans ce petit recoin pas très accessible. :smile:

1 « J'aime »

Salut

Projet sympa, surtout avec l’afficheur.

Par contre pour les capteurs de débit, j’aurais plutôt pris la version en laiton.

Ceux que tu as pris en plastique, ne m’inspire pas confiance question solidité, surtout avec les contraintes de la plomberie, les changements de température et les coups de bélier qu’il peut y avoir sur un réseau d’eau.

J’ai pris le risque oui. Je servirai de cobaye. Si ça ne tient pas le choc je le dirai ici. :wink:

1 « J'aime »