j’ai suivi ton conseille et utilisé Chat GPT, et je pense que je suis arrivé a un truc bien, il manqueras plus que le bypass.
Si tu veux je te posterai la config esp et celle de HA
j’ai suivi ton conseille et utilisé Chat GPT, et je pense que je suis arrivé a un truc bien, il manqueras plus que le bypass.
Si tu veux je te posterai la config esp et celle de HA
Bonjour
Super que tu y sois arrivé, oui pour.poster ta solution ![]()
Alors voici, le bypass n’est pas encore géré, car il est soit grillé soit grippé chez moi, donc pour le moment je l’ai exclu.
Voici un petit resumé des fonctions faites par l’IA :
Ce code pilote une VMC double flux Helios avec un ESP32, intégrée à Home Assistant.
Il gère :
la vitesse des deux ventilateurs (extraction et insufflation),
la connection par cable Ethernet
la sécurité (watchdog),
les températures, débits, RPM,
le suivi du filtre,
les temps de fonctionnement,
l’alimentation électrique,
les relais et le bypass,
les notifications Home Assistant.
J’ai rajouté un ADS1115 pour pouvoir mesurer les PWM des ventilateurs.
esphome:
name: vmc-double-flux-helios
friendly_name: VMC Double Flux Helios
# --------------------------------------------------
# 🔹 DÉMARRAGE SÉCURISÉ
# Initialisation des PWM et relais au boot
# --------------------------------------------------
on_boot:
priority: 600
then:
- globals.set:
id: pwm_extraction_mem
value: "0.25"
- globals.set:
id: pwm_insertion_mem
value: "0.25"
- output.set_level:
id: pwm_extraction
level: 0.25
- output.set_level:
id: pwm_insertion
level: 0.25
- switch.turn_off: relais1
- switch.turn_off: relais2
# --------------------------------------------------
# 🔹 HEURE SYNCHRONISÉE DEPUIS HOME ASSISTANT
# Pour les calculs de filtre, bypass et watchdog
# --------------------------------------------------
time:
- platform: homeassistant
id: ha_time
# --------------------------------------------------
# 🔹 CONFIGURATION ESP32 ET LOGGING
# --------------------------------------------------
esp32:
board: esp32dev
framework:
type: arduino
logger:
level: DEBUG
api:
encryption:
key: "G0+Rc2vukJs1vbwjWauQeknF/9y+hYYJoBosPZKkgrI="
ota:
- platform: esphome
password: "355962038079304261cb35e074746477"
# --------------------------------------------------
# 🔹 ADS1115 - Mesure tensions
# --------------------------------------------------
i2c:
- id: i2c_ads
sda: GPIO02
scl: GPIO04
scan: true
ads1115:
- id: 'ads1115_0'
address: 0x48
continuous_mode: true
# --------------------------------------------------
# 🔹 ETHERNET W5500
# --------------------------------------------------
ethernet:
type: w5500
clk_pin: GPIO18
mosi_pin: GPIO23
miso_pin: GPIO19
cs_pin: GPIO27
interrupt_pin: GPIO35
# --------------------------------------------------
# 🔹 BUS 1-WIRE POUR CAPTEURS DS18B20
# --------------------------------------------------
one_wire:
- platform: gpio
pin: GPIO14
# --------------------------------------------------
# 🔹 VARIABLES GLOBALES
# Persistent variables, watchdog, bypass, temps de fonctionnement, historique filtre
# --------------------------------------------------
globals:
# PWM mémorisés et précédents
- id: pwm_extraction_mem
type: float
restore_value: true
initial_value: "0.25"
- id: pwm_insertion_mem
type: float
restore_value: true
initial_value: "0.25"
- id: last_pwm_extraction
type: float
restore_value: true
initial_value: "0.25"
- id: last_pwm_insertion
type: float
restore_value: true
initial_value: "0.25"
# Watchdog
- id: watchdog_active
type: bool
restore_value: true
initial_value: "false"
- id: watchdog_counter_extraction
type: int
restore_value: true
initial_value: "0"
- id: watchdog_counter_insertion
type: int
restore_value: true
initial_value: "0"
- id: watchdog_triggered_count
type: int
restore_value: true
initial_value: "0"
# Historique Watchdog
- id: watchdog_history_1
type: long
restore_value: true
initial_value: "0"
- id: watchdog_history_2
type: long
restore_value: true
initial_value: "0"
- id: watchdog_history_3
type: long
restore_value: true
initial_value: "0"
- id: watchdog_history_4
type: long
restore_value: true
initial_value: "0"
- id: watchdog_history_5
type: long
restore_value: true
initial_value: "0"
- id: watchdog_history_6
type: long
restore_value: true
initial_value: "0"
- id: watchdog_history_7
type: long
restore_value: true
initial_value: "0"
- id: watchdog_history_8
type: long
restore_value: true
initial_value: "0"
- id: watchdog_history_9
type: long
restore_value: true
initial_value: "0"
- id: watchdog_history_10
type: long
restore_value: true
initial_value: "0"
# Filtre VMC
- id: filtre_last_changed
type: int
restore_value: true
initial_value: "0"
# Temps de fonctionnement ventilateurs
- id: uptime_extraction
type: float
restore_value: true
initial_value: "0.0"
- id: uptime_insertion
type: float
restore_value: true
initial_value: "0.0"
# --------------------------------------------------
# 🔹 SORTIES PWM
# --------------------------------------------------
output:
- platform: ledc
id: pwm_extraction
pin: 16
frequency: 10000 Hz
channel: 0
zero_means_zero: true
min_power: 0
max_power: 1
- platform: ledc
id: pwm_insertion
pin: 17
frequency: 10000 Hz
channel: 5
zero_means_zero: true
min_power: 0
max_power: 1
# --------------------------------------------------
# 🔹 SLIDERS PWM
# Pour commande manuelle via Home Assistant
# --------------------------------------------------
number:
- platform: template
name: "PWM Extraction"
id: pwm_extraction_slider
min_value: 0
max_value: 100
step: 1
unit_of_measurement: "%"
mode: slider
initial_value: "25"
restore_value: true
optimistic: true
set_action:
- if:
condition:
lambda: 'return id(watchdog_active);'
then:
- number.set:
id: pwm_extraction_slider
value: 0
else:
- globals.set:
id: pwm_extraction_mem
value: !lambda "return x / 100.0;"
- globals.set:
id: last_pwm_extraction
value: !lambda "return x / 100.0;"
- output.set_level:
id: pwm_extraction
level: !lambda "return id(pwm_extraction_mem);"
- component.update: pwm_extraction_actual
- platform: template
name: "PWM Insertion"
id: pwm_insertion_slider
min_value: 0
max_value: 100
step: 1
unit_of_measurement: "%"
mode: slider
initial_value: "25"
restore_value: true
optimistic: true
set_action:
- if:
condition:
lambda: 'return id(watchdog_active);'
then:
- number.set:
id: pwm_insertion_slider
value: 0
else:
- globals.set:
id: pwm_insertion_mem
value: !lambda "return x / 100.0;"
- globals.set:
id: last_pwm_insertion
value: !lambda "return x / 100.0;"
- output.set_level:
id: pwm_insertion
level: !lambda "return id(pwm_insertion_mem);"
- component.update: pwm_insertion_actual
# --------------------------------------------------
# 🔹 CAPTEURS
# Températures, débits, RPM, heures de fonctionnement, filtre, tension ADC
# --------------------------------------------------
sensor:
# PWM réels
- platform: template
name: "PWM Extraction Réel"
id: pwm_extraction_actual
unit_of_measurement: "%"
update_interval: 2s
lambda: |-
return id(pwm_extraction_mem) * 100.0;
- platform: template
name: "PWM Insertion Réel"
id: pwm_insertion_actual
unit_of_measurement: "%"
update_interval: 2s
lambda: |-
return id(pwm_insertion_mem) * 100.0;
# Températures VMC (DS18B20)
- platform: dallas_temp
address: 0x250000005174e128
name: "Température Air Rejetée"
id: vmc_temp_air_rejetee
update_interval: 10s
- platform: dallas_temp
address: 0x7a00000054422228
name: "Température Air Insufflée"
id: vmc_temp_air_insuflee
update_interval: 10s
- platform: dallas_temp
address: 0xe2000000509b4928
name: "Température Air Extérieur"
id: vmc_temp_air_exterieur
update_interval: 10s
- platform: dallas_temp
address: 0xdf00000050780d28
name: "Température Air Repris"
id: vmc_temp_air_repris
update_interval: 10s
# Débits ventilateurs (calculé depuis PWM)
- platform: template
name: "Débit Ventilateur Extraction"
id: vmc_debit_ventilation_extraction
unit_of_measurement: "m3/h"
icon: "mdi:fan"
update_interval: 2s
lambda: |-
return id(pwm_extraction_mem) * 315.0;
- platform: template
name: "Débit Ventilateur Insertion"
id: vmc_debit_ventilation_insertion
unit_of_measurement: "m3/h"
icon: "mdi:fan-chevron-down"
update_interval: 2s
lambda: |-
return id(pwm_insertion_mem) * 315.0;
# RPM avec watchdog
- platform: pulse_counter
pin:
number: 21
mode:
input: true
pullup: true
name: "RPM Ventilateur Extraction"
id: fan_speed_extraction
unit_of_measurement: "RPM"
update_interval: 2s
filters:
- multiply: 1
on_value:
then:
- lambda: |-
if (id(watchdog_active)) return;
float pwm = id(pwm_extraction_mem) * 100.0;
float rpm = id(fan_speed_extraction).state;
if (pwm >= 20.0 && rpm < 200.0) id(watchdog_counter_extraction)++;
else id(watchdog_counter_extraction) = 0;
if (id(watchdog_counter_extraction) >= 1) {
id(watchdog_active) = true;
id(pwm_extraction_mem) = 0.0;
id(pwm_insertion_mem) = 0.0;
id(pwm_extraction).set_level(0.0);
id(pwm_insertion).set_level(0.0);
id(pwm_extraction_slider).publish_state(0);
id(pwm_insertion_slider).publish_state(0);
}
- platform: pulse_counter
pin:
number: 22
mode:
input: true
pullup: true
name: "RPM Ventilateur Insertion"
id: fan_speed_insertion
unit_of_measurement: "RPM"
update_interval: 2s
filters:
- multiply: 1
on_value:
then:
- lambda: |-
if (id(watchdog_active)) return;
float pwm = id(pwm_insertion_mem) * 100.0;
float rpm = id(fan_speed_insertion).state;
if (pwm >= 20.0 && rpm < 200.0) id(watchdog_counter_insertion)++;
else id(watchdog_counter_insertion) = 0;
if (id(watchdog_counter_insertion) >= 1) {
id(watchdog_active) = true;
id(pwm_extraction_mem) = 0.0;
id(pwm_insertion_mem) = 0.0;
id(pwm_extraction).set_level(0.0);
id(pwm_insertion).set_level(0.0);
id(pwm_extraction_slider).publish_state(0);
id(pwm_insertion_slider).publish_state(0);
}
# Heures de fonctionnement
- platform: template
name: "Heures Ventilateur Extraction"
unit_of_measurement: "h"
update_interval: 30s
lambda: 'return id(uptime_extraction);'
- platform: template
name: "Heures Ventilateur Insertion"
unit_of_measurement: "h"
update_interval: 30s
lambda: 'return id(uptime_insertion);'
# --------------------------------------------------
# 🔹 ADS1115 - Mesure tensions via pont diviseur + Bruit
# --------------------------------------------------
# Mesures principales
- platform: ads1115
ads1115_id: ads1115_0
multiplexer: 'A0_GND'
gain: 2.048 # ±2.048V adapté pour ton pont diviseur
name: "Alim PWM"
id: voltage1
update_interval: 1s
filters:
- multiply: 16.00 # Facteur pour pont diviseur 220k/15k
- median:
window_size: 10
- sliding_window_moving_average:
window_size: 10
send_every: 1
- platform: ads1115
ads1115_id: ads1115_0
multiplexer: 'A1_GND'
gain: 2.048
name: "Alim PWM Extraction"
id: voltage2
update_interval: 1s
filters:
- multiply: 16.1
- median:
window_size: 40
- sliding_window_moving_average:
window_size: 20
send_every: 1
- platform: ads1115
ads1115_id: ads1115_0
multiplexer: 'A2_GND'
gain: 2.048
name: "Alim PWM Insertion"
id: voltage3
update_interval: 1s
filters:
- multiply: 16.1
- median:
window_size: 40
- sliding_window_moving_average:
window_size: 20
send_every: 1
# --------------------------------------------------
# 🔹 Filtre VMC
# Jours restants et statut
# --------------------------------------------------
- platform: template
name: "Filtre VMC - Jours restants"
id: filtre_vmc_days
unit_of_measurement: "jours"
icon: mdi:filter
update_interval: 30s
lambda: |-
if (id(filtre_last_changed) == 0) return -1;
int delta = (id(ha_time).now().timestamp - id(filtre_last_changed)) / 86400;
int jours = 180 - delta;
if (jours < 0) jours = 0;
return jours;
- platform: template
name: "Statut Filtre VMC"
id: filtre_vmc_status
update_interval: 30s
lambda: |-
if (id(filtre_last_changed) == 0) return 0;
int delta = (id(ha_time).now().timestamp - id(filtre_last_changed)) / 86400;
int jours = 180 - delta;
if (jours <= 0) return 2;
if (jours <= 30) return 1;
return 3;
# --------------------------------------------------
# 🔹 INTERVALS
# Gestion du redémarrage progressif, comptage uptime et ouverture automatique du bypass
# --------------------------------------------------
interval:
# 🔹 Redémarrage progressif PWM après reset watchdog
- interval: 500ms
then:
- if:
condition:
lambda: 'return !id(watchdog_active) && id(pwm_extraction_mem) < id(last_pwm_extraction);'
then:
- lambda: |-
id(pwm_extraction_mem) += 0.01;
if (id(pwm_extraction_mem) > id(last_pwm_extraction)) id(pwm_extraction_mem) = id(last_pwm_extraction);
id(pwm_extraction).set_level(id(pwm_extraction_mem));
id(pwm_extraction_slider).publish_state(id(pwm_extraction_mem) * 100.0);
- if:
condition:
lambda: 'return !id(watchdog_active) && id(pwm_insertion_mem) < id(last_pwm_insertion);'
then:
- lambda: |-
id(pwm_insertion_mem) += 0.01;
if (id(pwm_insertion_mem) > id(last_pwm_insertion)) id(pwm_insertion_mem) = id(last_pwm_insertion);
id(pwm_insertion).set_level(id(pwm_insertion_mem));
id(pwm_insertion_slider).publish_state(id(pwm_insertion_mem) * 100.0);
# 🔹 Incrément temps de fonctionnement des ventilateurs
- interval: 60s
then:
- lambda: |-
if (id(pwm_extraction_mem) > 0.0) id(uptime_extraction) += 1.0/60.0;
if (id(pwm_insertion_mem) > 0.0) id(uptime_insertion) += 1.0/60.0;
# --------------------------------------------------
# 🔹 TEXT SENSORS
# Informations textuelles pour Home Assistant
# --------------------------------------------------
text_sensor:
# Date du dernier changement de filtre
- platform: template
name: "Date dernier changement filtre"
id: filtre_date
lambda: |-
if (id(filtre_last_changed) == 0) return {"Jamais"};
time_t t = id(filtre_last_changed);
char buf[20];
strftime(buf, sizeof(buf), "%d/%m/%Y", localtime(&t));
return {buf};
# Watchdog déclenché (date/heure)
- platform: template
name: "Watchdog Déclenché - Date/Heure"
id: watchdog_triggered_time
lambda: |-
if (!id(watchdog_active)) return {"Non déclenché"};
time_t t = id(ha_time).now().timestamp;
char buf[20];
strftime(buf, sizeof(buf), "%d/%m/%Y %H:%M:%S", localtime(&t));
return {buf};
# Historique Watchdog
- platform: template
name: "Historique Watchdog"
id: watchdog_history_text
lambda: |-
std::string result = "";
long hist[10] = {
id(watchdog_history_1), id(watchdog_history_2), id(watchdog_history_3),
id(watchdog_history_4), id(watchdog_history_5), id(watchdog_history_6),
id(watchdog_history_7), id(watchdog_history_8), id(watchdog_history_9),
id(watchdog_history_10)
};
for(int i=0; i<10; i++){
if(hist[i] == 0) continue;
time_t tt = (time_t) hist[i];
char buf[20];
strftime(buf, sizeof(buf), "%d/%m/%Y %H:%M:%S", localtime(&tt));
result += buf;
result += "\n";
}
if(result == "") return {"Aucun"};
return {result.c_str()};
# --------------------------------------------------
# 🔹 BOUTONS
# Commandes manuelles pour watchdog, filtre, ventilateurs et bypass
# --------------------------------------------------
button:
- platform: template
name: "Reset Watchdog VMC"
icon: mdi:restart-alert
on_press:
- globals.set:
id: watchdog_active
value: "false"
- globals.set:
id: watchdog_counter_extraction
value: "0"
- globals.set:
id: watchdog_counter_insertion
value: "0"
- platform: template
name: "Réinitialiser filtre VMC"
icon: mdi:filter-plus
on_press:
- globals.set:
id: filtre_last_changed
value: !lambda "return id(ha_time).now().timestamp;"
- component.update: filtre_vmc_days
- component.update: filtre_vmc_status
- component.update: filtre_date
- platform: template
name: "Reset Compteurs Ventilateurs"
icon: mdi:restart
on_press:
- globals.set:
id: uptime_extraction
value: "0.0"
- globals.set:
id: uptime_insertion
value: "0.0"
- platform: template
name: "Reset Historique Watchdog"
icon: mdi:shield-refresh
on_press:
- lambda: |-
id(watchdog_history_1) = 0;
id(watchdog_history_2) = 0;
id(watchdog_history_3) = 0;
id(watchdog_history_4) = 0;
id(watchdog_history_5) = 0;
id(watchdog_history_6) = 0;
id(watchdog_history_7) = 0;
id(watchdog_history_8) = 0;
id(watchdog_history_9) = 0;
id(watchdog_history_10) = 0;
# --------------------------------------------------
# 🔹 NOTIFICATIONS HOME ASSISTANT
# Watchdog et filtre
# --------------------------------------------------
binary_sensor:
- platform: template
name: "Watchdog Déclenché"
id: ha_watchdog_notify
lambda: 'return id(watchdog_active);'
device_class: problem
on_press:
then:
- lambda: |-
// Shift historique watchdog
id(watchdog_history_10) = id(watchdog_history_9);
id(watchdog_history_9) = id(watchdog_history_8);
id(watchdog_history_8) = id(watchdog_history_7);
id(watchdog_history_7) = id(watchdog_history_6);
id(watchdog_history_6) = id(watchdog_history_5);
id(watchdog_history_5) = id(watchdog_history_4);
id(watchdog_history_4) = id(watchdog_history_3);
id(watchdog_history_3) = id(watchdog_history_2);
id(watchdog_history_2) = id(watchdog_history_1);
id(watchdog_history_1) = id(ha_time).now().timestamp;
- platform: template
name: "Watchdog Déclenché - Notification"
id: ha_watchdog_notify_auto
lambda: 'return id(watchdog_active);'
device_class: problem
on_press:
then:
- homeassistant.service:
service: notify.notify
data:
title: "VMC Double Flux"
message: "Watchdog VMC déclenché ! Vérifiez les ventilateurs."
- platform: template
name: "Filtre VMC - Attention"
id: filtre_vmc_warn
lambda: |-
if (id(filtre_last_changed) == 0) return false;
int delta = (id(ha_time).now().timestamp - id(filtre_last_changed)) / 86400;
int jours = 180 - delta;
return jours <= 30;
device_class: problem
on_press:
then:
- homeassistant.service:
service: notify.notify
data:
title: "VMC Double Flux"
message: "Le filtre VMC a moins de 30 jours avant remplacement."
# --------------------------------------------------
# 🔹 RELAIS ET BYPASS
# --------------------------------------------------
switch:
# Relais physiques
- platform: gpio
id: relais1_gpio
pin: 32
internal: true
- platform: gpio
id: relais2_gpio
pin: 33
internal: true
- platform: gpio
id: relais3
pin: 25
name: "Relais 3"
- platform: gpio
id: relais4
pin: 26
name: "Relais 4"
# Relais sécurisés (évite de fermer les deux relais du bypass en même temps)
- platform: template
name: "Relais 1 Sécurisé"
id: relais1
turn_on_action:
- switch.turn_off: relais2_gpio
- delay: 100ms
- switch.turn_on: relais1_gpio
turn_off_action:
- switch.turn_off: relais1_gpio
- platform: template
name: "Relais 2 Sécurisé"
id: relais2
turn_on_action:
- switch.turn_off: relais1_gpio
- delay: 100ms
- switch.turn_on: relais2_gpio
turn_off_action:
- switch.turn_off: relais2_gpio
Pour le moment, il faut que je crée les automatisation, et un dashboard.
Si une personne vois des choses a modifier, quelle n’hésite pas
Super intéressant ce projet ! J’ai la même VMC Atlantic que @cocof, avec un puits canadien dont le fonctionnement pourrait sûrement être optimisé avec un peu de domotique. Mais je ne suis pas très compétent en électronique, donc j’essaie d’abord de comprendre avant de me lancer.
Il y a un point qui m’interpelle : pourquoi un convertisseur logique 3,3V ↔ 5V alors que l’alimentation des moteurs délivre du 0-10V ? Est-ce que cela fonctionne simplement parce que le composant utilisé dans le convertisseur (le MOSFET BSS138) supporte en pratique 10V côté HV, ou y a-t-il une autre raison ?
Oui il s’agit d’un convertisseur 3v vers 5 mais je m’en sert comme un convertisseur 3 v vers 10v. Le pwm est bien en 0-10v