Brink Flair 325 Heat recovery unit ESPhome modbus integration (~$5)

@Fire_blade
What hardware do you use?
Note that the standard modbus adres from Brink is 20 (hex 0x14) instead of 01 (hex 0x1) in the example

I use the NODE MCU ESP32 with the same rs485 module used in the example on the same pins as the example. I also tried 0x14 with no luck and changed the unit to chanel 1 with 0x1 in the esp code with no luck.
when i log back in to the unit the channel was back at 20 for some reson, so tried again with 0x14 but stil no response
the connection settings are
modbus
chanel: 20
baudrate: 19k2
pairity: even
so it should in theory work but it doesn’t…

I dont know where you live, but i can provide a esp32 (usb3 or micro usb) with modbus print (enri.nl), ttl to 485 board and a 3d printed cabinet to put it in.
I live in the netherlands.

Edit: I made a new design of the PCB with MAX3485 (XY-017) instead of MAX485 and no more CRC Check failures.

1 Like

Hi,
I try to install this addon to my HRU Flair 400 with ESP32 board az-delivery-devkit-v4 and the rs485 modul
I can flash the code to the ESP32 board (I specifie the board type) and I set the communication section on the HRU (Chanel 1 / baudrate: 19k2 / pairity: even)

but when I connect to my HRU I have this logs:


log
[16:15:26][C][ota:097]:   Using Password.
[16:15:26][C][api:138]: API Server:
[16:15:26][C][api:139]:   Address: modbus.local:6053
[16:15:26][C][api:141]:   Using noise encryption: YES
[16:15:26][C][modbus_controller:275]: ModbusController:
[16:15:26][C][modbus_controller:276]:   Address: 0x01
[16:15:26][D][modbus_controller:032]: Modbus command to device=1 register=0x1F40 countdown=0 no response received - removed from send queue
[16:15:27][W][modbus:105]: Modbus CRC Check failed! D248!=90E8
[16:15:28][D][modbus_controller:032]: Modbus command to device=1 register=0xFC0 countdown=0 no response received - removed from send queue
[16:15:28][W][modbus:105]: Modbus CRC Check failed! D248!=7361
[16:15:29][D][modbus_controller:032]: Modbus command to device=1 register=0xFC4 countdown=0 no response received - removed from send queue
[16:15:30][D][modbus_controller:032]: Modbus command to device=1 register=0xFCA countdown=0 no response received - removed from send queue
[16:15:32][D][modbus_controller:032]: Modbus command to device=1 register=0xFCE countdown=0 no response received - removed from send queue
[16:15:33][D][modbus_controller:032]: Modbus command to device=1 register=0xFD2 countdown=0 no response received - removed from send queue
[16:15:34][D][modbus_controller:032]: Modbus command to device=1 register=0xFF1 countdown=0 no response received - removed from send queue
[16:15:56][D][sensor:127]: Rekup Power Difference: Sending state nan W with 1 decimals of accuracy

maybe it is the same problem than @ Fire_blade.

Thanks for your helps

Hi

After all the issue comes from the connection between HRU <-> RS485 <-> ESP. Now it works !
If that helps somebody I maked a schema of my setup:

I am having issues - using this setup, my nodemcu doesn’t read any data even though I can control the unit :cry: . Modbus: Can write to the device but cannot read: no response received Any help would be appreciated!

I used MAX485 and one of them was faulty, resulting in many CRC checks error. Switching the module helped. My final config with MAX485:

esphome:
  name: hrv-controller
  platform: ESP8266
  board: nodemcuv2

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

# Enable Home Assistant API
api:

ota:

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  
  manual_ip:
    static_ip: 192.168.0.19
    gateway: 192.168.0.1
    subnet: 255.255.255.0

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esphome HRV control"
    password: "GvJ6AafkENq6"

captive_portal:

uart:
  id: mod_bus
  tx_pin: D3
  rx_pin: D2
  baud_rate: 19200
  parity: even


modbus:
  id: modbus1
  flow_control_pin: D0
  send_wait_time: 2000ms
  
modbus_controller:
  - id: rekup
    address: 0x14
    modbus_id: modbus1
    setup_priority: -10  
    update_interval: 15s
    

text_sensor:
  - platform: modbus_controller
    modbus_controller_id: rekup
    id: rekup_bypass_status_text
    register_type: read
    address: 4050
    raw_encode: NONE
    name: Rekup Bypass Status
    lambda: |-
      uint16_t int_mode = (data[item->offset] << 8) + data[item->offset+1];
      ESP_LOGD("main","Parsed operation mode int : %d", int_mode);
      std::string mode_str;
      switch (int_mode) {
        case 0:  mode_str = "INITIALIZATING"; break;
        case 1:  mode_str = "OPEN"; break;
        case 2:  mode_str = "CLOSED"; break;
        case 3:  mode_str = "OPEN"; break;
        case 4:  mode_str = "CLOSED"; break;
        default: mode_str = "Unknown"; break;
  
      }
      return mode_str;
    
sensor:
  - platform: modbus_controller
    modbus_controller_id: rekup
    name: "Rekup To House temperature"
    id: rekup_to_house_temp
    register_type: read
    address: 4036
    unit_of_measurement: "°C"
    value_type: S_WORD  
    accuracy_decimals: 1
    filters:
      - multiply: 0.1
    
  - platform: modbus_controller
    modbus_controller_id: rekup
    name: "Rekup To House humidity"
    id: rekup_to_house_humidity
    register_type: read
    address: 4037
    unit_of_measurement: "%"
    value_type: S_WORD  
    accuracy_decimals: 1

  - platform: modbus_controller
    modbus_controller_id: rekup
    name: "Rekup To Outside Temperature"
    id: rekup_to_outside_temp
    register_type: read
    address: 4046
    unit_of_measurement: "°C"
    value_type: S_WORD  
    accuracy_decimals: 1
    filters:
      - multiply: 0.1

  - platform: modbus_controller
    modbus_controller_id: rekup
    name: "Rekup To Outside humidity"
    id: rekup_to_outside_humidity
    register_type: read
    address: 4047
    unit_of_measurement: "%"
    value_type: S_WORD  
    accuracy_decimals: 1

  - platform: modbus_controller
    modbus_controller_id: rekup
    name: "Rekup From Outside Temperature"
    id: rekup_from_outside_temp
    register_type: read
    address: 4081
    unit_of_measurement: "°C"
    value_type: S_WORD  
    accuracy_decimals: 1
    filters:
      - multiply: 0.1    

  - platform: modbus_controller
    modbus_controller_id: rekup
    name: "Rekup Flow Actual Intake"
    id: rekup_prutok_in
    register_type: read
    address: 4032
    unit_of_measurement: "m3/h"
    value_type: S_WORD  

  - platform: modbus_controller
    modbus_controller_id: rekup
    name: "Rekup Flow Actual Exhaust"
    id: rekup_prutok_out
    register_type: read
    address: 4042
    unit_of_measurement: "m3/h"
    value_type: S_WORD  

select:
  - platform: modbus_controller
    modbus_controller_id: rekup  
    name: "Rekup Modbus Control Mode"
    address: 8000
    value_type: S_WORD
    optimistic : TRUE
    optionsmap:
      "Device LCD": 0
      "Modbus Step": 1
      "Modbus Flow": 2


  - platform: modbus_controller
    modbus_controller_id: rekup  
    name: "Rekup Bypass Mode"
    address: 6100
    value_type: S_WORD
    optimistic : TRUE
    optionsmap:
      "Auto": 0
      "Closed": 1
      "Open": 2

switch:
- platform: modbus_controller
  modbus_controller_id: rekup
  name: "Rekup Bypass Boost switch"
  register_type: holding
  address: 6104
  bitmask: 1
  entity_category: config
  icon: "mdi:toggle-switch"

number:
  - platform: modbus_controller
    modbus_controller_id: rekup
    name: "Rekup Modbus step setting"
    id: rekup_step_setting
    register_type: holding
    address: 8001
    value_type: S_WORD      
    min_value: 0
    max_value: 3
    mode: slider

  - platform: modbus_controller
    modbus_controller_id: rekup
    name: "Rekup Modbus flow value"
    id: rekup_prutok_nastaveni
    register_type: holding
    address: 8002
    value_type: S_WORD      
    min_value: 0
    max_value: 400
    mode: slider

  - platform: modbus_controller
    modbus_controller_id: rekup
    name: "Rekup Flow 1"
    id: rekup_flow_1
    register_type: holding
    address: 6001
    unit_of_measurement: "m3/h"
    value_type: S_WORD      
    min_value: 50
    max_value: 400
    mode: slider
    
    
  - platform: modbus_controller
    modbus_controller_id: rekup
    name: "Rekup Flow 2"
    id: rekup_flow_2
    register_type: holding
    address: 6002
    unit_of_measurement: "m3/h"
    value_type: S_WORD      
    min_value: 50
    max_value: 400
    mode: slider
    
  - platform: modbus_controller
    modbus_controller_id: rekup
    name: "Rekup Flow 3"
    id: rekup_flow_3
    register_type: holding
    address: 6003
    unit_of_measurement: "m3/h"
    value_type: S_WORD   
    min_value: 50
    max_value: 400
    mode: slider

  - platform: modbus_controller
    modbus_controller_id: rekup
    name: "Rekup Fan Imbalance Intake"
    id: rekup_imbalance_intake
    register_type: holding
    address: 6035
    unit_of_measurement: "%"
    value_type: S_WORD   
    multiply: 10    
    min_value: -15
    max_value: 15
    mode: slider
      
  - platform: modbus_controller
    modbus_controller_id: rekup
    name: "Rekup Fan Imbalance Exhaust"
    id: rekup_imbalance_exhaust
    register_type: holding
    address: 6036
    unit_of_measurement: "%"
    value_type: S_WORD      
    multiply: 10 
    min_value: -15
    max_value: 15
    mode: slider    

Hello all,
Had a problem with communication as BroutMouton did:
…countdown=0 no response received - removed from send queue
Managed to solve that by changing modbus address to “14” and parity to “even”

but I still have a problem with changing control mode (8000) and step setting (8001)
they are “stuck” on “Modbus Step” and “0”
I can change the values of flow rate 1/2/3 from ESPHome and brink web page, I am reading temperatures and flow rates.
Would appreciate any help as I am out of ideas for now :frowning:
BTW: I can no longer control steps from display or brink web page

esphome:
  name: "esphome-web-brink"

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esphome-Web-Brink"
    password: "5la53ezGV7fx"

captive_portal:

web_server:
  port: 80

uart:
  id: mod_bus
  tx_pin: 16
  rx_pin: 17
  baud_rate: 19200
  stop_bits: 1
  parity: even

modbus:
  id: modbus1

modbus_controller:
  - id: brink
    ## the Modbus device addr.
    address: 0x14
    modbus_id: modbus1
    setup_priority: -10 


sensor:
  - platform: modbus_controller
    modbus_controller_id: brink
    name: "Brink To House temperature"
    id: brink_to_house_temp
    register_type: read
    address: 4036
    unit_of_measurement: "°C"
    value_type: S_WORD  
    accuracy_decimals: 1
    filters:
      - multiply: 0.1
  

select:
  - platform: modbus_controller
    modbus_controller_id: brink
    name: "Brink Modbus Control Mode"
    address: 8000
    value_type: S_WORD
    optimistic : TRUE
    optionsmap:
      "Device LCD": 0
      "Modbus Step": 1
      "Modbus Flow": 2


number:
  - platform: modbus_controller
    modbus_controller_id: brink
    name: "Brink Modbus step setting"
    id: brink_step_setting
    register_type: holding
    address: 8001
    value_type: S_WORD      
    min_value: 0
    max_value: 3
    mode: slider

UPDATE:
after restarting HRU I lost any connectivity, half of unit menu was dissabled.
ESPHome could not read any values from the unit

20:04:35	[D]	[modbus_controller.select:048]	
Found value 1 for option 'Modbus Step'
20:04:35	[D]	[select:015]	
'Brink Modbus Control Mode': Sending state Modbus Step (index 1)
20:04:36	[D]	[modbus_controller:032]	
Modbus command to device=20 register=0x1F40 countdown=0 no response received - removed from send queue
20:04:45	[D]	[modbus_controller:032]	
Modbus command to device=20 register=0x1F40 countdown=0 no response received - removed from send queue
20:04:46	[D]	[modbus_controller:032]	
Modbus command to device=20 register=0xFC4 countdown=0 no response received - removed from send queue

When I change back communication to InternalBus, restart the unit, connect ESPHome and change communication back to ModBus - connection restores (i can read temperature) but still cannot change “step settings” or “Modbus control mode”
And everything repeats after restart of HRU - no connection with HRU

@kotrfa asked me to share the picture card on my home assistant dashboard, so here it goes:

Picture HA Brink dashboard

Picture-elements card - picture

code for the picture elements card:

type: picture-elements
image: local/images/wtw_icons2.png
elements:
  - type: state-label
    entity: sensor.huidig_inlaatluchtvolume
    style:
      top: 59%
      left: 73%
      color: black
  - type: state-label
    entity: sensor.huidig_uitlaatluchtvolume
    style:
      top: 59%
      left: 27%
      color: black
  - type: state-label
    entity: sensor.binnen_aanzuig_temperatuur
    style:
      top: 20%
      left: 92%
      color: darkred
  - type: state-label
    entity: sensor.binnen_aanzuig_luchtvochtigheid
    style:
      top: 28%
      left: 92%
      color: '#4171b1'
  - type: state-label
    entity: sensor.binnen_uitblaas_temperatuur
    style:
      top: 77%
      left: 92%
      color: darkred
  - type: state-label
    entity: sensor.binnen_uitblaas_luchtvochtigheid
    style:
      top: 85%
      left: 92%
      color: '#4171b1'
  - type: state-label
    entity: sensor.buiten_aanzuig_temperatuur
    style:
      top: 20%
      left: 14.5%
      color: '#4171b1'
  - type: state-label
    entity: sensor.buiten_humidity
    style:
      top: 26%
      left: 14.5%
      color: '#4171b1'
  - type: state-label
    entity: sensor.buiten_uitblaas_temperatuur
    style:
      top: 77%
      left: 14.5%
      color: '#4171b1'
  - type: state-label
    entity: sensor.buiten_uitblaas_luchtvochtigheid
    style:
      top: 85%
      left: 14.5%
      color: '#4171b1'
  - type: state-label
    entity: sensor.bypass_status
    prefix: 'Bypass: '
    style:
      top: 26%
      left: 50%
      color: black
  - type: state-label
    entity: sensor.rendement
    prefix: 'η= '
    style:
      top: 72%
      left: 50%
      color: black
  - type: state-label
    entity: sensor.filter_wissel_in
    prefix: 'Filterwissel in '
    suffix: agen
    style:
      top: 92%

I use a DHT22 for the suction house side, so this temperature and humidity is normally not available.
Sorry, most of it is in Dutch language.
Also i calculate dewpoint from this suction temperature and humidity with esphome code
And the “performance” (rendement in dutch) in the picture card is also a calculation between suction side house, exhaust side house and suction side outside temperature

Here is the complete code for the vertical stack including the 4 ventilation stands.

type: vertical-stack
cards:
  - type: picture-elements
    image: local/images/wtw_icons2.png
    elements:
      - type: state-label
        entity: sensor.huidig_inlaatluchtvolume
        style:
          top: 59%
          left: 73%
          color: black
      - type: state-label
        entity: sensor.huidig_uitlaatluchtvolume
        style:
          top: 59%
          left: 27%
          color: black
      - type: state-label
        entity: sensor.binnen_aanzuig_temperatuur
        style:
          top: 20%
          left: 92%
          color: darkred
      - type: state-label
        entity: sensor.binnen_aanzuig_luchtvochtigheid
        style:
          top: 28%
          left: 92%
          color: '#4171b1'
      - type: state-label
        entity: sensor.binnen_uitblaas_temperatuur
        style:
          top: 77%
          left: 92%
          color: darkred
      - type: state-label
        entity: sensor.binnen_uitblaas_luchtvochtigheid
        style:
          top: 85%
          left: 92%
          color: '#4171b1'
      - type: state-label
        entity: sensor.buiten_aanzuig_temperatuur
        style:
          top: 20%
          left: 14.5%
          color: '#4171b1'
      - type: state-label
        entity: sensor.buiten_humidity
        style:
          top: 26%
          left: 14.5%
          color: '#4171b1'
      - type: state-label
        entity: sensor.buiten_uitblaas_temperatuur
        style:
          top: 77%
          left: 14.5%
          color: '#4171b1'
      - type: state-label
        entity: sensor.buiten_uitblaas_luchtvochtigheid
        style:
          top: 85%
          left: 14.5%
          color: '#4171b1'
      - type: state-label
        entity: sensor.bypass_status
        prefix: 'Bypass: '
        style:
          top: 26%
          left: 50%
          color: black
      - type: state-label
        entity: sensor.rendement
        prefix: 'η= '
        style:
          top: 72%
          left: 50%
          color: black
      - type: state-label
        entity: sensor.filter_wissel_in
        prefix: 'Filterwissel in '
        suffix: agen
        style:
          top: 92%
          left: 50%
          color: black
  - type: horizontal-stack
    cards:
      - type: custom:button-card
        name: Afwezig
        styles:
          card:
            - height: 150px
        icon: mdi:fan-auto
        size: 75%
        show_state: true
        state:
          - operator: template
            value: |
              [[[
                return states['number.ventilatie_stand'].state == "0.0"
              ]]]
            color: darkcyan
        tap_action:
          action: call-service
          service: number.set_value
          service_data:
            value: 0
            entity_id: number.ventilatie_stand
      - type: custom:button-card
        name: Stand1
        styles:
          card:
            - height: 150px
        icon: mdi:fan-speed-1
        size: 75%
        show_state: true
        state:
          - operator: template
            value: |
              [[[
                return states['number.ventilatie_stand'].state == "1.0"
              ]]]
            color: darkcyan
          - operator: default
        tap_action:
          action: call-service
          service: number.set_value
          service_data:
            value: 1
            entity_id: number.ventilatie_stand
      - type: custom:button-card
        name: Stand2
        styles:
          card:
            - height: 150px
        icon: mdi:fan-speed-2
        size: 75%
        show_state: true
        state:
          - operator: template
            value: |
              [[[
                return states['number.ventilatie_stand'].state == "2.0"
              ]]]
            color: darkcyan
        tap_action:
          action: call-service
          service: number.set_value
          service_data:
            value: 2
            entity_id: number.ventilatie_stand
      - type: custom:button-card
        name: Stand3
        styles:
          card:
            - height: 150px
        icon: mdi:fan-speed-3
        size: 75%
        show_state: true
        state:
          - operator: template
            value: |
              [[[
                return states['number.ventilatie_stand'].state == "3.0"
              ]]]
            color: darkcyan
        tap_action:
          action: call-service
          service: number.set_value
          service_data:
            value: 3
            entity_id: number.ventilatie_stand
  - type: entity
    entity: sensor.power_14
    state_color: false
    name: power
    icon: mdi:power-socket-eu
  - type: history-graph
    hours_to_show: 24
    entities:
      - entity: sensor.woonkamer_co2
      - entity: sensor.slaapkamer_co2
      - entity: sensor.badkamer_co2
      - entity: sensor.zolder_co2
4 Likes

Very tempting; that it screams for more details:

Would like to know more on how/where you placed the DHT22 in the system tubes.

Also interested in your calculations: I got no further then a Delta Temperature between Fresh air and Exhaust air, but you did much more; Congratulations!

If (translation) this is too specific, DM in Dutch is ook goed :wink:

1 Like

@ggaljoen
The DHT-22 (from dfrobot) just basicly hangs in the inlet. (wires under the flexible hose)
ESP32+DHT22

sensor:
  # extra DHT22 sensor on the house suction side as it is not present in the Brink Flair.
  - platform: dht
    model: DHT22
    pin: 22
    temperature:
      name: Binnen aanzuig temperatuur
      id: temp_from_inside
    humidity:
      name: Binnen aanzuig luchtvochtigheid
      id: humidity_from_inside
    update_interval: 15s

I have ordered now a C02 sensor from df robot, which has also humidity and temperature
Gravity: SCD41 Infrared CO2 Sensor I2C - DFRobot

#  - platform: scd4x
#    co2:
#      name: "CO2"
#      id: co2_from_inside
#    temperature:
#      name: Binnen aanzuig temperatuur
#      id: temp_from_inside
#    humidity:
#      name: Binnen aanzuig luchtvochtigheid
#      id: humidity_from_inside

But then it is not ~$5 anymore :wink:

Here is the code for performance (rendement):

sensor:
  - platform: template
    name: Rendement
    id: performance
    unit_of_measurement: "%"
    update_interval: 30s
    lambda: 'return ((id(temp_to_inside).state - id(temp_from_outside).state)/(id(temp_from_inside).state - id(temp_from_outside).state)*100);'
   # (temp_to_inside - temp_from_outside) / (temp_from_inside - temp_from_outside)

And here the code for dewpoint

sensor:
  - platform: template
    name: Binnen dauwpunt
    id: dewpoint_to_inside
    lambda: |-
        return (243.5*(log(id(humidity_to_inside).state/100)+((17.67*id(temp_to_inside).state)/
        (243.5+id(temp_to_inside).state)))/(17.67-log(id(humidity_to_inside).state/100)-
        ((17.67*id(temp_to_inside).state)/(243.5+id(temp_to_inside).state))));
    unit_of_measurement: °C
    update_interval: 15s
    icon: 'mdi:thermometer-water'

I hope its clear, otherwise i need to put the complete code online, but that is not equal to the TS because of the DHT22

1 Like

Guys, any ideas?
Maybe I misunderstood the installation instructions?
Help would be highly appreciated :slight_smile:
Thanks

I have tried more things - updates flair main board firmware, updated control panel firmware,
tried another ESP32 board, tried anothe UART to RS485 bord - no luck as the issue was in another place:

I’m using a BRINK PLUS board. And connected ESP32 to its ModBus port
Settings regarding communication on flair are applied after reboot, that is why after reboot of HRU I lost connectivity when communication type was set to ModBus.
I have changed bus type back to “InternalBus” (bound rate 19k2, parity: even) and everything started to work properly.

Could you please share what is hiding behind(how do you calculate):
sensor.filter_wissel_in

I will share my code, so you can look for yourself

1 Like

Thanks for the code,
did you buy brink “RHT Humidity Sensor”? It is insanely expensive :astonished:
I’m asking as i see that you have declared sensor on 4083

Yes, i have bought this RH sensor from Brink.

I also have had a DHT22 on the house suction side, because brink has no information on that.
I needed that to calculate performance “rendement”
Now i have bought a CO2 sensor from DFrobot, with RH and temperature to readout through I2C
Also works fine on the same esp32 (MH-ET-LIVE)

1 Like

Thanks for this great thread. I managed to set this up aswell. Im using a wemos d1 as mentioned above but instead of connecting it to the usb on the from i got a power shield and connect it to the 24v from the flair 300

nice indeed, though I am lost a bit in the latest state of affairs…

did anyone ever contact the devs at Brink at all? Seems they are always a bit reluctant to join modern technologies and are now implementing their own closed system.

Would be nice if they opened up their api’s (if at all available), and would help create an integration/add-on of sorts…

Having all Brink devices here, that would certainly be most welcome…

I did try contacting them few years ago.
Did not get answer from them. Instead - got an email from local representatives (tenko). They just sent a modbus documentation.