DIY Heizkörperlüfter mit ESPHome und HomeAssistant

Hallo,

weil es bis jetzt ein Erfolg war und noch Teile übrig sind, baue ich zur Zeit meinen dritten Heizkörperlüfter.
Ich konnte damit meine Heizungsanlage noch effizienter einstellen: Heizkurve anpassen und Vorlauftemperatur senken, trotzdem mit Nachtabsenkung arbeiten und das Wohnzimmer auf 22°C bekommen. Wir haben hier weiterhin die alten Rippenheizkörper mit Reflexfolie in den Nischen. Bei -10°C Außentemperatur, was generell bei uns eher selten vorkommt, benötige ich einen Vorlauf von 50°C. Bei 5°C Außentemperatur ~ 38°C Vorlauf. Somit haben wir uns auch für eine Wärmepumpe entschieden, die im Laufe des Jahres irgendwann kommt. Aber nun zum eigentlichen Thema…

Teileliste:

  • Je nach Heizkörperbreite entsprechende Anzahl an PC Silent Lüftern. Ich habe mich für “ARCTIC P12 PWM PST” entschieden. Die gibt es sogar günstig im 5er-Pack und haben gleich Stecker zum in Reihe schalten. Bis 70% Lüfterleistung sind die kaum zu hören. Je mehr, desto mehr Luftumsatz
  • 2x Winkelprofil in entsprechender Länge. Die Eloxierten aus Aluminium sehen meiner Meinung nach am besten aus. Im aktuellen Fall habe ich noch weiße aus Plastik, welche auch funktionieren.
  • ESP-01 mit Programmiermodul (passender USB-Serial-Adapter)
  • 12v Relaisplatine für ESP-01
  • Dallas/DS18b20 Temperaturfühler mit 4,7k Ohm Widerstand
  • 12V Netzteil
  • Typische DIY Dinge wie Käbelchen, Kabelbinder, Isolierband und Heißkleber
  • Gegebenenfalls einen 3D-Drucker zum Drucken des Gehäuses

→ weiter unten habe ich Bestellinks aufgeführt

Programmiert und verwaltet wird der ESP-01 über ESPHome in Homeassistant. Somit kann ich den aktuellen Stand sowie Vorlauftemperatur einsehen und die Lüfterzahl gegebenenfalls manuell steuern. In meinem Codebeispiel wird die Drehzahl bis 70% über eine kleine Formel abhängig der Vorlauftemperatur geregelt. Über Homeassistant Automationen lässt sich die Lüftung gegebenenfalls auf 100% steuern, damit der Raum noch schneller beheizt werden kann.

substitutions:
  devicename: esp01-heizkoerper-fan
  upper_devicename: ESP01-Heizkoerper-Fan
  
esphome:
  name: $devicename

esp8266:
  board: esp01_1m
  restore_from_flash: true

preferences:
    flash_write_interval: 3min

logger:
 baud_rate: 0
 
debug:
    update_interval: 5s

# Enable Home Assistant API
api:
    encryption:
      key: <encryption key>
ota:
  password: "<OTA PAssword>"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  domain: <Heimdomain, zb ".fritz.box">
  
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: ${upper_devicename}
    password: "<AP Wifi Password>"

captive_portal:
 
 
uart:
  baud_rate: 9600
  tx_pin: 1
    
switch:
  - platform: template
    id: FanRelais
    internal: true
    restore_state: on
    restore_mode: RESTORE_DEFAULT_OFF 
    turn_on_action:
      - uart.write: [0xA0, 0x01, 0x01, 0xA2]
    turn_off_action:
      - uart.write: [0xA0, 0x01, 0x00, 0xA1]
    on_turn_on: 
      - logger.log: "Switch Turned On!"
    on_turn_off:
      - logger.log: "Switch Turned Off!"

  - platform: restart
    name: "${upper_devicename} Reset"   


  - platform: template
    name: ${upper_devicename} ManualMode
    id: ManualMode
    optimistic: true
    restore_state: on


 
#DS18B20 shared bus pin, needs external 4.7k ohm pull-up resistor to 3.3V
dallas:
  pin: GPIO0
  update_interval: 60s
 

output:
  - platform: esp8266_pwm
    pin: GPIO2
    frequency: 20000 Hz
    id: pwm_output
    
fan:
  - platform: speed
    output: pwm_output
    name: ${upper_devicename} Ventilatoren
    id: fans
    on_turn_on:
      - switch.turn_on:
          id: FanRelais
    on_turn_off:
      - switch.turn_off:
          id: FanRelais 
    on_speed_set:
      - switch.turn_on:
          id: FanRelais 



#Math: ((3,3)*Temp)-(63,333)
    
sensor:
  - platform: dallas
    address: <Adresse des Temp-Sensors: ESP Flashen und aus dem Log die ID hierhin kopieren>
    name: ${upper_devicename} Heizkörpertemperatur
    id: temp
    on_value:
      then:
        - logger.log: "Temp - speed Math"
        - logger.log:
            format: "ManualMode: %.1f"
            args: [ 'id(ManualMode).state' ]
        - if: 
            condition: 
              and:
                - switch.is_off: ManualMode
                - lambda: |-
                        int tmp = id(temp).state;
                        return (tmp >=23) and (tmp<40);
            then:
              - fan.turn_on:
                  id: fans
                  speed: !lambda |-
                    int fanvalue = ((4) * id(temp).raw_state) - 80;
                    return fanvalue;
              - logger.log:
                  format: "Temp-Value: %.1f - Fan-Value %.1f"
                  args: [ 'id(temp).raw_state', 'id(fans).speed' ]
                    
                    
                    
    on_value_range:
      - below: 23
        then:
          - logger.log: "below 23: Fan OFF"
          
          - if: 
              condition: 
                - switch.is_off: ManualMode
              then:
                - fan.turn_off:
                    id: fans
                - logger.log:
                    format: "Temp-Value: %.1f - Fan-Value: off"
                    args: [ 'id(temp).raw_state' ]
      - above: 40
        then:
          - logger.log: "above 40: speed 70%"
          - if: 
              condition: 
                - switch.is_off: ManualMode
              then:
                - fan.turn_on:
                    id: fans
                    speed: 70
                - logger.log:
                    format: "Temp-Value: %.1f - Fan-Value %.1f"
                    args: [ 'id(temp).raw_state', 'id(fans).speed' ]
#  - platform: debug
#    free:
#      name: "${upper_devicename} Heap Free"
#    fragmentation:
#      name: "${upper_devicename} Heap Fragmentation"
#    block:
#      name: "${upper_devicename} Heap Max Block"
#    loop_time:
#      name: "${upper_devicename} Loop Time"


  - platform: uptime
    id: Uptime
    internal: true
    update_interval: 60s
    on_raw_value:
      then:
        - text_sensor.template.publish:
            id: esp01_gas_uptime_human
            state: !lambda |-
              int seconds = round(id(Uptime).raw_state);
              int days = seconds / (24 * 3600);
              seconds = seconds % (24 * 3600);
              int hours = seconds / 3600;
              seconds = seconds % 3600;
              int minutes = seconds /  60;
              seconds = seconds % 60;
              return (
                (days ? to_string(days) + "d " : "") +
                (hours ? to_string(hours) + "h " : "") +
                (minutes ? to_string(minutes) + "m " : "") +
                (to_string(seconds) + "s")
              ).c_str();

  - platform: wifi_signal
    name: ${upper_devicename} Signal Strength

time:
  - platform: homeassistant
    id: homeassistant_time

      
text_sensor:
  - platform: version
    name: ${upper_devicename} ESPHome Version
  - platform: wifi_info
    ip_address:
      name: ${upper_devicename} IP Address
    ssid:
      name: ${upper_devicename} SSID
    bssid:
      name: ${upper_devicename} BSSID
  - platform: template
    id: esp01_gas_uptime_human
    name: ${upper_devicename} Uptime Human Readable
    icon: mdi:clock-start
  - platform: debug
    device:
      name: "${upper_devicename} Device Info"
    reset_reason:
      name: "${upper_devicename} Reset Reason"


web_server: # creates a web server where you can access all this stuff without home assistant (good for debugging or working headless (no HA))
  port: 80
  include_internal: true
  ota: true

binary_sensor: # exposes online status
  - platform: status
    name: "${upper_devicename} Status"

In den nächsten Tagen werde ich diesen Beitrag weiter ergänzen:

  • aktueller Fortschritt
  • 3D-Projekt des Gehäuses

Hier eine kleine Auflistung, wo ich die entsprechenden Bauteile bestellen könnt. Ich bestelle gerne bei Aliexpress, Ihr müsst das aber nicht! Andere “Chinashops” wie Banggood haben die teile bestimmt auch im Programm. also keine Werbung, nur Beispiele, keine affiliate Links! Zudem sind die unterschiedlichen Bauteilen bei unterschiedlichen Händlern vielleicht günstiger. Schaut selbst!

Weitere Tipps, um damit Energie zu sparen:

  • Die Heizkörper sollten soweit es geht frei stehen, damit die Wärme in den Raum abgestrahlt werden kann
  • Zumindest die Konvektion, also die Luftzirkulation, sollte ungehindert stattfinden. Also Couch vom Heizkörper weg, damit zumindest 30cm platz sind.
  • Keine Vorhänge bis zum Boden, sonst fängt sich die Wärme größtenteils im Bereich Fenster-Vorhang

WICHTIG: Das ganze ist Work in progress. So wie es da ist, funktioniert zumindest es bei mir und ich bin soweit zufrieden damit. Aktualisieren kann man die Funktionalität über OTA (Over the Air) in ESPHome. Falls ihr Verbesserungsideen habt, immer gerne her damit!

9 „Gefällt mir“

Ach so ein Mist, da bin ich doch gerade dran :scream:

Spaß beiseite, bin gespannt :pray:

Darauf bin ich nicht gekommen, gefällt mir :+1:

Warte erst auf meinen nächsten Thread, da geht es um einen getunten Katzenfutterautomat mit ESPhome und homeassistant Anbindung, Kamera und PIR-Sensor. :grin:

3 „Gefällt mir“

Sehr schönes Projekt! Steht bei uns(testweise) auch an. Wie planst du die Befestigung am Heizkörper? Gerade was das Thema Entkopplung angeht?

Befestigt habe ich das zur Zeit einfach nur mit langen Kabelbindern. Das ganze System “liegt” darin und ist einigermaßen gut entkoppelt. Als weiterer Vorteil stellte sich heraus, dass man so relativ einfach den Winkel einstellen kann.

Ich habe den Code verbessert, da doch noch einige Bugs darin waren:

substitutions:
  devicename: esp01-heizkoerper-fan-wohnzimmer
  upper_devicename: ESP01-Heizkoerper-Fan-Wohnzimmer
  DallasID: "<Adresse des Temp-Sensors: ESP Flashen und aus dem Log die ID hierhin kopieren>"
  IPAddress: "IP-Adresse in deinem Netzwerk - Ich lege die IP-Adresse selbst fest, da bei mir das Wifi der kleinen ESPs nicht immer perfekt funktioniert und somit vielleicht keine IP zugeteilt wird. Gegebenenfalls unten noch Gateway und DNS-Server anpassen"
  ApiKey: "<API Encryption Key>"
  OTAPass: "<OTA-Password>"
  APPass: "<Fallback-AP-Password>"

esphome:
  name: $devicename

esp8266:
  board: esp01_1m
  restore_from_flash: true

preferences:
    flash_write_interval: 3min

# Enable logging
logger:
#  level: VERBOSE
  baud_rate: 0

debug:
    #update_interval: 5s

# Enable Home Assistant API
api:
  encryption:
    key: ${ApiKey}

ota:
  password: ${OTAPass}

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  domain: .fritz.box
  manual_ip:
    static_ip: ${IPAddress}
    subnet: 255.255.255.0
    gateway: 192.168.178.1
    dns1: 192.168.178.1

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: ${upper_devicename}
    password: ${APPass}

captive_portal:

uart:
  baud_rate: 9600
  tx_pin: 1
    
switch:
  - platform: template
    id: FanRelais
    internal: true
    optimistic: true
    restore_state: on
    restore_mode: RESTORE_DEFAULT_OFF 
    turn_on_action:
      - logger.log: "switch - FanRelais - turn_on_action - uart.write: Turn On!"
      - uart.write: [0xA0, 0x01, 0x01, 0xA2]
    turn_off_action:
      - logger.log: "switch - FanRelais - turn_off_action - uart.write: Turn Off!"
      - uart.write: [0xA0, 0x01, 0x00, 0xA1]

  - platform: template
    name: ${upper_devicename} ManualMode
    id: ManualMode
    optimistic: true
    restore_state: on

button:
  - platform: restart
    name: "${upper_devicename} Restart"   
    disabled_by_default: true
  - platform: factory_reset
    name: "${upper_devicename} Factory reset"   
    disabled_by_default: true

    
#DS18B20 shared bus pin, needs external 4.7k ohm pull-up resistor to 3.3V
dallas:
  pin: GPIO0
  update_interval: 60s
#  update_interval: 5s


output:
  - platform: esp8266_pwm
    pin: GPIO2
    frequency: 20000 Hz
    id: pwm_output
    
fan:
  - platform: speed
    output: pwm_output
    name: ${upper_devicename} Ventilatoren
    id: fans
    on_turn_on:
      - logger.log: "Fan - on_turn_on - Relais turn On!"
      - switch.turn_on:
          id: FanRelais
    on_turn_off:
      - logger.log: "Fan - on_turn_off - Relais turn Off!"
      - switch.turn_off:
          id: FanRelais 


#Math: ((10/3)*Temp)-(63 1/3)
#Math: ((10/3)*Temp)-(63,333)
#Math: ((3,3)*Temp)-(63,333)
    
sensor:
  - platform: dallas
    address: ${DallasID}
    name: ${upper_devicename} Heizkörpertemperatur
    id: temp
    on_value:
      then:
        - logger.log:
            format: "Dallas - on_value - Temp: %.1f - ManualMode: %s"
            args: [ 'id(temp).raw_state', 'id(ManualMode).state ? "ON" : "OFF"' ]

        - if: 
            condition: 
              and:
                - switch.is_off: ManualMode
                - lambda: |-
                        int tmp = id(temp).state;
                        return (tmp >= 23);
            then:
              - logger.log:
                  format: "Dallas - on_value - start - Temp (%.1f)"
                  args: [ 'id(temp).raw_state' ]
              - fan.turn_on:
                  id: fans
                  speed: !lambda |-
                    int fanvalue = ((4) * id(temp).raw_state) - 80;
                    fanvalue = fanvalue > 70 ? 70 : fanvalue;
                    return fanvalue;
              - logger.log:
                  format: "Dallas - on_value - end - Temp (%.1f) => Fan-Value %i"
                  args: [ 'id(temp).raw_state', 'id(fans).speed' ]
            else:
              - if: 
                  condition: 
                    and:
                      - switch.is_off: ManualMode
                      - lambda: |-
                              int tmp = id(temp).state;
                              return (tmp <23);
                  then:
                    - fan.turn_off:
                        id: fans
                    - logger.log:
                        format: "Dallas - on_value - Temp (%.1f) < 23 => Fan Off"
                        args: [ 'id(temp).raw_state' ]



  - platform: uptime
    id: Uptime
    internal: true
    update_interval: 60s
    disabled_by_default: true
    on_raw_value:
      then:
        - text_sensor.template.publish:
            id: esp01_gas_uptime_human
            state: !lambda |-
              int seconds = round(id(Uptime).raw_state);
              int days = seconds / (24 * 3600);
              seconds = seconds % (24 * 3600);
              int hours = seconds / 3600;
              seconds = seconds % 3600;
              int minutes = seconds /  60;
              seconds = seconds % 60;
              return (
                (days ? to_string(days) + "d " : "") +
                (hours ? to_string(hours) + "h " : "") +
                (minutes ? to_string(minutes) + "m " : "") +
                (to_string(seconds) + "s")
              ).c_str();

  - platform: wifi_signal
    internal: true
    id: rssi_sensor

  - platform: template
    name: "${upper_devicename} Wifi"
    unit_of_measurement: "%"
    accuracy_decimals: 0
    icon: "mdi:wifi"
    update_interval: 60s
    disabled_by_default: true
    lambda: |-
      // Taken from   https://github.com/tzapu/WiFiManager/blob/master/...
      int quality;
      const int rssi = id(rssi_sensor).state;
      if(rssi <= -100){quality = 0;}
      else if (rssi >= -50){quality = 100;}
      else{quality = 2 * (rssi + 100);}
      return quality;

time:
  - platform: homeassistant
    id: homeassistant_time

      
text_sensor:
  - platform: version
    name: ${upper_devicename} ESPHome Version
    disabled_by_default: true
  - platform: wifi_info
    ip_address:
      name: ${upper_devicename} IP Address
      disabled_by_default: true
    ssid:
      name: ${upper_devicename} SSID
      disabled_by_default: true
    bssid:
      name: ${upper_devicename} BSSID
      disabled_by_default: true
  - platform: template
    id: esp01_gas_uptime_human
    name: ${upper_devicename} Uptime Human Readable
    icon: mdi:clock-start
    disabled_by_default: true
  - platform: debug
    device:
      name: "${upper_devicename} Device Info"
      disabled_by_default: true
    reset_reason:
      name: "${upper_devicename} Reset Reason"
      disabled_by_default: true



web_server: # creates a web server where you can access all this stuff without home assistant (good for debugging or working headless (no HA))
  port: 80
  include_internal: true
  ota: true

binary_sensor: # exposes online status
  - platform: status
    name: "${upper_devicename} Status"
    disabled_by_default: false
2 „Gefällt mir“

So sieht das Ganze nun aus:

Die Verbindung zum Lüfter habe ich folgendermaßen gemacht: Kontakte abisoliert, etwas dicker verlötet, in den Stecker und dann so mit Heißkleber fixiert, dass man es auch nochmal herausziehen könnte. Does the job…

Den Temperatursensor und das Kabel für das PWM-Signal habe ich direkt auf der Unterseite der Platine verlötet:

Hier ein Bild vom 3D gedruckten Gehäuse:

Im Anhang die 3mf-Dateien → wird hochgeladen, sobald das Dateiformat erlaubt wird :slight_smile:

Daher “nur” ein Link zu diesem OnShape Projekt

Falls das mit dem Link nicht funktioniert, das Projekt ist öffentlich unter der Bezeichnung “ESP-01 Relais Box” zu finden.

2 „Gefällt mir“

@mathmoe könntest du bitte nochmal ein Bild schicken wie du das genau verkabelt hast? Stehe beim Nachbau gerade etwas auf dem Schlauch.

Hi,
An welcher Stelle hängt es denn? Bilder sind schwierig, da alles fertig verkabelt unter dem Heizkörper hängt :slight_smile:

Cooles Projekt, hab das schon mehrfach gesehen.

Ich werde das mal fürs Arbeitszimmer nachbauen um morgen schneller den Raum warm zu bekommen. Lüfter hab ich noch zwei, die sollten bei der Raumgröße reichen und einen ESP8266 mit Relais liegt auch noch in der Bastelkiste.

@mathmoe ich habe jetzt deine Verlinkten Produkte gekauft aber leider funktioniert das ESP Flasher nicht :frowning:

Ich bekomme im web.esphome.io Fenster immer die Meldung “Failed to execute ‘open’ on ‘SerialPort’: Failed to open serial port” kennt jemand den Fehler und weiß wie ich Ihn beheben kann?

Hab bereits den USB Port und den PC Gewechselt überall der gleiche Fehler.

USB-Treiber installiert ?

Ja habe ich, habe schon mehrere Wemos D1 usw. geflasht ohne Probleme. Daher wundert mich das

Hast du das Modul mit dem Relais genutzt ?

Den muss on den Programmiermodus gesetzt werden:

GPIO0 muss während des Bootens auf Masse liegen, damit der Chip in denProgrammiermodus geht.Dazu eine Brücke zwischen GPIO0 und GND legen.Diese Brücke muss nach dem Programmieren wieder entfernt

1 „Gefällt mir“

Ich hatte beide probiert, das was zum USB Programmierer dazu war und das was beim Relais dabei war =) ich versuch das aber mal DANKE

Hi, ich habe gerade bei der Installation gestgestellt das ESP die Programmierung “restore_state: on” nicht mehr zuläßt. kann ich die beiden Zeilen die es berifft entfernen oder klappt das dann nicht mehr? Noch was, danke für die Tolle Anleitung. Grüße

DANKE hat funktioniert… ich dachte der USB Flasher macht das von selbst :sweat_smile:

Nee, ich hatte da was in Erinnerung und das Datenblatt davon rausgesucht, da war auch der Text raus.

Spannend! bin gerade an einem ähnlichen Thema dran.
BJ 67, unsaniert was Dämmung betrifft, neue Gasheizung (alte war defekt und kurzfristig war das Thema WP nicht zu finanzieren) und spiele gerade mit der Vorlauftemperatur rum. Zieltemperatur sind mal 20 Grad, habe alle Heizkörperthermostate auf 5 und versuche zu schauen wie sich die Räume verhalten.
Im WZ ist mein Sorgenkind: da sind zwei neue Typ22 HK (100x400x1800, Typ 22) die aber in Summe ca. 48qm inkl. Flur heizen müssen (ohne ca. 42). Bei aktuellen Temperaturen von ca. 8-9 Grad und einer VT von 34,3 Grad schaffen die es leider nicht mehr den Raum ordentlich zu heizen.

Wie viele extra-Grad konntest du mit den Lüftern rausholen?

Würde gerne mit einem Vereinfachten Aufbau Starten: WLAN Steckdose via Zeit steuern wenn die Heizung an ist und ggf. einfach mit 5v an USB laufen lassen und Erfahrung sammeln.

Hallo
sehr interessantes Projekt, ich hab für unsere Wohnzimmerheizung eine fertige Lösung in die Heizung eingebaut um den Wohnbereich mit ca 180m³ aufzuwärmen, mein Heizkörper ist recht gross 240x60x15cm und hat ausreichend Platz innendrin.
Produkt : 3x SpeedComfort, Der Raum wird auch mit diesen kleinen Lüftern wesentlich schneller warm da die Wärme besser im Raum verteilt wird. Die Temperatur Thermostat am Heizkörperthermostat konnte um 1-2° reduziert.
Unter Umständen werde ich diese Einheit demnächst gegen einen Selbstbau mit 120mmm Lüftern austauschen um die Heizung noch effizienter zu machen.
LG Tom