Baxi Remeha Dietrich to ESP32 with EspHome or to Modbus-TCP?

Hello everyone.

I am trying to connect my Baxi aerothermal to Home Assistant. (When this is done I will publish the project on GitHub but it is still very raw)

To do this, I have connected a GTW-08 Gateway to convert the R-Bus signal into Modbus.
I have connected an ESP32 with a TTL converter to the modbus and that’s it, we now have communication between HA and the aerothermal system :).

Here the GTW-08 connected to the R-Bus:

Here the ESP32 connected to the Gateway:

My system has 2 zones. Zone 1 is the heating/cooling and Zone 2 is the water heater.

To simplify things I have started with zone 2, the water heater.

Here is the yaml that I have made for now:

substitutions:
  settings_skipped_updates: "30"
  devicename: "baxi"

esphome:
  name: $devicename

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

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

time:
  - platform: homeassistant
    id: homeassistant_time
     
uart:
  id: mod_bus
  tx_pin: GPIO17
  rx_pin: GPIO16
  baud_rate: 9600
  stop_bits: 1

modbus:
  id: baxi_modbus
  flow_control_pin: 5
 
modbus_controller:
  - id: baxi
    address: 0x64
    modbus_id: baxi_modbus
    setup_priority: -10
    update_interval: "15s"
    command_throttle: "50ms"


##############################  ENTITIES  ##############################


##############################  SENSORS  ##############################
sensor:

- platform: modbus_controller                       #1631 varDhwTankTemperature 8-1
  modbus_controller_id: $devicename
  name: "ACS Temperatura"  
  address: 1631
  register_type: holding
  value_type: S_WORD
  unit_of_measurement: "°C"
  accuracy_decimals: 2
  device_class: temperature
  state_class: measurement
  filters:
    - multiply:  0.01


##############################  NUMBER  ##############################


number:

  - platform: modbus_controller                     #1177 parZoneDhwComfortSetpoint 8-1
    modbus_controller_id: $devicename
    name: "ACS Temperatura de Confort"
    address: 1177
    use_write_multiple: false
    unit_of_measurement: "°C"
    device_class: temperature
    min_value: 0
    max_value: 100
    value_type: U_WORD 
    multiply: 100

  - platform: modbus_controller                     #1178 parZoneDhwReducedSetpoint 8-1
    modbus_controller_id: $devicename
    name: "ACS Temperatura ECO"
    address: 1178
    use_write_multiple: false
    unit_of_measurement: "°C"
    device_class: temperature
    min_value: 0
    max_value: 100
    value_type: U_WORD 
    multiply: 100


##############################  SELECT  ##############################


select:

- platform: modbus_controller                       #1161 parZoneMode 2
  modbus_controller_id: $devicename
  name: "ACS Modo"
  address: 1161
  use_write_multiple: false
  value_type: U_WORD
  optionsmap:
    "Auto": 0
    "Manual": 1
    "Off": 2
  skip_updates: 10

##############################  TEXT SENSOR  ##############################


text_sensor:


- platform: modbus_controller                       #411 varApStatus
  modbus_controller_id: $devicename
  name: "Estado del aparato RAW"
  address: 411
  register_type: holding
  bitmask: 0
  raw_encode: HEXBYTES

- platform: modbus_controller                       #411 varApStatus
  modbus_controller_id: $devicename
  name: "Estado del aparato"
  address: 411
  register_type: holding
  bitmask: 0
  raw_encode: HEXBYTES
  lambda: |-
    uint8_t value = modbus_controller::word_from_hex_str(x, 0);
    switch (value) {
      case 0: return std::string("En espera");
      case 1: return std::string("Demanda calor");
      case 3: return std::string("Calentando calefaccion");
      case 4: return std::string("Calentando ACS");
      case 6: return std::string("Postcirculacion bomba de calor");
      case 7: return std::string("Refrigeracion activa");
      case 8: return std::string("Parada controlada del compresor");
      case 9: return std::string("Bloqueado");
      case 10: return std::string("Bloqueo temporal");
      case 11: return std::string("Test carga minima");
      case 12: return std::string("Test carga maxima CC");
      case 16: return std::string("Proteccion antiheladas");
      case 17: return std::string("Purgago activado");
      default: return std::string("Desconocido");
    }
    return x;

- platform: modbus_controller                       #412 varApSubStatus
  modbus_controller_id: $devicename
  name: "Subestado del aparato raw"
  address: 412
  register_type: holding
  bitmask: 0
  raw_encode: HEXBYTES

- platform: modbus_controller                       #412 varApSubStatus
  modbus_controller_id: $devicename
  name: "Subestado del aparato"
  address: 412
  register_type: holding
  bitmask: 0
  raw_encode: HEXBYTES
  lambda: |-
    uint8_t value = modbus_controller::word_from_hex_str(x, 0);
    switch (value) {
      case 0: return std::string("Parado");
      case 1: return std::string("Ciclo anticorto");
      case 2: return std::string("Cambio de valvula de inversion a calefaccion");
      case 3: return std::string("Alimentacion de la bomba del sistema hibrido");
      case 4: return std::string("Condiciones de arranque pendientes en bomba y respaldo");
      case 30: return std::string("Funcionamiento normal");
      case 31: return std::string("Punto de consigna interno limitado");
      case 60: return std::string("Posfuncionamiento de la bomba");
      case 62: return std::string("Cambio de valvula de 3 vias a ACS");
      case 65: return std::string("Derivacion del compresor");
      case 66: return std::string("Temperatura superior a temperatura maxima de funcionamiento del compresor");
      case 67: return std::string("Temperatura exterior inferior a temperatura maxima de funcionamiento del compresor");
      case 68: return std::string("El funcionamiento hibrido solicita la desactivacion del compresor");
      case 69: return std::string("Deshielo en curso");
      case 70: return std::string("No se reunen las condiciones para deshielo");
      case 71: return std::string("Deshielo en curso");
      case 75: return std::string("Apagado del compresor por condensacion");
      case 78: return std::string("Correccion del punto de consigna de temperatura");
      case 82: return std::string("Temperatura inferior a temperatura minima de refrigeracion");
      case 88: return std::string("BL-respaldo limitado");
      case 89: return std::string("BL-bomba de calor limitada");
      case 90: return std::string("BL-bomba de calor y respaldo limitados");
      case 91: return std::string("BL-tarifa reducida");
      case 92: return std::string("PV-con bomba de calor");
      case 93: return std::string("PV-con bomba de calor y respaldo");
      case 94: return std::string("BL-red electrica inteligente");
      default: return std::string("Desconocido");
    }
    return x;

- platform: modbus_controller                       #1619 varZoneCurrentActivities 2
  modbus_controller_id: $devicename
  name: "ACS Actividad raw"
  address: 1619
  register_type: holding
  bitmask: 0
  raw_encode: HEXBYTES

- platform: modbus_controller                       #1619 varZoneCurrentActivities 2
  modbus_controller_id: $devicename
  name: "ACS Actividad"
  address: 1619
  register_type: holding
  bitmask: 0
  raw_encode: HEXBYTES
  lambda: |-
    uint8_t value = modbus_controller::word_from_hex_str(x, 0);
    switch (value) {
      case 0: return std::string("Off");
      case 1: return std::string("ECO");
      case 2: return std::string("Confort");
      case 3: return std::string("Antilegionella");
      default: return std::string("Desconocido");
    }
    return x;

- platform: modbus_controller                       #1620 varZoneCurrentMode 2
  modbus_controller_id: $devicename
  name: "ACS Modo de funcionamiento raw"
  address: 1620
  register_type: holding
  bitmask: 0
  raw_encode: HEXBYTES

- platform: modbus_controller                       #1620 varZoneCurrentMode 2
  modbus_controller_id: $devicename
  name: "ACS Modo de funcionamiento"
  address: 1620
  register_type: holding
  bitmask: 0
  raw_encode: HEXBYTES
  lambda: |-
    uint8_t value = modbus_controller::word_from_hex_str(x, 0);
    switch (value) {
      case 0: return std::string("Auto");
      case 1: return std::string("Manual");
      case 2: return std::string("Off");
      case 3: return std::string("Temporal");
      case 4: return std::string("Vacaciones");
      default: return std::string("Desconocido");
    }
    return x;

And the result is this:

Well, that’s what I want to do from now on:

1 - Define a Climate entity to see better the information.
How I think it should work:

  • Be able to select the work mode: Automatic (Scheduled) / Manual / Off.
  • When the Scheduled mode is selected, the machine will change between ECO and COMFORT according to its program, the target temperature should change between ECO and COMFORT according to the state it is in, and that temperature should be able to be changed.
  • When Manual mode is selected, it should work with the comfort temperature.

And I think this is it for the DHW. I don’t think it is a good idea to act on the heater, what I want is for the system to decide when to heat and when not, I will only select the Work Mode and the Temperatures of those modes.

2 - Do the same for Heating/Cooling. Here we will act on the machine. But once the DHW has been installed, the Heating/Cooling will be the same.

My first attempt was to do it all at once and I already managed to get almost all the parameters. But I preferred to do it slower and finish the DHW.

This is what I got:

But first let’s finish the DHW and then move on to Heating/Cooling

Well, the post is a bit long but I wanted to explain everything well.
This system will be valid for any machine from the Baxi/Dietrich/Remeha group…

What do I need from the community? Well, a help :slight_smile:

  • If anyone sees a way to improve the code, it would be welcome.
  • If someone helps me with the Climates, it would be welcome, I am not able to do it.

I also attach the Modbus addresses in Excel, it is easier to work with than with the PDF (I also attach the PDF)

Any help will be welcome!

Thank you so much!

Modbus parameters in Excel
Modbus parameters in PDF

1 Like

Hello good!

I’ve been arguing with ChatGPT all day, we’ve gone into a loop but in the end there’s a climate that ESPHome has let me compile:

climate:


- platform: thermostat
  id: baxi_thermostat
  name: "Baxi ACS Termostato"
  sensor: acs_temperatura
  default_preset: Comfort
  min_idle_time: 30s
  min_heating_off_time: 5min
  min_heating_run_time: 10min

  idle_action:
    - lambda: |-
        id(acs_modo).set_name("OFF");
        ESP_LOGD("climate", "Modo: OFF activado");

  preset:
    - name: "Comfort"
      default_target_temperature_low: 22.0

    - name: "ECO"
      default_target_temperature_low: 18.0

  heat_action:
    - lambda: |-
        id(acs_modo).set_name("HEAT");
        ESP_LOGD("climate", "Modo: HEAT activado");

  on_state:
    - lambda: |-
        esphome::climate::ClimateCall call(id(baxi_thermostat));
        if (id(acs_modo).state == 0) {  // AUTO
            call.set_mode(esphome::climate::CLIMATE_MODE_AUTO);
            call.perform();
            ESP_LOGD("climate", "Modo: AUTO activado");
        } else if (id(acs_modo).state == 1) {  // HEAT
            call.set_mode(esphome::climate::CLIMATE_MODE_HEAT);
            call.perform();
            ESP_LOGD("climate", "Modo: HEAT activado");
        } else if (id(acs_modo).state == 2) {  // OFF
            call.set_mode(esphome::climate::CLIMATE_MODE_OFF);
            call.perform();
            ESP_LOGD("climate", "Modo: OFF activado");
        }

And my ESP has broken and it no longer sends me anything… hahaha
He didn’t like something about what we have programmed.

INFO Starting log output from 192.168.31.118 using esphome API
WARNING Can't connect to ESPHome API for baxi @ 192.168.31.118: Error connecting to [AddrInfo(family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, proto=6, sockaddr=IPv4Sockaddr(address='192.168.31.118', port=6053))]: [Errno 111] Connect call failed ('192.168.31.118', 6053) (SocketAPIError)
INFO Trying to connect to baxi @ 192.168.31.118 in the background

In the end I’m just trying to make a thermostat with 2 working modes and 3 presets. I don’t understand why it’s so difficult… Any help from the community? :slight_smile:

Thank you very much!

I’ve been struggling to properly integrate my BAXI aerothermal &boiler for ages. The available remaha integration works only half way for me with a lot of missing stuff.

This however, blows my mind, if you ever figure it out, would be great to get a proper guide on what is needed and how its hooked up etc.

In the end it seems to me that the direction of the project is going to change… I have achieved more in one afternoon with Modbus than in the entire week with ESPHome…

I leave you the result.
The HA thermostat is faster to react than the Baxi App. It takes almost 1 minute to read a system change…

Next step? get the thermostat to behave differently when the system is in Comfort Mode or ECO Mode since the target temperatures are different.

Well, DHW finished. Tomorrow the climate.

Thanks!

Hi, can you please share your updated yaml? I have been struggling with modbus and the heat pump i own (Remeha Elga Ace, same manufacturer)

Seems you are the expert now!