{"id":4167,"date":"2025-12-31T10:35:15","date_gmt":"2025-12-31T09:35:15","guid":{"rendered":"https:\/\/domo.rem81.com\/?p=4167"},"modified":"2026-01-09T11:22:42","modified_gmt":"2026-01-09T10:22:42","slug":"ha-mesure-de-temperature-de-leau-v2026-stratification-estimation-du-volume-deau-chaude","status":"publish","type":"post","link":"https:\/\/domo.rem81.com\/index.php\/2025\/12\/31\/ha-mesure-de-temperature-de-leau-v2026-stratification-estimation-du-volume-deau-chaude\/","title":{"rendered":"HA-Mesure de temp\u00e9rature de l\u2019eau (V2026) : stratification + estimation du volume d\u2019eau chaude"},"content":{"rendered":"\n\n\n\n<h1 class=\"wp-block-heading\">Intro<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Dans un article pr\u00e9c\u00e9dent <a href=\"https:\/\/domo.rem81.com\/index.php\/2022\/11\/25\/ha-gestion-eau-chaude-sanitaire\/\">HA-Gestion Eau Chaude Sanitaire \u2013 Domo Rem81<\/a> je mesurais la temp\u00e9rature du ballon ECS avec une seule sonde au contact de la cuve donnant ainsi une <strong>tendance<\/strong> exploitable (et c\u2019est d\u00e9j\u00e0 tr\u00e8s bien).<br>Mais dans un ballon \u00e9lectrique, la r\u00e9alit\u00e9 est souvent une <strong>stratification<\/strong> : tr\u00e8s chaud en haut, ti\u00e8de au milieu, froid en bas. Avec une seule mesure, on peut croire que \u201cle ballon est bon\u201d alors que <strong>le volume r\u00e9ellement utilisable<\/strong> (ex : douche) ne l\u2019est pas autant.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Objectif de cette V2026 :<\/h1>\n\n\n\n<ul class=\"wp-block-list\">\n<li>mesurer 5 points de temp\u00e9rature <strong>du haut vers le bas<\/strong>,<\/li>\n\n\n\n<li>en d\u00e9duire un <strong>volume d\u2019eau <\/strong> exprim\u00e9 en litres,<\/li>\n\n\n\n<li>garder une valeur coh\u00e9rente m\u00eame si une sonde d\u00e9croche (WiFi, bus 1-Wire, etc.).<\/li>\n\n\n\n<li>G\u00e9rer la chauffe de l&rsquo;ECS non pas en fonction d&rsquo;une temp\u00e9rature mais en fonction d&rsquo;un volume d&rsquo;eau chaude disponible.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">R\u00e9alisation:<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">Implantation des sondes (recommandation pratique)<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Tr\u00e8s haut \/ Haut<\/strong> : proche de la sortie ECS et du d\u00f4me sup\u00e9rieur (zone la plus chaude).<\/li>\n\n\n\n<li><strong>Milieu<\/strong> : mi-hauteur du ballon.<\/li>\n\n\n\n<li><strong>Bas \/ Tr\u00e8s bas<\/strong> : zone proche de la r\u00e9sistance (souvent la plus \u201cfroide\u201d en d\u00e9but de chauffe PV partielle).<\/li>\n\n\n\n<li>Les DS18B20 peuvent \u00eatre :\n<ul class=\"wp-block-list\">\n<li>soit gliss\u00e9es dans l\u2019isolant au contact de la cuve (comme expliqu\u00e9 dans la V1),<\/li>\n\n\n\n<li>soit positionn\u00e9es dans des fourreaux si le ballon en dispose.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Bus 1-Wire : c\u00e2blage classique (3 fils), sondes en parall\u00e8le avec r\u00e9sistance de rappel de 4.7 k ohm entre le \u00ab\u00a0signal\u00a0\u00bb et le \u00ab\u00a0+VCC\u00a0\u00bb.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Principe de calcul du \u201cvolume d\u2019eau chaude utile\u201d<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">On d\u00e9coupe virtuellement le ballon (200 L) en <strong>4 segments verticaux<\/strong> d\u00e9finis par 5 sondes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pour chaque segment :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>si les 2 temp\u00e9ratures (haut\/bas du segment) sont au-dessus du seuil \u2192 segment \u201c100% chaud\u201d<\/li>\n\n\n\n<li>si les 2 sont sous le seuil \u2192 segment \u201c0% chaud\u201d<\/li>\n\n\n\n<li>si on est en transition \u2192 on estime la fraction chaude du segment<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Dans le code,  j&rsquo;ai ajout\u00e9 deux s\u00e9curit\u00e9s importantes :<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Correction monotone<\/strong> : on force <code>T[0] &gt;= T[1] &gt;= T[2] &gt;= T[3] &gt;= T[4]<\/code> (on \u00e9vite les inversions aberrantes dues \u00e0 une sonde qui \u201cd\u00e9rive\u201d ou un point de mesure mal plaqu\u00e9).<\/li>\n\n\n\n<li><strong>Valeur de repli<\/strong> : si une sonde est indisponible (<code>NaN<\/code>) \u2192 on conserve le <strong>dernier volume valide<\/strong> (global restaur\u00e9 au reboot).<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Code ESPHome (ESP32 + 6 DS18B20 + volume calcul\u00e9)<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Remarque : j&rsquo;ai  d\u00e9clar\u00e9 6 sondes de T\u00b0 (5 pour le ballon + 1 \u201csortie r\u00e9gulateur\u201d). Le volume calcul\u00e9 utilise <strong>les 5 sondes ballon<\/strong>, la \u00ab\u00a0temp\u00e9rature sortie r\u00e9gulateur\u00a0\u00bb me sert uniquement de contr\u00f4le et d&rsquo;alarme en cas de d\u00e9faillance du r\u00e9gulateur thermostatique situ\u00e9 en sortie de l&rsquo;ECS.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Vous trouverez la derni\u00e8re version du code ici: <a href=\"https:\/\/github.com\/remycrochon\/home-assistant\/blob\/master\/esphome\/esp139-ecs.yaml\" target=\"_blank\" rel=\"noreferrer noopener\">home-assistant\/esphome\/esp139-ecs.yaml at master \u00b7 remycrochon\/home-assistant<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>substitutions:\n  device_name: esp139-ecs\n  adress_ip: \"192.168.0.139\"\n\nesphome:\n  name: ${device_name}\n\nesp32:\n  board: esp32dev\n  framework:\n    type: arduino\n\nwifi:\n  networks:\n    - ssid: !secret wifi\n      password: !secret mdpwifi\n  reboot_timeout: 5min\n  min_auth_mode: WPA2  \n  manual_ip:\n    static_ip: ${adress_ip}\n    gateway: 192.168.0.254\n    subnet: 255.255.255.0\n    dns1: !secret dns1\n    dns2: !secret dns2\n\n# Utilisez la LED de l'appareil comme LED d'\u00e9tat, qui clignotera s'il y a des avertissements (lent) ou des erreurs (rapide)\nstatus_led:\n  pin:\n    number: GPIO23\n    inverted: true\n\nlogger:\n  level: DEBUG\n\napi:\n  \nota:\n  platform: esphome\n\nweb_server:\n  port: 80\n\none_wire:\n  - platform: gpio  \n    pin: GPIO32\n\nglobals:\n  - id: last_volume_chaud\n    type: float\n    restore_value: yes\n    initial_value: '0'\n\nsensor:\n  - platform: dallas_temp\n    address: 0x3649465609646128\n    name: \"Temp Tr\u00e9s Haut\"\n    id: temp_th\n    update_interval: 10s\n    device_class: temperature\n    accuracy_decimals: 2\n\n  - platform: dallas_temp\n    address: 0xc0ae154809646128\n    name: \"Temp Haut\"\n    id: temp_h\n    update_interval: 10s\n    device_class: temperature\n    accuracy_decimals: 2\n\n  - platform: dallas_temp\n    address: 0xab48885709646128\n    name: \"temp_ecs\"\n    id: temp_m\n    update_interval: 10s\n    device_class: temperature\n    accuracy_decimals: 2\n    filters: \n      - offset: 2\n\n  - platform: dallas_temp\n    address: 0x82012111efe81d28\n    name: \"Temp Bas\"\n    id: temp_b\n    update_interval: 10s\n    device_class: temperature\n    accuracy_decimals: 2\n    \n  - platform: dallas_temp\n    address: 0x61b6945709646128\n    name: \"Temp Tr\u00e9s Bas\"\n    id: temp_tb\n    update_interval: 10s\n    device_class: temperature\n    accuracy_decimals: 2\n\n  - platform: dallas_temp\n    address: 0x94000000855c4e28\n    name: \"Temp Sortie R\u00e9gulateur\"\n    id: temp_s_regule\n    update_interval: 10s\n    device_class: temperature\n    accuracy_decimals: 2\n\n  - platform: template\n    name: \"Ballon volume eau chaude\"\n    id: ballon_volume_chaud\n    unit_of_measurement: \"L\"\n    icon: \"mdi:water-thermometer\"\n    accuracy_decimals: 0\n    update_interval: 10s\n    lambda: |-\n      \/\/ Param\u00e8tres physiques\n      const float V_TOTAL = 200.0f;   \/\/ volume total du ballon en litres\n      const float T_SEUIL = 40.0f;    \/\/ temp\u00e9rature mini eau \"utile\"\n      const float K = 4.0f;           \/\/ raideur de la transition hyperbolique (2 \u00e0 6)\n\n      \/\/ R\u00e9cup\u00e9ration des 5 sondes (du haut vers le bas)\n      float T&#091;5] = {\n        id(temp_th).state,  \/\/ Tr\u00e8s haut\n        id(temp_h).state,   \/\/ Haut\n        id(temp_m).state,   \/\/ Milieu\n        id(temp_b).state,   \/\/ Bas (r\u00e9sistance)\n        id(temp_tb).state   \/\/ Tr\u00e8s bas\n      };\n\n      bool all_ok = true;\n      for (int i = 0; i &lt; 5; i++) {\n        if (isnan(T&#091;i])) {\n          all_ok = false;\n        }\n      }\n\n      \/\/ Si une sonde est indispo \u2192 on garde le dernier volume valide\n      if (!all_ok) {\n        ESP_LOGW(\"ecs\", \"Lecture DS18B20 manquante \u2192 on garde last_volume_chaud = %.0f L\", id(last_volume_chaud));\n        return id(last_volume_chaud);\n      }\n\n      \/\/ \ud83d\udd27 Correction monotone :\n      \/\/ on impose que T&#091;0] &gt;= T&#091;1] &gt;= T&#091;2] &gt;= T&#091;3] &gt;= T&#091;4]\n      for (int i = 1; i &lt; 5; i++) {\n        if (T&#091;i] &gt; T&#091;i-1]) {\n          T&#091;i] = T&#091;i-1];\n        }\n      }\n\n      const int   SEGMENTS = 4;                  \/\/ 5 sondes \u2192 4 segments verticaux\n      const float V_SEG    = V_TOTAL \/ SEGMENTS; \/\/ volume par segment\n      float volume = 0.0f;\n\n      for (int i = 0; i &lt; SEGMENTS; i++) {\n        float Th = T&#091;i];       \/\/ temp\u00e9rature en haut du segment\n        float Tb = T&#091;i+1];     \/\/ temp\u00e9rature en bas du segment\n        float f  = 0.0f;       \/\/ fraction d'eau chaude dans le segment (0..1)\n\n        if (Th &gt;= T_SEUIL &amp;&amp; Tb &gt;= T_SEUIL) {\n          \/\/ Segment enti\u00e8rement au-dessus du seuil\n          f = 1.0f;\n        } else if (Th &lt; T_SEUIL &amp;&amp; Tb &lt; T_SEUIL) {\n          \/\/ Segment enti\u00e8rement en dessous du seuil\n          f = 0.0f;\n        } else {\n          \/\/ Zone de transition : on interpole + correction hyperbolique\n          float denom = Th - Tb;\n\n          if (fabs(denom) &lt; 0.01f) {\n            \/\/ Gradient quasi nul \u2192 on consid\u00e8re homog\u00e8ne\n            f = (Th &gt;= T_SEUIL) ? 1.0f : 0.0f;\n          } else {\n            \/\/ interpolation lin\u00e9aire de la position de T_SEUIL entre Th et Tb\n            float f_lin = (Th - T_SEUIL) \/ denom;\n            if (f_lin &lt; 0.0f) f_lin = 0.0f;\n            if (f_lin &gt; 1.0f) f_lin = 1.0f;\n\n            \/\/ Application d'une sigmo\u00efde hyperbolique autour de 0.5\n            float x   = f_lin - 0.5f;      \/\/ recentrage en 0\n            float num = tanh(K * x);\n            float den = tanh(K * 0.5f);    \/\/ normalisation pour garder &#091;0,1]\n            float f_hyp = 0.5f * (num \/ den + 1.0f);\n\n            f = f_hyp;\n          }\n        }\n\n        \/\/ S\u00e9curit\u00e9 bornes\n        if (f &lt; 0.0f) f = 0.0f;\n        if (f &gt; 1.0f) f = 1.0f;\n\n        volume += f * V_SEG;\n      }\n\n      \/\/ On m\u00e9morise le dernier volume valide\n      id(last_volume_chaud) = volume;\n      return volume;\n\nbinary_sensor:\n  - platform: status\n    name: \"Status\"\n\nswitch:   \n  - platform: gpio\n    name: \"Relais\"\n    pin: GPIO16\n    id: relais\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Exploitation dans les automatismes<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Id\u00e9e simple:<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Dans mon article, les automatismes V1 se basaient sur <strong>un seuil de temp\u00e9rature<\/strong> (ex : &lt; 40\u00b0C \u2192 chauffe nuit en HC, &gt; 45\u00b0C \u2192 stop).<br>Avec le <strong>volume<\/strong>, tu peux piloter plus finement, par exemple :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Nuit (HC) : si <code>Ballon volume eau chaude<\/code> &lt; <strong>80 L<\/strong> \u2192 chauffe r\u00e9seau<\/li>\n\n\n\n<li>Arr\u00eat : si <code>Ballon volume eau chaude<\/code> &gt; <strong>140 L<\/strong> \u2192 stop<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Exemple (logique identique \u00e0 ton \u201ccahier des charges\u201d, mais crit\u00e8re \u201cL\u201d au lieu de \u201c\u00b0C\u201d) :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>trigger:\n  - platform: numeric_state\n    entity_id: sensor.ballon_volume_eau_chaude\n    below: 80\n    id: v_bas\n  - platform: numeric_state\n    entity_id: sensor.ballon_volume_eau_chaude\n    above: 140\n    id: v_haut\n  # + tes triggers HC\/HP + soleil + mode Auto, etc.\n\n# Puis choose: comme dans ton automatisme, en rempla\u00e7ant les conditions de temp\u00e9rature.\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Id\u00e9e un peu plus complexe:<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">J&rsquo;exploite le volume d&rsquo;ECS dans ma nouvelle version de routeur ESPHOME 2026, voir cet article: <a href=\"https:\/\/domo.rem81.com\/index.php\/2026\/01\/09\/ha-routeur-solaire-photovoltaique-avec-esphome-v2026-hp-hc-calibration\/\">HA-Routeur Solaire Photovolta\u00efque avec ESPHome \u2014 V2026 (HP\/HC + calibration) \u2013 Domo Rem81<\/a><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Tableau de bord:<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Exemple de carte:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"401\" height=\"634\" src=\"https:\/\/domo.rem81.com\/wp-content\/uploads\/2026\/01\/image.png\" alt=\"\" class=\"wp-image-4186\" srcset=\"https:\/\/domo.rem81.com\/wp-content\/uploads\/2026\/01\/image.png 401w, https:\/\/domo.rem81.com\/wp-content\/uploads\/2026\/01\/image-190x300.png 190w\" sizes=\"auto, (max-width: 401px) 100vw, 401px\" \/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>type: picture-elements\nelements:\n  - entity: sensor.esp139_ecs_temp_sortie_regulateur\n    prefix: \"\"\n    style:\n      background: null\n      color: black\n      font-size: 150%\n      left: 60%\n      top: 3%\n      transform: none\n    type: state-label\n  - entity: sensor.esp139_ecs_temp_tres_haut\n    prefix: &lt;--\n    style:\n      background: null\n      color: black\n      font-size: 150%\n      left: 65%\n      top: 25%\n      transform: none\n    type: state-label\n  - entity: sensor.esp139_ecs_temp_haut\n    prefix: &lt;--\n    style:\n      background: null\n      color: black\n      font-size: 150%\n      left: 65%\n      top: 35%\n      transform: none\n    type: state-label\n  - entity: sensor.esp139_ecs_temp_ecs\n    prefix: &lt;--\n    style:\n      background: null\n      color: black\n      font-size: 150%\n      left: 65%\n      top: 45%\n      transform: none\n    type: state-label\n  - entity: sensor.esp139_ecs_temp_bas\n    prefix: &lt;--\n    style:\n      background: null\n      color: black\n      font-size: 150%\n      left: 65%\n      top: 55%\n      transform: none\n    type: state-label\n  - entity: sensor.esp139_ecs_temp_tres_bas\n    prefix: &lt;--\n    style:\n      background: null\n      color: black\n      font-size: 150%\n      left: 65%\n      top: 65%\n      transform: none\n    type: state-label\n  - entity: sensor.esp139_ecs_ballon_volume_eau_chaude\n    prefix: \"\"\n    style:\n      background: null\n      color: black\n      font-size: 190%\n      left: 30%\n      top: 50%\n      transform: none\n    type: state-label\n  - entity: sensor.esp176_esp32_routeur_1r_p_ecs_jsymk\n    prefix: \"\"\n    style:\n      background: null\n      color: black\n      font-size: 150%\n      left: 76%\n      top: 75%\n      transform: none\n    type: state-label\nimage: \/local\/images\/ecs.png\ngrid_options:\n  columns: 12\n  rows: 10\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Le fichier image est t\u00e9l\u00e9chargeable ici: <a href=\"https:\/\/github.com\/remycrochon\/domo.rem81\/blob\/main\/ecs.png\" target=\"_blank\" rel=\"noreferrer noopener\">domo.rem81\/ecs.png at main \u00b7 remycrochon\/domo.rem81<\/a><\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Notes de r\u00e9glage:<\/h1>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>V_TOTAL<\/code> : adapte \u00e0 ton ballon (200 L ici).<\/li>\n\n\n\n<li><code>T_SEUIL<\/code> : 40\u00b0C est un bon standard \u201ceau utile\u201d, mais tu peux mettre 42\u201345\u00b0C si tu veux \u00eatre plus conservateur.<\/li>\n\n\n\n<li><code>K<\/code> (tanh) : plus K est grand, plus la transition est \u201cbrutale\u201d. 3\u20135 est g\u00e9n\u00e9ralement raisonnable.<\/li>\n\n\n\n<li>Offset sur <code>temp_ecs<\/code> : tu as mis <code>offset: 2<\/code> sur la sonde milieu ; conserve-le si tu as \u00e9talonn\u00e9 \u201csur robinet\u201d, comme expliqu\u00e9 dans la V1.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Intro Dans un article pr\u00e9c\u00e9dent HA-Gestion Eau Chaude Sanitaire \u2013 Domo Rem81 je mesurais la temp\u00e9rature du ballon ECS avec une seule sonde au contact de la cuve donnant ainsi &hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[17],"class_list":["post-4167","post","type-post","status-publish","format-standard","hentry","category-homeassistant","tag-esphome"],"_links":{"self":[{"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/posts\/4167","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/comments?post=4167"}],"version-history":[{"count":10,"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/posts\/4167\/revisions"}],"predecessor-version":[{"id":4198,"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/posts\/4167\/revisions\/4198"}],"wp:attachment":[{"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/media?parent=4167"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/categories?post=4167"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/tags?post=4167"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}