Encore un autre Routeur Solaire pour chauffe-eau et chauffage
Principe de base
Afin d'optimiser l'auto-consommation de l’électricité produite par nos panneaux solaires, mieux vaut utiliser l’inertie de certains équipements électriques lorsque l'on a plus d’énergie que nécessaire, plutôt que de la perdre et de la racheter en heure de pointe dopée au CO2.
Si vous voulez comprendre pourquoi, c'est ICI
Faire Simple c'est dur...
il existe autant de routeur solaire que de personne ayant réfléchi au principe.
La principale difficulté étant de connaitre la puissance en Watt ( pas VA) d'injection sur le réseau en instantanée afin d'adapter au plus juste la "dérivation" (soutirage) vers nos consommateurs a forte inertie.
De toutes les solutions, la plus simple est de récupérer les informations du Linky.
Mais pour diverses raisons, certains n'ont pas de Linky, d'autre n'ont pas les informations d'injection d'activée, d'autre ...
Bref, la solution retenue se passera donc de Linky, et de ce fait sera universelle.
Le principe étant d'adapter la consommation de nos équipement a forte inertie afin que la puissance injectée soit le plus proche de 0.
La difficulté est que pour avoir une estimation juste de la puissance en Watt injectée, il faut une acquisition du courant et de la tension et tout un tas de calcul permettant de prendre en compte les harmoniques (oui les cours théoriques avec de belles sinusoïdales qui se simplifient à mort, ça reste très loin de la pratique avec tous nos appareils ...)
On peut réduire toute cette complexité et utilisant des milliers d'heures ingénieries intégré dans un boitier à 32€ : le "Compteur d'énergie monophasé, à impulsions, avec 2 mesures bidirectionnelles jusqu'à 100 A".
il n'y a plus qu'a
Maintenant c'est facile, on a 2 signaux à implusion tous les W.h qui nous informent de la consommation et de l'injection.
Il nous suffit d'adapter la puissance soutirée avant le compteur pour tendre vers 0 !
* Si je consome alors je reduis le soutirage des les équipements à intertie
* Si j'injecte, alors j'augmente le soutirage vers les équipements à intertie
Facile !
Les equipements à intertie
Cela doit être des équipements purement résistif et sans électronique :
- chauffe-eau a thermostat mécanique.
- radiateur type convecteur a thermostat mécanique.
- radiateur soufflant
- dégivrage résistif
Les équipements résistifs se pilotent facilement entre 0 et 100% avec un SSR monophasé piloté en 0-10V (prendre le dissipateur de chaleur) à 8€. Ne pas hésiter à prendre un 40A même pour piloter un équipement à 10A...
Attention de prendre un pilotable en 0-10V. Car il existe d'autre modèles pilotables ( 4-20mA, 0-5V ... et des tout ou rien : 0-3V 0-32V DC/AC ....)
Combinons tout cela
En 2023, pour moins de 10€ on a un micro contrôleur qui fait Wifi, programmable par USB et en OTA, intégrable à de la domotique type HomeAssistant, et programmable en YAML ! On ne vas pas s'en priver !
Il nous faut :
- N'importe quel ESP32 de préférence ou à défaut un ESP8266
- une alimentation
- ESPHome
- facultatif : HomeAssistant
Il nous faudra aussi un convertisseur PWM vers 0-10V afin de piloter nos SSR.
2 sorties de routage ?
Oui ! Car à y être, cela ne coûte pas grand chose d'avoir 2 sorties.
Une première sortie prioritaire pour le chauffe-eau et une seconde non prioritaire pour un radiateur/convecteur par exemple.
La deuxième est optionnelle
Et 2 sorties relais ?
Et comme cela ne coûtes pas grand chose de plus d'avoir 2 relais de plus, on en pilotera
- un sur la sur-consommation (plus de 400W)
- et un pour la sur-injection ( plus de 400W). Cela peut être très utile ;-)
Je m’orienterai donc vers la carte suivante : ESP32 dual relay (à moins de 10€)
Le Code
Depuis ESPHome (je n'ai plus touché à l'IDE Arduino depuis que j'ai découvert ESPHome !)
Si dessous un exemple de code pour l'ESP32 dual Relay (à adapter à vos besoins).
esphome:
name: solar-router
friendly_name: solar_router
on_boot:
priority: -100
then:
- switch.turn_off: relay_over_Prod
- switch.turn_off: relay_over_Conso
- sensor.template.publish:
id: global_pwm
state: 0
esp32:
board: esp32dev
# gpio26 and gpio27 -> in (Yes 2 pin together)
# gpio33 and gpio25 -> in (Yes 2 pin together)
# Relay 1 - gpio16
# Relay 2 - gpio17
# LED status - gpio23
# bouton - gpio0
# PWM 1 - gpio02
# PWM 2 - gpio22
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "tototototototototo"
# if 4h and no HomeAssistant : reboot in case of Wifi Crash ?.
reboot_timeout: 4h
ota:
password: "huolalalalalalla"
wifi:
ssid: 'wifi45'
password: 'superwpa'
# if 4h and no Wifi : reboot in case of Wifi Crash ?.
reboot_timeout: 4h
manual_ip:
# Set this to the IP of the ESP
static_ip: 192.168.0.188
# Set this to the IP address of the router. Often ends with .1
gateway: 192.168.0.1
# The subnet of the network. 255.255.255.0 works for most home networks.
subnet: 255.255.255.0
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "SolarRouter"
password: "anotherpassword"
captive_portal:
web_server:
port: 80
version: 2 # ESP8266 , set to 2 for ESP32
local: True # ESP32
# Status LED
status_led:
pin: GPIO23
binary_sensor:
- platform: template
name: "OffLine"
id: offline
# if no power (3Wh) or no injection (3Wh) in 10 minutes, we are OFFLINE /o\
lambda: 'return id(p_in_offline).state and id(p_out_offline).state;'
on_press:
# And so, we stop all !
- lambda: 'id(global_pwm).publish_state(0);'
- platform: copy
name: "P_in OffLine"
id: p_in_offline
source_id: p_in
internal: True
filters:
# If no pulse since 10 minuts, are we OffLine ?
- delayed_on : 600s
- platform: copy
name: "P_out OffLine"
id: p_out_offline
source_id: p_out
internal: True
filters:
# If no pulse since 10 minuts, are we OffLine ?
- delayed_on : 600s
- platform: gpio
pin:
number: GPIO26
inverted : false
mode:
input: true
pullup: true
name: "P_in"
id: p_in
internal: True
device_class : POWER
filters:
- delayed_on: 500ms
on_press:
then:
- component.update: w_in
- binary_sensor.template.publish:
id: offline
state: OFF
- platform: gpio
pin:
number: GPIO33
inverted : false
mode:
input: true
pullup: true
name: "P_out"
id: p_out
internal: True
device_class : POWER
filters:
- delayed_on: 500ms
on_press:
then:
- component.update: w_out
- binary_sensor.template.publish:
id: offline
state: OFF
sensor:
- platform: pulse_counter
pin:
# use duplicate pin with same signal
number : GPIO27
inverted : false
mode:
input: true
pullup: false
name: "Delta P Conso (W_in)"
id: w_in
unit_of_measurement: 'W'
update_interval : never
internal_filter : 13us
accuracy_decimals: 0
filters:
- multiply: 30
# every on change set PWM | if 0 -> check W_out <=30
on_value:
- lambda: !lambda |-
if (x > 0) {
if (int(id(global_pwm).state) >= 0) {
id(global_pwm).publish_state(max(0,int(id(global_pwm).state) - max(1,int(x/30))));
}
if (id(w_out).state > 0 ) {
id(w_out).publish_state(0);
}
}
- platform: pulse_counter
pin:
# use duplicate pin with same signal
number : GPIO25
inverted : false
mode:
input: true
pullup: false
name: "Delta P Inj (W_out)"
id: w_out
unit_of_measurement: 'W'
update_interval : never
internal_filter : 13us
accuracy_decimals: 0
filters:
- multiply: 30
# every on change set PWM | if 0 -> check W_out <=30
on_value:
- lambda: !lambda |-
if (x > 0) {
if (int(id(global_pwm).state) <= 200) {
id(global_pwm).publish_state(min(200,int(id(global_pwm).state) + int(x/30)));
}
if (id(w_in).state > 0) {
id(w_in).publish_state(0);
}
}
- platform: template
name: "Conso Prioritaire PWM (Actuel)"
id: "conso_pwm1"
accuracy_decimals: 0
unit_of_measurement: "%"
- platform: template
name: "Conso Secondaire PWM (Actuel)"
id: "conso_pwm2"
accuracy_decimals: 0
unit_of_measurement: "%"
- platform: template
name: "Injection Acutelle"
id: "p_inj"
accuracy_decimals: 0
unit_of_measurement: "W"
lambda: !lambda |-
if (int(id(global_pwm).state) >= 200) {
return id(w_out).state;
} else {
return 0;
}
- platform: template
name: "Global PWM (Actuel)"
id: "global_pwm"
update_interval: 60s
accuracy_decimals: 0
unit_of_measurement: "%"
# on change : set 2 other PWM 0-100% and 100%-200%
on_value:
then:
- lambda: !lambda |-
id(pwm_output_high_p).set_level(float(min(100,int(x)))/100.0);
id(pwm_output_low_p).set_level(float(min(100,max(0,int(x) - 100)))/100.0);
id(conso_pwm1).publish_state(min(100,int(x)));
id(conso_pwm2).publish_state(min(100,max(0,int(x) - 100)));
- if:
condition:
lambda: 'return (id(w_in).state > 400) and (x==0) and ! (id(relay_over_Conso).state);'
then:
- logger.log: "over-consumption"
- switch.turn_on: relay_over_Conso
- if:
condition:
lambda: 'return (id(w_out).state > 400) and (x==200) and !(id(relay_over_Prod).state);'
then:
- logger.log: "over-production"
- switch.turn_on: relay_over_Prod
- if:
condition:
lambda: 'return (x<200) and (id(relay_over_Prod).state);'
then:
- switch.turn_off: relay_over_Prod
- if:
condition:
lambda: 'return (x>0) and (id(relay_over_Conso).state);'
then:
- switch.turn_off: relay_over_Conso
# PWM output (software on ESP8266 to test)
output:
- platform: ledc # ledc for ESP32 (hardware PWM) esp8266_pwm for ESP8266
# Primary Power pin in PWM converted to 0-10V converted to 0-100% AC 230V wave
pin: GPIO02
frequency: 1000 Hz
id: pwm_output_high_p
- platform: ledc # ledc for ESP32 (hardware PWM) esp8266_pwm for ESP8266
# Secondary Power pin in PWM converted to 0-10V converted to 0-100% AC 230V wave
pin: GPIO22
frequency: 1000 Hz
id: pwm_output_low_p
switch:
- platform: gpio
pin:
number: GPIO16
inverted : false
id: 'relay_over_Conso'
name: "Sur-Consommation"
restore_mode: ALWAYS_OFF
- platform: gpio
pin:
number: GPIO17
inverted : false
id: 'relay_over_Prod'
name: "Sur-Production"
restore_mode: ALWAYS_OFF
Le Schéma complet
ici il y aura le schéma complet un jour ...
Le résulat
Le module est parfaitement autonome et fonctionne avec et sans HomeAssistant !