Hi to all,
I have just put the boiler VICTRIX SUPERIOR 35 PLUS control into operation using the MODBUS interface (they call it BMS), so I want to share with the community my findings and working solution.
I chose esphome installed on ESP32-WROOM-32 with UART to RS 485 converter.
The converter is connected to the terminals D+, D- and GND(24)
My YAML configuration is as follows (names of the sensors are in the Czech language, but I tried to add a comment explaining what it is)
substitutions:
settings_skipped_updates: "30"
bus_name: "bms"
esphome:
name: kotel
friendly_name: kotel
esp32:
board: esp32dev
framework:
type: arduino
logger:
baud_rate: 0
level: ERROR
time:
- platform: sntp
id: sntp_time
timezone: Europe/Prague
interval:
- interval: 20sec
then:
- if:
condition:
switch.is_on: do_heater
then:
- number.set:
id: boiler_control_heat_num
value: 0x0055
api:
encryption:
key: redacted
reboot_timeout: 1h
ota:
- platform: esphome
password: redacted
web_server:
port: 80
wifi:
ssid: redacted
password: redacted
domain: redacted
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: redacted
password: redacted
captive_portal:
uart:
id: mod_bus
tx_pin: GPIO16
rx_pin: GPIO17
baud_rate: 19200 # default is 9600
stop_bits: 1
parity: EVEN
#debug:
# sequence:
# - lambda: UARTDebug::log_binary(direction, bytes, ':');
# https://www.vipsgas.cz/User_Files/f/images/stories/soubory/navody/immergas/victrix-superior-2021-specifikace-protokol-modbus-web.pdf
modbus:
id: modbus1
#send_wait_time: 500ms
uart_id: mod_bus
modbus_controller:
- id: bms
## the Modbus device addr
address: 1 #0x1
update_interval: 10s
command_throttle: 100ms
modbus_id: modbus1
setup_priority: -10
on_offline:
then:
- logger.log: "Boiler goes offline!"
on_online:
then:
- logger.log: "Boiler back online!"
sensor:
- platform: wifi_signal # Reports the WiFi signal strength/RSSI in dB
name: "WiFi Signal dB"
id: wifi_signal_db
update_interval: 60s
entity_category: "diagnostic"
- platform: copy # Reports the WiFi signal strength in %
source_id: wifi_signal_db
name: "WiFi Signal Percent"
filters:
- lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
unit_of_measurement: "Signal %"
entity_category: "diagnostic"
- platform: internal_temperature
name: "Internal Temperature"
update_interval: 300s
entity_category: "diagnostic"
- platform: homeassistant
id: hass_obyvak_temp
internal: True
entity_id: sensor.obyvak_ws_temperature
- platform: homeassistant
id: hass_obyvak_hum
internal: True
entity_id: sensor.obyvak_ws_humidity
- platform: modbus_controller
modbus_controller_id: $bus_name
name: "Boiler 256 RAW bit"
id: boiler_control_raw
register_type: holding
address: 256
- platform: modbus_controller
modbus_controller_id: $bus_name
# Outlet temperature from the primary heat exchanger with a resolution of 0.1 °C
name: "Teplota výstupu"
id: boiler_output_temp
address: 0x300
register_type: read
value_type: S_WORD
unit_of_measurement: "°C"
accuracy_decimals: 1
device_class: temperature
state_class: measurement
filters:
- multiply: 0.1
- platform: modbus_controller
modbus_controller_id: $bus_name
#Return temperature with a resolution of 0.1 °C
name: "Teplota zpátečky"
address: 0x301
register_type: read
value_type: S_WORD
unit_of_measurement: "°C"
accuracy_decimals: 1
device_class: temperature
state_class: measurement
filters:
- multiply: 0.1
- platform: modbus_controller
modbus_controller_id: $bus_name
id: tuv_modbus_temp
#DHW temperature with a resolution of 0.1 °C
name: "Teplota TUV"
address: 0x302
register_type: read
value_type: S_WORD
unit_of_measurement: "°C"
accuracy_decimals: 1
device_class: temperature
state_class: measurement
filters:
- multiply: 0.1
- platform: modbus_controller
modbus_controller_id: $bus_name
#Flue gas temperature in °C
name: "Teplota spalin"
address: 0x303
register_type: read
value_type: S_WORD
unit_of_measurement: "°C"
accuracy_decimals: 0
device_class: temperature
state_class: measurement
filters:
- multiply: 1
# Outdoor temperature with a resolution of 0.1 °C only with connected outdoor temperature probe
# - platform: modbus_controller #
# modbus_controller_id: $bus_nme
# name: "Venkovni teplota"
# address: 0x304
# #register_type: holding
# register_type: read
# value_type: S_WORD
# unit_of_measurement: "°C"
# accuracy_decimals: 1
# device_class: temperature
# state_class: measurement
# filters:
# - multiply: 0.1
#
- platform: modbus_controller
modbus_controller_id: $bus_name
#Modulation level with 0.1% resolution
name: "Úroveň modulace"
address: 0x307
#register_type: holding
register_type: read
value_type: U_WORD
unit_of_measurement: "%"
accuracy_decimals: 1
device_class: power_factor
state_class: measurement
filters:
- lambda: |-
if (x == 32768) return 0;
else return x * 0.1;
- platform: modbus_controller
modbus_controller_id: $bus_name
#Calculated heating output temperature with a resolution of 0.1 °C
name: "Vypočtená výstupní teplota"
address: 0x308
register_type: read
value_type: U_WORD
unit_of_measurement: "°C"
accuracy_decimals: 1
device_class: temperature
state_class: measurement
filters:
- multiply: 0.1
- platform: modbus_controller
modbus_controller_id: $bus_name
#Heating output temperature with a resolution of 0.1 °C only with a connected HVDT temperature probe
name: "Teplota za HVDT"
address: 0x309
#register_type: holding
register_type: read
value_type: U_WORD
unit_of_measurement: "°C"
accuracy_decimals: 1
device_class: temperature
state_class: measurement
filters:
- lambda: |-
if (x == 32768) return 0;
else return x * 0.1;
- platform: modbus_controller
modbus_controller_id: $bus_name
#Measured DHW inlet temperature with a resolution of 0.1 °C only for boilers equipped with a DHW inlet temperature probe
name: "Vstupní teplota TUV"
address: 0x30A
#register_type: holding
register_type: read
value_type: U_WORD
unit_of_measurement: "°C"
accuracy_decimals: 2
device_class: temperature
state_class: measurement
filters:
- lambda: |-
if (x == 32768) return 0;
else return x;
- platform: modbus_controller
modbus_controller_id: $bus_name
#DHW flow in l/min - only boilers equipped with DHW flow measurement
name: "Průtok TUV"
address: 0x30B
register_type: read
value_type: U_WORD
unit_of_measurement: "L/min"
accuracy_decimals: 0
device_class: volume_flow_rate
state_class: measurement
filters:
- lambda: |-
if (x == 32768) return 0;
else return x;
- platform: modbus_controller
modbus_controller_id: $bus_name
#Pump flow rate in l/min
name: "Průtok čerpadlem"
address: 0x30C
register_type: read
value_type: U_WORD
unit_of_measurement: "L/min"
accuracy_decimals: 0
device_class: volume_flow_rate
state_class: measurement
filters:
- lambda: |-
if (x == 32768) return 0;
else return x;
- platform: modbus_controller
modbus_controller_id: $bus_name
#Fan speed in rpm
name: "Rychlost ventilátoru"
address: 0x30D
register_type: read
value_type: U_WORD
unit_of_measurement: "Hz"
accuracy_decimals: 2
device_class: frequency
state_class: measurement
filters:
- multiply: 1
- platform: modbus_controller
modbus_controller_id: $bus_name
#Burner operating hours (High word)
name: "Provozní hodiny hořáku (High word)"
id: burner_op_hours_HIGH
address: 0x349
internal: True
register_type: read
value_type: U_WORD
accuracy_decimals: 0
- platform: modbus_controller
modbus_controller_id: $bus_name
#Burner operating hours (Low word)
name: "Provozní hodiny hořáku (Low word)"
id: burner_op_hours_LOW
address: 0x34A
internal: True
register_type: read
value_type: U_WORD
accuracy_decimals: 0
- platform: template
#Total burner operating hours = 65535 x High word + Low word
name: "Provozní hodiny hořáku"
update_interval: 60s
lambda: |-
return id(burner_op_hours_HIGH).state * 65535 + id(burner_op_hours_LOW).state;
state_class: TOTAL_INCREASING
device_class: duration
unit_of_measurement: "h"
- platform: modbus_controller
modbus_controller_id: $bus_name
#Maximum heating temperature limit with a resolution of 0.1 °C
name: "Maximální teplota vytápění"
address: 0x400
register_type: read
value_type: S_WORD
unit_of_measurement: "°C"
accuracy_decimals: 1
device_class: temperature
state_class: measurement
filters:
- multiply: 0.1
- platform: modbus_controller
modbus_controller_id: $bus_name
#Minimum heating temperature limit with a resolution of 0.1 °C.
name: "Minimalni teplota vytápění"
address: 0x401
register_type: read
value_type: S_WORD
unit_of_measurement: "°C"
accuracy_decimals: 1
device_class: temperature
state_class: measurement
filters:
- multiply: 0.1
- platform: modbus_controller
modbus_controller_id: $bus_name
#Maximum DHW temperature limit with a resolution of 0.1 °C.
name: "Maximální teplota TUV"
address: 0x402
register_type: read
value_type: S_WORD
unit_of_measurement: "°C"
accuracy_decimals: 1
device_class: temperature
state_class: measurement
filters:
- multiply: 0.1
- platform: modbus_controller
modbus_controller_id: $bus_name
#Minimum DHW temperature limit with a resolution of 0.1 °C.
name: "Minimalni teplota TUV"
address: 0x403
register_type: read
value_type: S_WORD
unit_of_measurement: "°C"
accuracy_decimals: 1
device_class: temperature
state_class: measurement
filters:
- multiply: 0.1
text_sensor:
- platform: modbus_controller
modbus_controller_id: $bus_name
id: boiler_error
icon: mdi:car-brake-low-pressure
name: "Error code"
address: 0x1
raw_encode: HEXBYTES
bitmask: 0
register_type: read
lambda: |-
uint16_t state_val = modbus_controller::word_from_hex_str(x, 0);
return x;
select:
- platform: modbus_controller
use_write_multiple: true
modbus_controller_id: $bus_name
#Boiler control
name: "Ovládání kotle"
id: boiler_control_option
address: 256
entity_category: config
value_type: U_WORD
optionsmap:
#Reserved
"Rezervováno": 0 # BIT 0 ~ 0
#Enable DHW heating
"Povolit ohřev TUV": 2 # BIT 1 ~ DEC=2, HEX=2
#Enable heating and hot water heating
"Povolit vytápění a ohřev TUV": 4 # BIT 2 ~ DEC=4, HEX=4
#Deactivation of the BOOST function
"Deaktivace funkce BOOST": 512 # BIT 9 ~ DEC=512, HEX=200
lambda: |-
if ((x & 0x206) == 0)
return std::string("Rezervováno");
if ((x & 0x206) == 2)
return std::string("Povolit ohřev TUV");
if ((x & 0x206) == 4)
return std::string("Povolit vytápění a ohřev TUV");
if ((x & 0x206) == 512)
return std::string("Deaktivace funkce BOOST");
return {};
write_lambda: |-
uint16_t unmodified = id(boiler_control_raw).state;
uint16_t modified = ((unmodified & ~0x206) | value);
return modified;
number:
- platform: modbus_controller
modbus_controller_id: $bus_name
id: boiler_heating_temperature
use_write_multiple: false
#Setting the heating temperature with a resolution of 0.1 °C
#Example: enter 732 to set the output temperature to 73.2 °C.
name: "Nastavení vytápění"
address: 514
max_value: 85
min_value: 20
value_type: U_WORD
multiply: 10
unit_of_measurement: "°C"
device_class: temperature
- platform: modbus_controller
modbus_controller_id: $bus_name
id: boiler_water_storage_temperature
register_type: holding
use_write_multiple: false
#Setting the DHW temperature with a resolution of 0.1 °C
#Example: write 455 to set the DHW temperature to 45.5 °C.
name: "Nastaveni TUV"
address: 515
max_value: 60
min_value: 10
value_type: U_WORD
multiply: 10
unit_of_measurement: "°C"
device_class: temperature
- platform: modbus_controller
modbus_controller_id: $bus_name
name: "Boiler 512 (heat) RAW"
internal: True
id: boiler_control_heat_num
register_type: holding
address: 512
max_value: 85
min_value: 0
step: 85
switch:
- platform: template
id: do_heater
optimistic: true
restore_mode: RESTORE_DEFAULT_OFF
#Enable heating
name: "Povolit topeni"
lambda: |-
return id(do_heater).state;
climate:
- platform: thermostat
id: boiler_heat_thermostat
on_boot_restore_from: memory
#Heating thermostat
name: "Termostat topení"
icon: mdi:heat-pump
visual:
min_temperature: 16
max_temperature: 25
temperature_step: 0.1
# Sensors
sensor: hass_obyvak_temp
humidity_sensor: hass_obyvak_hum
# Presets
default_preset: startup
preset:
- name: startup
default_target_temperature_low: 19.0
mode: "OFF"
- name: comfort
default_target_temperature_low: 21.0
mode: heat
- name: home
default_target_temperature_low: 20.0
mode: heat
- name: sleep
default_target_temperature_low: 18.4
mode: heat
- name: away
default_target_temperature_low: 16.0
mode: heat
preset_change:
- logger.log: Preset has been changed!
min_heating_off_time: 60s
min_heating_run_time: 60s
min_idle_time: 60s
startup_delay: true
set_point_minimum_differential: 1
heat_overrun: 0.5
heat_deadband: 0.2
heat_action:
- switch.turn_on: do_heater
idle_action:
- switch.turn_off: do_heater
- Setting boiler operating mode is solved using SELECT (id: boiler_control_option)
- Setting DHW temperature is only possible in “Enable DHW heating” mode
- Setting heating temperature is only possible in “Enable heating and hot water heating” mode
- For heating, it is necessary to write the value 85(dec) / 0x0055 hex to the HOLDING register at address 512 on a regular basis (every 20 seconds)
interval:
- interval: 20sec
then:
- if:
condition:
switch.is_on: do_heater
then:
- number.set:
id: boiler_control_heat_num
value: 0x0055
Maybe it will be useful to someone