HERMES ELECTRONIC WR3223 ventilation controller ESP32

@mcweis I bought the same board you linked, but I still get the same issue. Could you share your yaml-File? Maybe also which version of ESPHome you are using.

@mcweis i would be happy to get the info too :upside_down_face:

I’m using version 2023.10.1 of ESPhome

And this is my yaml-file. I hope it works for you, as I’ve made a little change, as I have an EWT. But it also worked for me with the original file from schmurgel

esphome:
  name: heizung
  friendly_name: Heizung
  includes:
    - wr3223_commands.h
    - wr3223_connector.h
    - wr3223_sensorConnectors.h
    - wr3223_relaisValueDecoder.h
    - wr3223_statusValueHolder.h
    - wr3223_errorValueDecoder.h
    - wr3223_modeValueDecoder.h
    - wr3223_controller.h
  libraries: 
    - EEPROM
  platformio_options: 
    build_flags:
      -fexceptions
    build_unflags:
      -fno-exceptions

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:
  baud_rate: 0

# Enable Home Assistant API
api:
  encryption:
    key: "e/+++jk8p5jje+3GA6YMj0kZFTOVWIONkdJ6QBXf+Xk="

ota:
  password: "54137a410e3b9d95c539422b5265e969"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Heizung Fallback Hotspot"
    password: "YOURPASSWORD"

captive_portal:

uart:
  - id: uart_1
    tx_pin: GPIO19
    rx_pin: GPIO18
    baud_rate: 9600
    data_bits: 7
    parity: EVEN
    stop_bits: 1
    #debug:

custom_component:
  - lambda: |-
      auto wr3223 = new WR3223::WR3223Controller(10000, id(uart_1));
      return {wr3223};
    components:
    - id: wr3223Controller

text_sensor:
  - platform: custom    
    id: WR3223TextSensorConnector
    lambda: |-
      auto wr3223Text = new WR3223::WR3223TextSensorConnector();
      App.register_component(wr3223Text);
      return {wr3223Text->errorTextSensor,
              wr3223Text->customTextSensor};
    text_sensors: 
      - name: "FEHLER-Text"
        entity_category: diagnostic
      - name: "Custom-Text"
        entity_category: diagnostic

# das sollte hier genau übereinstimme, die flags des modus passen hier zum index
select:
  - platform: template
    name: "Betriebsmodus"
    id: "select_betriebsmodus"    
    options:             
      - "AUS"       
      - "Sommerbetrieb"
      - "Sommer-Abluftbetrieb"
      - "Winterbetrieb"      
      - "Handbetrieb"          
    lambda: |-
      auto wr3223Ctrl = static_cast<WR3223::WR3223Controller *>(wr3223Controller);
      int modus = wr3223Ctrl->Get_Modus();
      // if (modus == 1)      
      //   id(select_betriebsmodus).set_icon("mdi:white-balance-sunny");
      // else if (modus == 2)
      //   id(select_betriebsmodus).set_icon("mdi:sun-angle-outline");
      // else if (modus == 3)
      //   id(select_betriebsmodus).set_icon("mdi:snowflake");
      // else if (modus == 3)
      //   id(select_betriebsmodus).set_icon("mdi:hand-back-left-outline");
      // else
      //   id(select_betriebsmodus).set_icon("mdi:pump-off");           
      return id(select_betriebsmodus).at(modus);
    set_action: 
      then:
        - lambda: |-
            auto wr3223Ctrl = static_cast<WR3223::WR3223Controller *>(wr3223Controller);
            wr3223Ctrl->Set_Modus(id(select_betriebsmodus).index_of(x));
    update_interval: 60s
    
  - platform: template
    name: "Luftstufe"    
    id: "select_luftstufe"
    icon: mdi:fan
    options:             
      - "AUS"
      - "Luftstufe 1"
      - "Luftstufe 2"
      - "Luftstufe 3"      
    lambda: |-
      auto wr3223Ctrl = static_cast<WR3223::WR3223Controller *>(wr3223Controller);
      int stufe = wr3223Ctrl->Get_Luftstufe();            
      // if (stufe == 1)      
      //   id(select_luftstufe).set_icon("mdi:fan-speed-1");
      // else if (stufe == 2)
      //   id(select_luftstufe).set_icon("mdi:fan-speed-2");
      // else if (stufe == 3)
      //   id(select_luftstufe).set_icon("mdi:fan-speed-3");
      // else
      //   id(select_luftstufe).set_icon("mdi:fan-off");        
      return id(select_luftstufe).at(stufe);
    set_action: 
      then:
        - lambda: |-
            auto wr3223Ctrl = static_cast<WR3223::WR3223Controller *>(wr3223Controller);            
            wr3223Ctrl->Set_Luftstufe(id(select_luftstufe).index_of(x));
    update_interval: 60s

switch:
  - platform: template
    name: "Zusatzheizung"
    device_class: switch
    icon: mdi:heat-wave    
    lambda: 'return static_cast<WR3223::WR3223Controller *>(wr3223Controller)->Get_Zusatzheizung_On();'
    turn_on_action:
      - lambda: 'static_cast<WR3223::WR3223Controller *>(wr3223Controller)->Set_Zusatzheizung_On(true);'
    turn_off_action:
      - lambda: 'static_cast<WR3223::WR3223Controller *>(wr3223Controller)->Set_Zusatzheizung_On(false);'

  - platform: template
    name: "Wärmepumpe"
    id: warmepumpe_an    
    device_class: switch 
    icon: mdi:heat-pump-outline       
    lambda: |-
      bool result = static_cast<WR3223::WR3223Controller *>(wr3223Controller)->Get_Waermepunpe_On();      
      // if (result)      
      //   id(warmepumpe_an).set_icon("mdi:pump");
      // else
      //   id(warmepumpe_an).set_icon("mdi:pump-off");
      return result;
    turn_on_action:
      - lambda: 'static_cast<WR3223::WR3223Controller *>(wr3223Controller)->Set_Waermepunpe_On(true);'
    turn_off_action:
      - lambda: 'static_cast<WR3223::WR3223Controller *>(wr3223Controller)->Set_Waermepunpe_On(false);'

  - platform: template
    name: "Kühlung"    
    id: "kuhlung_an"
    device_class: switch    
    icon: mdi:snowflake    
    lambda: |-
      bool result = static_cast<WR3223::WR3223Controller *>(wr3223Controller)->Get_Kuehlung_On();      
      // if (result)      
      //   id(kuhlung_an).set_icon("mdi:snowflake");
      // else
      //   id(kuhlung_an).set_icon("mdi:snowflake-off");
      return result;
    turn_on_action:
      - lambda: 'static_cast<WR3223::WR3223Controller *>(wr3223Controller)->Set_Kuehlung_On(true);'
    turn_off_action:
      - lambda: 'static_cast<WR3223::WR3223Controller *>(wr3223Controller)->Set_Kuehlung_On(false);'

number:
  - platform: template
    name: "Luftstufe 1 Ventilatorstellwert"
    id: "vent_level_1_speed"
    min_value: 40
    max_value: 100
    step: 1
    entity_category: config
    icon: mdi:fan-speed-1
    unit_of_measurement: "%"    
    update_interval: never
    lambda: 'return static_cast<WR3223::WR3223Controller *>(wr3223Controller)->get_vent_level_speed(1);'
    set_action: 
      then:
        - lambda: 'static_cast<WR3223::WR3223Controller *>(wr3223Controller)->set_vent_level_speed(1, (int)x);'

  - platform: template
    name: "Luftstufe 2 Ventilatorstellwert"
    id: "vent_level_2_speed"
    min_value: 40
    max_value: 100
    step: 1
    entity_category: config
    icon: mdi:fan-speed-2
    unit_of_measurement: "%"
    update_interval: never
    lambda: 'return static_cast<WR3223::WR3223Controller *>(wr3223Controller)->get_vent_level_speed(2);'
    set_action: 
      then:
        - lambda: 'static_cast<WR3223::WR3223Controller *>(wr3223Controller)->set_vent_level_speed(2, (int)x);'

  - platform: template
    name: "Luftstufe 3 Ventilatorstellwert"
    id: "vent_level_3_speed"
    min_value: 40
    max_value: 100
    step: 1
    entity_category: config
    icon: mdi:fan-speed-3
    unit_of_measurement: "%"
    update_interval: never
    lambda: 'return static_cast<WR3223::WR3223Controller *>(wr3223Controller)->get_vent_level_speed(3);'
    set_action: 
      then:
        - lambda: 'static_cast<WR3223::WR3223Controller *>(wr3223Controller)->set_vent_level_speed(3, (int)x);'

  - platform: template
    name: "Update Interval"
    min_value: 1
    max_value: 20
    step: 1
    mode: SLIDER
    entity_category: config    
    icon: mdi:refresh-circle
    lambda: 'return (static_cast<WR3223::WR3223Controller *>(wr3223Controller)->get_update_interval() / 1000);'
    set_action: 
      then:
        - lambda: |-
            auto controller = static_cast<WR3223::WR3223Controller *>(wr3223Controller);
            controller->set_update_interval((int)x * 1000);
            controller->call_setup();    
  # - platform: template
  #   name: "Zuluft soll Temperatur"
  #   min_value: 18
  #   max_value: 35
  #   step: 1
  #   mode: SLIDER
  #   entity_category: config    
  #   icon: mdi:home-thermometer-outline
  #   lambda: 'return static_cast<WR3223::WR3223Controller *>(wr3223Controller)->Get_Zuluft_Soll_Temp();'
  #   set_action: 
  #     then:
  #       - lambda: 'static_cast<WR3223::WR3223Controller *>(wr3223Controller)->Set_Zuluft_Soll_Temp(x);'

binary_sensor:
  - platform: custom
    id: WR3223BinarySensorConnector
    lambda: |-
      auto wr3223Binary = new WR3223::WR3223BinarySensorConnector();
      App.register_component(wr3223Binary);
      return {wr3223Binary->kompressorRelaySensor,
              wr3223Binary->zusatzheizungRelaySensor,
              wr3223Binary->erdwaermetauscherRelaySensor,
              wr3223Binary->bypassRelaySensor,
              wr3223Binary->vorheizregisterRelaySensor,
              wr3223Binary->netzrelaisBypassRelaySensor,
              wr3223Binary->bedienteilAktivRelaySensor,
              wr3223Binary->bedienungViaRSSchnittstelleRelaySensor,
              wr3223Binary->luftstufeVorhandenRelaySensor,
              wr3223Binary->wW_NachheizregisterRelaySensor,
              wr3223Binary->magnetventilRelaySensor,
              wr3223Binary->vorheizenAktivRelaySensor,
              wr3223Binary->errorSensor,
              wr3223Binary->nachheizregisterSensor,
              wr3223Binary->absenkungSensor};
    binary_sensors: 
      - name: "Relais: Kompressor"
        entity_category: diagnostic
      - name: "Relais: Zusatzheizung"
        entity_category: diagnostic
      - name: "Relais: Erdwaermetauscher"
        entity_category: diagnostic
      - name: "Relais: Bypass"
        entity_category: diagnostic
      - name: "Relais: Vorheizregister"
        entity_category: diagnostic
      - name: "Relais: Netzrelais Bypass"
        entity_category: diagnostic
      - name: "Relais: Bedienteil aktiv"
        entity_category: diagnostic
      - name: "Relais: Bedienung via RSSchnittstelle"
        entity_category: diagnostic
      - name: "Relais: Luftstufe Vorhanden"
        entity_category: diagnostic
      - name: "Relais: Warmwasser Nachheizregister"
        entity_category: diagnostic
      - name: "Relais: Magnetventil"
        entity_category: diagnostic
      - name: "Relais: Vorheizen aktiv"
        entity_category: diagnostic
      - name: "FEHLER"
        device_class: problem
        entity_category: diagnostic
      - name: "Nachheizregister ON"
        entity_category: diagnostic
      - name: "Absenkung ON"
        entity_category: diagnostic



sensor:
- platform: custom
  id: WR3223SensorConnector
  lambda: |-
    auto wr3223Sensor = new WR3223::WR3223SensorConnector();
    App.register_component(wr3223Sensor);
    return {wr3223Sensor->verdampfertemperatur_sensor, 
            wr3223Sensor->kondensatortemperatur_sensor,
            wr3223Sensor->aussentemperatur_sensor,            
            wr3223Sensor->fortlufttemperatur_sensor,
            wr3223Sensor->zulufttemperatur_sensor,
            wr3223Sensor->nachvorheizregistertemperatur_sensor,
            wr3223Sensor->drehzahl_zuluft_sensor,
            wr3223Sensor->drehzahl_abluft_sensor};
  sensors:
  - name: "Temp: Verdampfer"
    unit_of_measurement: °C
    accuracy_decimals: 1
    device_class: temperature    
  - name: "Temp: Kondensator"
    unit_of_measurement: °C
    accuracy_decimals: 1
    device_class: temperature    
  - name: "Temp: Aussen"
    unit_of_measurement: °C
    accuracy_decimals: 1
    device_class: temperature      
  - name: "Temp: Nach Wärmetauscher (Fortluft)"
    unit_of_measurement: °C
    accuracy_decimals: 1
    device_class: temperature    
  - name: "Temp: Zuluft"
    unit_of_measurement: °C
    accuracy_decimals: 1
    device_class: temperature    
  - name: "Temp: Nach Vorheizregister"
    unit_of_measurement: °C
    accuracy_decimals: 1    
    device_class: temperature    
  - name: "Drehzahl: Zuluft"    
    unit_of_measurement: rpm
    state_class: measurement    
  - name: "Drehzahl: Abluft"
    unit_of_measurement: rpm
    state_class: measurement    

button:
  - platform: template
    name: Update Relais    
    icon: mdi:update
    entity_category: config    
    on_press:
      - lambda: 'static_cast<WR3223::WR3223Controller *>(wr3223Controller)->Refresh_Relais();'
  - platform: template
    name: Restore config
    icon: mdi:update
    entity_category: config
    on_press:
      - lambda: 'static_cast<WR3223::WR3223Controller *>(wr3223Controller)->Config_Restore();'
  - platform: template
    name: Save config
    icon: mdi:update
    entity_category: config    
    on_press:
      - lambda: 'static_cast<WR3223::WR3223Controller *>(wr3223Controller)->Config_Save();'

#switch:
#  - platform: template
#    name: "RefreshRelais"
#    lambda: |-
#      auto wr3223Relais = new WR3223RelaisConnector();      
#      if(wr3223Relais->zusatzheizungSensor->state){
#        return true;
#      } else {
#        return false;
#      }
#    turn_on_action:      
#      - homeassistant.service: 
#          service: Refresh_Relais
#    turn_off_action:
#      - homeassistant.service: 
#          service: Refresh_Relais

#text_sensor:
#- platform: custom
#  lambda: |-
#    auto wr3223 = new WR3223Connector(id(uart_1));
#    App.register_component(wr3223);
#    return {wr3223};
#  text_sensors:
#    id: "uart_readline"

#interval:
#  - interval: 3s    
#    then:
#      - uart.write:
#          id: uart_1
#          data: [0x04, 0x30, 0x30, 0x31, 0x31, 0x54, 0x32, 0x05]
    

I’ve first connected the board to ESPhome and then pasted the code into the existing yaml-file

@mcweis Thank you a lot! I will try it and get response! :slightly_smiling_face:

@mcweis THX for the share of the YAML! It works well. I still have a question about this, taxes don’t work yet. Is that because of this status:


If yes, what must be done to get it activ?

i just found in the logs:

Must the panel disconnected?

@stekolos-dev What did you change? I still get the same error. The only difference is that in my yaml is not a OTA passwort set as this seems to be deprecated.

I post the YAML and changed the API and OTA passwort. I thing the trick for me was do flash and only wait until the ESP was connecting to wireless lan. But i tryed it several times, before it works now.

From where do I get the OTA passwort? Can I define it freely in the yaml or does it needs to be stored somewhere?

This steps helped me:

First install an ESP32 as new device (direct in HA) and flash this basic setup to the ESP.
→ Install
After that was working fine. I copied from this part from step one

Enable Home Assistant API

api:
encryption:
key: “from the basic setup - step 1”

ota:
password: “from the basic setup - step 1”

i change only this from the original YAML from @mcweis

That works for me.

Thx for your response…I tried in in so many different ways . Its working finde until I add the files from the yaml-File. Then everything complies, flashing works but the device seems not to start.

The last try I had was to build only the bin-File with ESPHome and do the flashing via https://web.esphome.io/ and USB. This gives some new info. In the logs I can see after flashing:

ets Jul 29 2019 12:21:46

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:1184
Terminal disconnected: BreakError: Break received

When I flash it with basic setup booting seems to work without problems.

Same is thing. After flash and startup i must wait abaout 2-3 minutes. But than it works.

Yes, if you want to controll it by home assistant you have to detach the “Bedienteil”

Look at the pictures in github

If have checked the cables. I think i must detach the Pin 20-24.

Is that correct?

If you’re sure you can build it back if Iit don’t works, give it a try. My heating looks like the pictures at github. I have a plug, wich I pulled out and that was it.

I have detached the display “Bedienteil” now it works. But think i must do some fine tunings in the settings, because the “Zusatzheizung” is starting in every mod. Also the option “Zusatzheizung” and “Wärmepumpe” could not be setup. For info, we have a “Häusliche Feuerstätte” and a “Erdwärmertauscher”. Our house is from 2006.

Our hous is from 2015 and we’ve also a “Erdwärmetauscher” perhaps the orders to manage your heating are diffrent with the model you own.

Do you know were i cold change the heating order? I am wondering, that it works, because in a document i got from Schwörer it is not possible to set the heating mode :wink:. But it works.

The mode is set in the file wr3223_modeValueDecoder.h, but I don’t know, how it works.

Also, if I look at your picture, it seems, that you only can read the mode and not write=set it?

I am very surprised at the manufacturer’s information from Schwörer. I can really control something with the currently code. However, the compessor always starts, I actually want to avoid that. But with Schwörer I am not surprised for a long time anyway. Our original Bedin Panel is exactly 3m away from the Häusslichen fireplace. This leads to the fact that the set room temperature leads to the fact that the bypass opens and all rooms get cold. Finally, finally ventilate when manually. That is exactly what I want to improve with the circuit.
I think the control and the code underlying here will certainly help me. And with the help of the community we fix it :smiley:

Hello everybody,

after some time I had a look into the log and I get the following errors.

Does anybody else have the same errors, or is it just me?

[12:29:54][I][START:386]: Command: SP
[12:29:54][E][FINAL:376]: FAILED Command: SP Data: ???.
[12:29:54][I][START:386]: Command: Re
[12:29:54][E][FINAL:376]: FAILED Command: Re Data: -4.
[12:29:54][I][START:386]: Command: Rd
[12:29:54][E][FINAL:376]: FAILED Command: Rd Data: 61.
[12:29:54][I][START:386]: Command: Es
[12:29:55][E][FINAL:376]: FAILED Command: Es Data: 10.
[12:29:55][W][component:214]: Component custom_component took a long time for an operation (2.69 s).
[12:29:55][W][component:215]: Components should block for at most 20-30ms.
[12:30:02][E][WRITER:248]: WRITE: -30
[12:30:02][E][WRITER:249]: Data Length: 3
[12:30:02][E][WRITER:250]: COMMAND: SW
[12:30:02][E][WRITER:275]: Data to Write: :04:30:30:31:31:02:53:57:2d:33:30:03:29
[12:30:02][D][WRITER:299]: Antwort erhalten:
[12:30:02][I][WRITER:302]: Antwort POSITIV