Pour un chauffe eau à thermostat mécanique uniquement
{.is-warning}
J’ai des panneaux solaires, et parfois, ils produisent plus que ce que je consomme.
Idée : injecter le surplus dans mon eau chaude sanitaire.
Prospection et choix d’une solution
Au départ je louchais sur des projets comme l’excellentissime F1ATB. Sauf que moi j’ai déjà un système domotique plutôt élaboré qui détient déjà toutes les infos pour prendre les décisions que prend l’esp32 dans le projet F1ATB. Donc j’ai gardé la partie matos du projet (le strict minimum) et je me suis chargé du reste :
- l’esp32 ne gère que la puissance injectée au chauffe eau avec son triac, ce n’est pas le cerveau, c’est juste un accionneur. On lui fournit un poucentage de puissance et il obéit.
- HA collecte les données de consommation côté compteur (j’ai un appareil qui fait ça à peu près à la seconde), calcule ce qui doit être envoyé au chauffe eau et actionne l’esp32 en conséquence.
En plus mon compteur est dans le paninter à côté du portail et mon chauffe eau est dans les combles au dessus de la salle de bain, ni l’un ni l’autre ne sont à côté du tableau alors tout piloter en un seul point (mesure, cerveau, commande) est un peu illusoire chez moi.
Avantages :
- avoir accès à toute la richesse des automatisations HA pour piloter son chauffe eau de façon simple.
- un système domotique qui contrôle tout : le cerveau de la maison.
- intégration native de tout le matos compatible HA
Inconvénients :
- moins bonne réactivité en cas de changements rapides et fréquents d’appel de puissance, et encore, sauf si la mesure est directement reliée à HA.
- il faut avoir un home assistant
Bref, je ne dis pas que ma solution est meilleure, je dis que c’est celle qui me va le mieux dans mon contexte.
Matos
Le même qu’ICI
- un esp32
- un triac
- un radiateur/dissipateur, une boite, une alim, de la colle, un fer à souder…etc…
Je passe sur la phase d’assemblage soudage, c’est très bien documenté sur le site F1ATB. Moi je n’ai gardé que le strict minimum : esp32 + triac.
Et pour la programmation : esphome !
esphome
Un GPIO pour le signal ZC, un autre pour piloter le triac, un input number pour passer la consigne et… c’est tout.
esphome:
name: "boiler-dimmer"
friendly_name: Boiler Dimmer
esp32:
board: esp32dev
framework:
type: arduino
# version: 5.4.0 # ça c'est parce qu'une des versions récentes d'esphome buguait, maintenant ça remarche
# Enable logging
logger:
# level: INFO
# Enable Home Assistant API
api:
encryption:
key: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
ota:
- platform: esphome
password: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "XXXXXXXXXXXXXXXXXXXXXX"
password: "XXXXXXXXXXXXXXX"
use_address: 192.168.x.y # parce que mes IOT ont un réseau à part, alors je précise l'IP ici
## Si on veut le piloter directement dans une page web
#web_server:
# ota: False
# log: False
# local: True
# version: 3
captive_portal:
output:
- platform: ac_dimmer
id: triac_chauffe_eau
gate_pin: GPIO14
zero_cross_pin:
number: GPIO12
mode: INPUT_PULLUP # Crucial pour la stabilité du signal Z-C
min_power: 0%
max_power: 100%
# On utilise un "number" ou "sensor" pour piloter finement la puissance
number:
- platform: template
name: "Puissance Chauffe-Eau"
id: puissance_chauffe_eau
min_value: 0
max_value: 100
step: 1
unit_of_measurement: "%"
optimistic: true
set_action:
then:
- output.set_level:
id: triac_chauffe_eau
level: !lambda "return x / 100.0;"
# Capteur de diagnostic
sensor:
- platform: wifi_signal
name: "Signal WiFi ESP commande chauffe-eau"
update_interval: 60s
Automatisations
quelques variables
Celles qui doivent être créées mais qui sont gérées automatiquement :
- input_boolean.forcer_chauffe_eau : pour passer le chauffe en marche forcée (ou pour utiliser les heures creuses…)
- input_number.target_chauffe_eau : valeur numérique calculée à envoyer à l’esp32
Celle qu’il faut créer et renseigner :
- input_number.puissance_max_chauffe_eau : une valeur à définir selon son propre chauffe eau
mise à jour de la cible à atteindre : input_number.target_chauffe_eau
Quand la mesure de puissance change (à peu près toutes les secondes), cet automate enclenche le calcul du % de puissance à appliquer.
alias: Update target chauffe eau
description: ""
triggers:
- trigger: state
entity_id:
- sensor.my_sonoff_powct_power_a # c'est mon energy meter, chacun mettra le sien.
conditions: []
actions:
- action: script.set_target_chauffe_eau
metadata: {}
data: {}
mode: single
Et le script de calcul associé : sa logique est simple, calculer la valeur optimale pour maintenir une faible consommation du réseau, de l’ordre d’1% de la puissance totale du chauffe eau pour injecter le moins possible. Stocker cette valeur dans input_number.target_chauffe_eau
sequence:
- if:
- condition: state
entity_id: input_boolean.forcer_chauffe_eau
state:
- "on"
then:
- action: input_number.set_value
metadata: {}
target:
entity_id: input_number.target_chauffe_eau
data:
value: 100
else:
- action: input_number.set_value
metadata: {}
target:
entity_id: input_number.target_chauffe_eau
data:
value: >
{% set current = states('input_number.target_chauffe_eau') | float
%} {% set puissance_max =
states('input_number.puissance_max_chauffe_eau') | float %} {% set
puissance_compteur = states('sensor.my_sonoff_powct_power_a') |
float %} {% set un_pourcent = puissance_max / 100 %}
{% if puissance_compteur < 0 %}
{% set injection = puissance_compteur | abs %}
{% set x = injection / un_pourcent %}
{% set n = (x + 1) | int %}
{{ [current + n, 100] | min }}
{% elif puissance_compteur > un_pourcent %}
{% set reduction = (puissance_compteur / un_pourcent) | int %}
{{ [current - reduction, 0] | max }}
{% else %}
{{ current }}
{% endif %}
alias: Set Target chauffe eau
description: ""
injection de la cible calculée vers l’esp32
Quand la valeur de input_number.target_chauffe_eau change (automate précédent), cet automate actualise la consigne vers l’esp32.
alias: Set target to triac on target change
description: ""
triggers:
- trigger: state
entity_id:
- input_number.target_chauffe_eau
- trigger: homeassistant
event: start
conditions: []
actions:
- data:
value: "{{ states('input_number.target_chauffe_eau') | float }}"
action: number.set_value
target:
entity_id: number.boiler_dimmer_puissance_chauffe_eau
mode: single
Conclusion
Selon moi, c’est beaucoup plus simple et beaucoup plus modulaire. Avantage, j’ai d’autres automatisations/script (gestion heures creuses, marche forcée) qui viennent se brancher dessus et tout roucoule tranquillou
Et d’autres sont à venir, par exemple : 5 mesures de température sur toute la hauteur du chauffe eau pour évaluer la quantité d’eau chaude et prévoir une marche forcée pendant les heures creuses si nécessaire. J’aurais juste à créer un module en plus qui viendra se brancher dessus. Une architecture modulaire et évolutive sans devoir tout reprendre à chaque étape ![]()