Skip to content

Instantly share code, notes, and snippets.

@cliftonc
Last active March 20, 2024 20:49
Show Gist options
  • Save cliftonc/4e93b2dc8ff6acb5dbbcd48b1df92224 to your computer and use it in GitHub Desktop.
Save cliftonc/4e93b2dc8ff6acb5dbbcd48b1df92224 to your computer and use it in GitHub Desktop.
Brink Flair 300 via Modbus

This is the setup I am using to publish events for my Brink Flair 300 heat recovery unit. I have a Raspberry Pi 3, with an RS485>USB, connected to the 2 & 3 pin of the X15 input of the HRU. The flow in Node-Red talks locally to the device via modbus, and then connects to an mqtt server that is used for further connection to Home Assistant, this appears to be the most robust solution.

I used this USB stick: Rpi RS485 USB, but I think any of them will work. You do not need to buy the super expensive 'digital adapter' and the app, and with this setup you also do not need the super expensive RF bridge and connectors (though will likely want some physical alternative to this setup to turn it on / off if needed).

It currently provides temperature, set fan speed, actual fan speed, binary sensors for max, medium, normal (same as the 4-way RF switch) and inputs for speed (50-300, +50, -50). I also have another flow connecting this to the output of my air sensor, so when the air quality drops or it detects us cooking it turns the ventilation on!

Huge thanks to https://github.com/sirjackal/brink-modbus as this was essential in making it all work and getting the steer towards Modbus being the way to connect.

Connection to Brink Flair

Use the 2 & 3 connections to the red X15 (note that mine came with a removable red plug already in it - so I just needed to connect it).

brink

Example in Home Assistant

small

large

Home Assistant Config:


sensor:
  - platform: mqtt
    unique_id: "ventilation/supply_temperature"
    name: "Ventilation Supply Temperature"
    state_topic: "ventilation/supply_temperature"
    unit_of_measurement: "°C"
    value_template: "{{ value }}"
  - platform: mqtt
    unique_id: "ventilation/outside_temperature"
    name: "Ventilation Outside Temperature"
    state_topic: "ventilation/outside_temperature"
    unit_of_measurement: "°C"
    value_template: "{{ value }}"
  - platform: mqtt
    unique_id: "ventilation/flow_speed"
    name: "Ventilation Flow Speed"
    state_topic: "ventilation/flow_speed"
    unit_of_measurement: "m3"
    value_template: "{{ value }}"
  - platform: mqtt
    unique_id: "ventilation/set_flow_speed"
    name: "Ventilation Set Flow Speed"
    state_topic: "ventilation/set_flow_speed"
    unit_of_measurement: "m3"
    value_template: "{{ value }}"
binary_sensor:
  - platform: mqtt
    unique_id: "ventilation/speed_normal"
    name: "Ventilation Normal Speed"
    payload_on: "on"
    payload_off: "off"
    state_topic: "ventilation/speed_normal"
    value_template: "{{ value_json.payload }}"
  - platform: mqtt
    unique_id: "ventilation/speed_medium"
    name: "Ventilation Medium Speed"
    payload_on: "on"
    payload_off: "off"
    state_topic: "ventilation/speed_medium"
    value_template: "{{ value_json.payload }}"
  - platform: mqtt
    unique_id: "ventilation/speed_max"
    name: "Ventilation Max Speed"
    payload_on: "on"
    payload_off: "off"
    state_topic: "ventilation/speed_max"
    value_template: "{{ value_json.payload }}"
switch:
  - platform: mqtt
    unique_id: fan_max_switch
    name: "Ventilation"
    state_topic: "ventilation/speed_max"
    command_topic: "ventilation/air_flow"
    payload_on: 300
    payload_off: 50
    state_on: "on"
    state_off: "off"
    optimistic: false
    qos: 0
    retain: true

sensor:
- platform: mqtt
unique_id: "ventilation/supply_temperature"
name: "Ventilation Supply Temperature"
state_topic: "ventilation/supply_temperature"
unit_of_measurement: "°C"
value_template: "{{ value }}"
- platform: mqtt
unique_id: "ventilation/outside_temperature"
name: "Ventilation Outside Temperature"
state_topic: "ventilation/outside_temperature"
unit_of_measurement: "°C"
value_template: "{{ value }}"
- platform: mqtt
unique_id: "ventilation/flow_speed"
name: "Ventilation Flow Speed"
state_topic: "ventilation/flow_speed"
unit_of_measurement: "m3"
value_template: "{{ value }}"
- platform: mqtt
unique_id: "ventilation/set_flow_speed"
name: "Ventilation Set Flow Speed"
state_topic: "ventilation/set_flow_speed"
unit_of_measurement: "m3"
value_template: "{{ value }}"
binary_sensor:
- platform: mqtt
unique_id: "ventilation/speed_normal"
name: "Ventilation Normal Speed"
payload_on: "on"
payload_off: "off"
state_topic: "ventilation/speed_normal"
value_template: "{{ value_json.payload }}"
- platform: mqtt
unique_id: "ventilation/speed_medium"
name: "Ventilation Medium Speed"
payload_on: "on"
payload_off: "off"
state_topic: "ventilation/speed_medium"
value_template: "{{ value_json.payload }}"
- platform: mqtt
unique_id: "ventilation/speed_max"
name: "Ventilation Max Speed"
payload_on: "on"
payload_off: "off"
state_topic: "ventilation/speed_max"
value_template: "{{ value_json.payload }}"
switch:
- platform: mqtt
unique_id: fan_max_switch
name: "Ventilation"
state_topic: "ventilation/speed_max"
command_topic: "ventilation/air_flow"
payload_on: 300
payload_off: 50
state_on: "on"
state_off: "off"
optimistic: false
qos: 0
retain: true
[
{
"id":"61755636.844f88",
"type":"tab",
"label":"Living Area",
"disabled":false,
"info":""
},
{
"id":"720e809d.ed139",
"type":"modbus-read",
"z":"61755636.844f88",
"name":"Supply Temperature",
"topic":"",
"showStatusActivities":false,
"logIOActivities":false,
"showErrors":false,
"unitid":"20",
"dataType":"InputRegister",
"adr":"4036",
"quantity":"1",
"rate":"1",
"rateUnit":"m",
"delayOnStart":false,
"startDelayTime":"",
"server":"9270f483.3edde8",
"useIOFile":false,
"ioFile":"",
"useIOForPayload":false,
"emptyMsgOnFail":false,
"x":170,
"y":100,
"wires":[
[
],
[
"4f132360.b4a2ec"
]
]
},
{
"id":"1cccf2a4.6b6bdd",
"type":"mqtt out",
"z":"61755636.844f88",
"name":"",
"topic":"ventilation/supply_temperature",
"qos":"",
"retain":"",
"broker":"1b1e9157.0451ff",
"x":690,
"y":100,
"wires":[
]
},
{
"id":"4f132360.b4a2ec",
"type":"change",
"z":"61755636.844f88",
"name":"",
"rules":[
{
"t":"set",
"p":"payload",
"pt":"msg",
"to":"$round(msg.payload.data[0]/10,1)\t",
"tot":"jsonata"
}
],
"action":"",
"property":"",
"from":"",
"to":"",
"reg":false,
"x":420,
"y":100,
"wires":[
[
"1cccf2a4.6b6bdd"
]
]
},
{
"id":"8cc7472a.b9b3d8",
"type":"mqtt out",
"z":"61755636.844f88",
"name":"",
"topic":"ventilation/outside_temperature",
"qos":"",
"retain":"",
"broker":"1b1e9157.0451ff",
"x":690,
"y":240,
"wires":[
]
},
{
"id":"1a6cdbbe.9c4a24",
"type":"change",
"z":"61755636.844f88",
"name":"",
"rules":[
{
"t":"set",
"p":"payload",
"pt":"msg",
"to":"$round(msg.payload.data[0]/10,1)\t",
"tot":"jsonata"
}
],
"action":"",
"property":"",
"from":"",
"to":"",
"reg":false,
"x":420,
"y":240,
"wires":[
[
"8cc7472a.b9b3d8"
]
]
},
{
"id":"57495b84.6dcf24",
"type":"modbus-read",
"z":"61755636.844f88",
"name":"Outside Temperature",
"topic":"",
"showStatusActivities":false,
"logIOActivities":false,
"showErrors":false,
"unitid":"20",
"dataType":"InputRegister",
"adr":"4046",
"quantity":"1",
"rate":"1",
"rateUnit":"m",
"delayOnStart":false,
"startDelayTime":"",
"server":"9270f483.3edde8",
"useIOFile":false,
"ioFile":"",
"useIOForPayload":false,
"emptyMsgOnFail":false,
"x":180,
"y":240,
"wires":[
[
],
[
"1a6cdbbe.9c4a24"
]
]
},
{
"id":"f5f4a63f.912878",
"type":"mqtt in",
"z":"61755636.844f88",
"name":"Air Flow",
"topic":"ventilation/air_flow",
"qos":"2",
"datatype":"utf8",
"broker":"1b1e9157.0451ff",
"x":120,
"y":580,
"wires":[
[
"2ebb3f58.2a4e5"
]
]
},
{
"id":"8c91642d.716a78",
"type":"modbus-read",
"z":"61755636.844f88",
"name":"Flow Speed",
"topic":"",
"showStatusActivities":false,
"logIOActivities":false,
"showErrors":false,
"unitid":"20",
"dataType":"InputRegister",
"adr":"4042",
"quantity":"1",
"rate":"5",
"rateUnit":"s",
"delayOnStart":false,
"startDelayTime":"",
"server":"9270f483.3edde8",
"useIOFile":false,
"ioFile":"",
"useIOForPayload":false,
"emptyMsgOnFail":false,
"x":150,
"y":340,
"wires":[
[
],
[
"49b645c0.5346dc"
]
]
},
{
"id":"1ae9143b.306cac",
"type":"mqtt out",
"z":"61755636.844f88",
"name":"",
"topic":"ventilation/flow_speed",
"qos":"",
"retain":"",
"broker":"1b1e9157.0451ff",
"x":660,
"y":340,
"wires":[
]
},
{
"id":"49b645c0.5346dc",
"type":"change",
"z":"61755636.844f88",
"name":"Set Flow Speed",
"rules":[
{
"t":"set",
"p":"payload",
"pt":"msg",
"to":"msg.payload.data[0]",
"tot":"jsonata"
}
],
"action":"",
"property":"",
"from":"",
"to":"",
"reg":false,
"x":380,
"y":340,
"wires":[
[
"1ae9143b.306cac"
]
]
},
{
"id":"bbc52552.f5b068",
"type":"mqtt in",
"z":"61755636.844f88",
"name":"Air Flow Up",
"topic":"ventilation/air_flow_up",
"qos":"2",
"datatype":"utf8",
"broker":"1b1e9157.0451ff",
"x":130,
"y":660,
"wires":[
[
"8f6dad8b.0fe15"
]
]
},
{
"id":"19188630.29e82a",
"type":"mqtt in",
"z":"61755636.844f88",
"name":"Air Flow Down",
"topic":"ventilation/air_flow_down",
"qos":"2",
"datatype":"utf8",
"broker":"1b1e9157.0451ff",
"x":140,
"y":740,
"wires":[
[
"11ef7c32.1e1cb4"
]
]
},
{
"id":"8f6dad8b.0fe15",
"type":"change",
"z":"61755636.844f88",
"name":"Add Speed",
"rules":[
{
"t":"set",
"p":"payload",
"pt":"msg",
"to":"$min([$flowContext('speed')+50,300])\t",
"tot":"jsonata"
}
],
"action":"",
"property":"",
"from":"",
"to":"",
"reg":false,
"x":330,
"y":660,
"wires":[
[
"2ebb3f58.2a4e5"
]
]
},
{
"id":"11ef7c32.1e1cb4",
"type":"change",
"z":"61755636.844f88",
"name":"Drop Speed",
"rules":[
{
"t":"set",
"p":"payload",
"pt":"msg",
"to":"$max([$flowContext('speed')-50,50])\t",
"tot":"jsonata"
}
],
"action":"",
"property":"",
"from":"",
"to":"",
"reg":false,
"x":330,
"y":740,
"wires":[
[
"2ebb3f58.2a4e5"
]
]
},
{
"id":"7cdeed74.8dd7a4",
"type":"function",
"z":"61755636.844f88",
"name":"Store Speed",
"func":"var speed = msg.payload;\nvar speedSetting = 'normal';\nflow.set('speed', speed)\nif (speed >= 150) {\n speedSetting = 'medium';\n}\nif (speed === 300) {\n speedSetting = 'max';\n}\nreturn { payload: speedSetting };",
"outputs":1,
"noerr":0,
"initialize":"",
"finalize":"",
"x":630,
"y":500,
"wires":[
[
"c6eaf3a8.a784c"
]
]
},
{
"id":"18799ec0.a2c261",
"type":"modbus-read",
"z":"61755636.844f88",
"name":"Set Flow Speed",
"topic":"",
"showStatusActivities":false,
"logIOActivities":false,
"showErrors":false,
"unitid":"20",
"dataType":"InputRegister",
"adr":"4031",
"quantity":"1",
"rate":"5",
"rateUnit":"s",
"delayOnStart":false,
"startDelayTime":"",
"server":"9270f483.3edde8",
"useIOFile":false,
"ioFile":"",
"useIOForPayload":false,
"emptyMsgOnFail":false,
"x":160,
"y":460,
"wires":[
[
],
[
"3f784458.59c1cc"
]
]
},
{
"id":"3f784458.59c1cc",
"type":"change",
"z":"61755636.844f88",
"name":"Set Flow Speed",
"rules":[
{
"t":"set",
"p":"payload",
"pt":"msg",
"to":"msg.payload.data[0]",
"tot":"jsonata"
}
],
"action":"",
"property":"",
"from":"",
"to":"",
"reg":false,
"x":380,
"y":460,
"wires":[
[
"7cdeed74.8dd7a4",
"bf904aa6.1d1f48"
]
]
},
{
"id":"bf904aa6.1d1f48",
"type":"mqtt out",
"z":"61755636.844f88",
"name":"",
"topic":"ventilation/set_flow_speed",
"qos":"",
"retain":"",
"broker":"1b1e9157.0451ff",
"x":680,
"y":440,
"wires":[
]
},
{
"id":"c6eaf3a8.a784c",
"type":"switch",
"z":"61755636.844f88",
"name":"Switch Type",
"property":"payload",
"propertyType":"msg",
"rules":[
{
"t":"eq",
"v":"normal",
"vt":"str"
},
{
"t":"eq",
"v":"medium",
"vt":"str"
},
{
"t":"eq",
"v":"max",
"vt":"str"
}
],
"checkall":"true",
"repair":false,
"outputs":3,
"x":810,
"y":500,
"wires":[
[
"96b351a9.1671e",
"c6f87409.423468",
"ab7d39fb.44db08"
],
[
"18669241.41cbae",
"c6f87409.423468",
"97080657.caeb18"
],
[
"18669241.41cbae",
"96b351a9.1671e",
"204894a1.6b58dc"
]
]
},
{
"id":"382ecb3a.3e2bd4",
"type":"mqtt out",
"z":"61755636.844f88",
"name":"Normal",
"topic":"ventilation/speed_normal",
"qos":"",
"retain":"",
"broker":"1b1e9157.0451ff",
"x":1280,
"y":360,
"wires":[
]
},
{
"id":"d961de3e.ee334",
"type":"mqtt out",
"z":"61755636.844f88",
"name":"Medium",
"topic":"ventilation/speed_medium",
"qos":"",
"retain":"",
"broker":"1b1e9157.0451ff",
"x":1280,
"y":460,
"wires":[
]
},
{
"id":"28c61ab0.d190b6",
"type":"mqtt out",
"z":"61755636.844f88",
"name":"Max",
"topic":"ventilation/speed_max",
"qos":"",
"retain":"",
"broker":"1b1e9157.0451ff",
"x":1270,
"y":540,
"wires":[
]
},
{
"id":"18669241.41cbae",
"type":"change",
"z":"61755636.844f88",
"name":"Normal Off",
"rules":[
{
"t":"set",
"p":"payload",
"pt":"msg",
"to":"off",
"tot":"str"
}
],
"action":"",
"property":"",
"from":"",
"to":"",
"reg":false,
"x":1070,
"y":340,
"wires":[
[
"382ecb3a.3e2bd4"
]
]
},
{
"id":"96b351a9.1671e",
"type":"change",
"z":"61755636.844f88",
"name":"Medium Off",
"rules":[
{
"t":"set",
"p":"payload",
"pt":"msg",
"to":"off",
"tot":"str"
}
],
"action":"",
"property":"",
"from":"",
"to":"",
"reg":false,
"x":1070,
"y":440,
"wires":[
[
"d961de3e.ee334"
]
]
},
{
"id":"c6f87409.423468",
"type":"change",
"z":"61755636.844f88",
"name":"Max Off",
"rules":[
{
"t":"set",
"p":"payload",
"pt":"msg",
"to":"off",
"tot":"str"
}
],
"action":"",
"property":"",
"from":"",
"to":"",
"reg":false,
"x":1060,
"y":549,
"wires":[
[
"28c61ab0.d190b6"
]
]
},
{
"id":"204894a1.6b58dc",
"type":"change",
"z":"61755636.844f88",
"name":"Max On",
"rules":[
{
"t":"set",
"p":"payload",
"pt":"msg",
"to":"on",
"tot":"str"
}
],
"action":"",
"property":"",
"from":"",
"to":"",
"reg":false,
"x":1060,
"y":580,
"wires":[
[
"28c61ab0.d190b6"
]
]
},
{
"id":"97080657.caeb18",
"type":"change",
"z":"61755636.844f88",
"name":"Medium On",
"rules":[
{
"t":"set",
"p":"payload",
"pt":"msg",
"to":"on",
"tot":"str"
}
],
"action":"",
"property":"",
"from":"",
"to":"",
"reg":false,
"x":1070,
"y":471,
"wires":[
[
"d961de3e.ee334"
]
]
},
{
"id":"ab7d39fb.44db08",
"type":"change",
"z":"61755636.844f88",
"name":"Normal On",
"rules":[
{
"t":"set",
"p":"payload",
"pt":"msg",
"to":"on",
"tot":"str"
}
],
"action":"",
"property":"",
"from":"",
"to":"",
"reg":false,
"x":1070,
"y":370,
"wires":[
[
"382ecb3a.3e2bd4"
]
]
},
{
"id":"2ebb3f58.2a4e5",
"type":"modbus-write",
"z":"61755636.844f88",
"name":"Air Flow",
"showStatusActivities":true,
"showErrors":true,
"unitid":"20",
"dataType":"HoldingRegister",
"adr":"8002",
"quantity":"1",
"server":"9270f483.3edde8",
"emptyMsgOnFail":false,
"keepMsgProperties":false,
"x":620,
"y":580,
"wires":[
[
],
[
]
]
},
{
"id":"1702ff05.241cd1",
"type":"modbus-write",
"z":"61755636.844f88",
"name":"Set Mode",
"showStatusActivities":false,
"showErrors":false,
"unitid":"20",
"dataType":"HoldingRegister",
"adr":"8000",
"quantity":"1",
"server":"9270f483.3edde8",
"emptyMsgOnFail":false,
"keepMsgProperties":false,
"x":320,
"y":820,
"wires":[
[
],
[
]
]
},
{
"id":"f4eac99b.f9c888",
"type":"inject",
"z":"61755636.844f88",
"name":"Set Mode",
"props":[
{
"p":"payload"
}
],
"repeat":"",
"crontab":"",
"once":false,
"onceDelay":0.1,
"topic":"",
"payload":"2",
"payloadType":"str",
"x":120,
"y":820,
"wires":[
[
"1702ff05.241cd1"
]
]
},
{
"id":"9270f483.3edde8",
"type":"modbus-client",
"name":"Ventilation",
"clienttype":"serial",
"bufferCommands":true,
"stateLogEnabled":true,
"queueLogEnabled":true,
"tcpHost":"127.0.0.1",
"tcpPort":"502",
"tcpType":"DEFAULT",
"serialPort":"/dev/ttyUSB0",
"serialType":"RTU-BUFFERD",
"serialBaudrate":"19200",
"serialDatabits":"8",
"serialStopbits":"1",
"serialParity":"even",
"serialConnectionDelay":"500",
"unit_id":"20",
"commandDelay":"10",
"clientTimeout":"1000",
"reconnectOnTimeout":true,
"reconnectTimeout":"2000",
"parallelUnitIdsAllowed":false
},
{
"id":"1b1e9157.0451ff",
"type":"mqtt-broker",
"name":"HomeAssistant",
"broker":"192.168.178.207",
"port":"1883",
"clientid":"",
"usetls":false,
"compatmode":false,
"keepalive":"60",
"cleansession":true,
"birthTopic":"",
"birthQos":"0",
"birthPayload":"",
"closeTopic":"",
"closeQos":"0",
"closePayload":"",
"willTopic":"",
"willQos":"0",
"willPayload":""
}
]
#!/usr/bin/env python3
import minimalmodbus
import math
instrument = minimalmodbus.Instrument ('/dev/ttyUSB0', 20) # port name, slave address (in decimal)
instrument.serial.baudrate = 19200 # Baud
instrument.serial.bytesize = 8
instrument.serial.parity = minimalmodbus.serial.PARITY_EVEN
instrument.serial.stopbits = 1
instrument.serial.timeout = 0.5 # seconds
instrument.mode = minimalmodbus.MODE_RTU # rtu or ascii mode
instrument.clear_buffers_before_each_transaction = True
value = instrument.read_register (8001, 0, 3, False) # Registernumber, number of decimals, function code
switchPositionEnum = {0: 'absence', 1: 'low', 2: 'medium', 3: 'high'}
print ('Power level:', value, '(' + switchPositionEnum [value] + ')')
value = instrument.read_register (4036, 0, 4, True)
print ('Supply temperature:', value / 10, 'C')
value = instrument.read_register (4046, 0, 4, True)
print ('Drain temperature:', value / 10, 'C')
value = instrument.read_register (4023, 0, 4, False)
print ('Inlet pressure:', value / 10, 'Pa')
value = instrument.read_register (4024, 0, 4, False)
print ('Outlet pressure:', value / 10, 'Pa')
value = instrument.read_register (4031, 0, 4, False)
print ('Set inlet air volume:', value, 'm3')
value = instrument.read_register (4032, 0, 4, False)
print ('Current inlet air volume:', value, 'm3')
value = instrument.read_register (4041, 0, 4, False)
print ('Output air volume set:', value, 'm3')
value = instrument.read_register (4042, 0, 4, False)
print ('Current air outlet volume:', value, 'm3')
value = instrument.read_register (6100, 0, 3, False)
bypassModeEnum = {0: 'automatic', 1: 'closed', 2: 'open'}
print ('Bypass mode:', bypassModeEnum [value])
value = instrument.read_register (4050, 0, 4, False)
bypassStateEnum = {0: 'initialize', 1: 'open', 2: 'closed', 3: 'open', 4: 'closed', 255: 'error'}
print ('Bypass status:', bypassStateEnum [value])
value = instrument.read_register (4100, 0, 4, False)
filterStateEnum = {0: 'clean', 1: 'spinavy'}
print ('Filter status:', filterStateEnum [value])
value = instrument.read_register (4060, 0, 4, False)
preheaterStateEnum = {0: 'initialize', 1: 'inactive', 2: 'active', 3: 'test mode'}
print ('Preheat status:', preheaterStateEnum [value])
value = instrument.read_register (4061, 0, 4, False)
print ('Preheat performance:', value, '%')
value = instrument.read_register (6033, 0, 3, False)
imbalanceEnum = {0: 'not allowed', 1: 'allowed'}
print ('Unbalanced:', imbalanceEnum [value])
value = instrument.read_register (6035, 0, 3, False)
print ('Supply unbalance:', value, '%')
value = instrument.read_register (6036, 0, 3, False)
print ('Deduction unbalance:', value, '%')
value = instrument.read_register (6000, 0, 3, False)
print ('Flow rate level 0 (absence):', value, 'm3')
value = instrument.read_register (6001, 0, 3, False)
print ('Flow rate level 1 (low):', value, 'm3')
value = instrument.read_register (6002, 0, 3, False)
print ('Flow rate level 2 (middle):', value, 'm3')
value = instrument.read_register (6003, 0, 3, False)
print ('Flow rate level 3 (high):', value, 'm3')
value = instrument.read_register (4115, 0, 4, False)
print ('Apply filter:', math.floor (value / 24), 'days')
@Arc86
Copy link

Arc86 commented Mar 14, 2021

Great, that was the missing piece of the puzzle :) I've got it working now on a Raspberry Pi Zero W. Thank you for your help and your work.

@Turtle-code
Copy link

Ah yes, you need to put the flair into the right mode first, so that you can set the speed directly.

I use mbpoll to do that:

mbpoll -a 20 -b 19200 -r 8000 -t 4 -v -m rtu -d 8 -p even -1 -0 /dev/ttyUSB0 2

This came via: https://github.com/sirjackal/brink-modbus/blob/main/commands.md#ovl%C3%A1d%C3%A1n%C3%AD-modbus

You only ever do this once (not sure if they are powered off if you have to re-do it - so it may be helpful to put it as a startup command into Node-red).
I run in the same issue, unable to set the fan speed.

I tried the modbus command, but i get an error:
[14][06][1F][40][00][02][0C][CE]
Waiting for a confirmation...
<14><86><03><13>
ERROR Illegal data value
Write output (holding) register failed: Illegal data value

Somehow i'm not able to put it in the right mode?

Any clues where to look to fix this? (I'm using a Brink Flair 300)

@Arc86
Copy link

Arc86 commented Apr 11, 2021

I have noticed that you will receive this error if it is already in mode 2. You can try this by executing the command with 0 or 1 at the end. You’ll notice you won’t get an error. Now do the command again with 2 at the end and it will work without errors. If you would execute that command again with 2 at the end you’ll receive that error because it’s already in that mode.

now why you can’t control fan speed, I have noticed when I used the IR Remote and it is on 2nd fan mode (200rpm). The modbus command get unresponsive. The commands do get through, no errors however the fan doest respond. Putting it back on lowest RPM mode on IR Remote and it becomes responsive again.

Finally if for whatever reason the power is cut from the Brink machine you do need to apply the command stated above again. It automatically reverts back to mode 0 after power outage.

@Turtle-code
Copy link

I have noticed that you will receive this error if it is already in mode 2. You can try this by executing the command with 0 or 1 at the end. You’ll notice you won’t get an error. Now do the command again with 2 at the end and it will work without errors. If you would execute that command again with 2 at the end you’ll receive that error because it’s already in that mode.

now why you can’t control fan speed, I have noticed when I used the IR Remote and it is on 2nd fan mode (200rpm). The modbus command get unresponsive. The commands do get through, no errors however the fan doest respond. Putting it back on lowest RPM mode on IR Remote and it becomes responsive again.

Finally if for whatever reason the power is cut from the Brink machine you do need to apply the command stated above again. It automatically reverts back to mode 0 after power outage.

Thanks for the extra info, so it seems it is already in mode 2. (if i switch to mode 1, i can switch back to mode 2)
I don't use an IR Remote, but have been using the Brink Home app, maybe that is interfering with the modbus commands.
I'll have a look later today.

@Turtle-code
Copy link

An update from my side:
Somehow my unit does not seem to respond to flow rate settings when set to mode 2. (through register 8002, while 8000 is set to 2)
I do have Co2 sensors attached to the unit; so maybe these are interfering with setting the flow rate.
When the system is idle; flow rate varies between 120 (during day) and 220 (at night).

When i set the unit to mode 1 for switch control, i am able to increase flow rate when i put it into switch position 3. (set register 8001 to 3)
That's what i'm using now when i need to increase flow speed when someone is showering at home.

@hnykda
Copy link

hnykda commented Aug 26, 2021

Thanks a lot! I followed most of it over here (feel free to steal some sensors: https://gist.github.com/hnykda/bbd1bc3b81bd5ab62efac1bda9547943 for example). HA post here

@brisc
Copy link

brisc commented Sep 2, 2021

Thanks a lot. I followed this and @hnykda post and it works great

@Nobeernogman
Copy link

Hi, i have a few questions.

  1. I copy'd your configuration.yaml, and i have this in home-assistant: https://i.imgur.com/laN98X0.png How can i make it that i can control the ventilation speed in home-assistant?

  2. What's the correct way to manual set the ventilation speed? I also use 2 mechanical switches, one in the kitchen (4 way switch) and one in the shower (3 way switch)

When i switched my kitchen mechanical switch to mode "2", and set for example "150" to ventilation/air_flow it's tied to mechanical switch on mode 2

I want to change fanspeed with mqtt, but not change the fanspeed of the current mode the mechanical switch is currently on. is that possible?

@renssies
Copy link

@Nobeernogman Very late answer, but the brink flair disables any other ways of changing the speed when register 8000 is changed. And changing that register is required to do control over Modbus.

One solution is to read the state of the switch with a Arduino or Raspberry Pi and add it like that. The switch is connected using a RJ11 cable.

@ggaljoen
Copy link

ggaljoen commented Dec 29, 2021

@renssies & @Nobeernogman
Important to know is the highest speed request is performed!
BUT the exception of selection level1; first to choose level1 is served and all other requests will be ignored.
My settings to take control of the system speed with modbus; select preset2 on all controls and that way you can go up or down.
Modbus control is working when you first changed address 8000 to value 1 (control by 8001 preset) or 2 (control by 8002 m³)

@renssies
Copy link

renssies commented Dec 30, 2021

A small heads-up for the people using a Brink Flair with the (optional) plus module or the Brink Fair Plus 200/225/300/400.

These models use a "plus print" (UWA2-E in the image) that communicates with the main PCB using a similar protocol to Modbus.
They use an STM32 to translate everything to a so-called "BrinkBus" for the main PCB.

There are a few things to keep in mind to use this:

  • The ModBus implementation on the plus print is a bit more error-prone, you might need to use a more expensive USB adapter. The one mentioned in the post seemed to drop out every few days. I used this adapter: https://www.antratek.nl/usb-rs485-module
  • You need to use the ModBus connector on the plus print. This is X06 on UWA2-E
  • The jumper behind the ModBus connector (X07) needs to be disconnected (it should come disconnected from the factory)
  • The communication setting in the interface has to be set to BrinkBus, not ModBus. This setting only applies to the ModBus port on the main PCB. When a plus print is installed this port used to communicate with the Plus module. So it should be set to BrinkBus. Modbus is always enabled when using the port on the plus module.
  • If you've changed the parity, slave ID, or baud rate before changing the communication setting to BrinkBus. You might need to change them again. The main PCB and the plus PCB have their own seperate memory and the interface only shows the values of the main PCB.

@GreyEarl
Copy link

In my new built house there will be wired mechanical switches added to the Flair 300. I find them looking outdated and not practical when you try to digitalize and "smartify" your house. So, my plan is to remove the switches and replace them with something else that is connected to Home Assistant.

However, the mechanical switches include a handy led indicator when you need to have the filters changed/cleaned. As far as I can see, the above solution does not include this information. Any other way to retrieve the "cleaning" state?

@hnykda
Copy link

hnykda commented Jan 29, 2022

Filter status is on modbus register 4100, you can just read it. 0 is clean, 1 is dirty. Then in home assistant it's the same as for other readers + you might set up a notification when it changes into "dirty".

@GreyEarl
Copy link

GreyEarl commented Feb 1, 2022

Great! Thank you very much! @hnykda

@ajpohv
Copy link

ajpohv commented Feb 2, 2022

@renssies I searched for days...you're posted solves it. Thank you!
I have a Flair 400 Plus. The installer replaced and installed the PCB's in december...and didn't do there job. Wrong installed jumbers and settings. With this post, I got it fixed. Finally!

@hnykda
Copy link

hnykda commented Feb 13, 2022

The modbus.py in this gist is more of an example than anything. You don't really "install" it, it's just to show how you can read/set values from the HRV via python. In the most simplest case, you would just do python modbus.py

@jant90
Copy link

jant90 commented Apr 14, 2022

I'm just wondering: have you considered using ESPHome instead? Do you think it could work? @renssies, @cliftonc and @hnykda seem experienced in this field, maybe one of have any idea?

In their October 2021 update they added the modbus controller component so I think it should be possible (however I'm not familiar with modbus at all, I've never used it).

What I like about this approach is that you don't need an expensive RPi (just an ESP32 and RS485 module which should be less than 5 bucks), and the ESP32 can be powered directly from the Flair USB port too. Because all modbus stuff is taken care off by ESPHome on the ESP32 chip itself there are less points of failure (no MQTT broker or Node-RED required), and ESPHome nicely integrates into Home Assistant automatically. Lastly, this would be very nice for inexperienced users too, just flash ESPHome from a premade yaml file and connect 2 wires.

Anyways, I've ordered an RS485 and I will probably play around with it a bit when it comes. :)

@hnykda
Copy link

hnykda commented Apr 14, 2022

@jant90 I did try that, brief mention here, but it was before the component was officially out and I didn't make it work (I did use the component code, but I think it might be in better shape now). It simply didn't send the values 🤷 . Read the comments in the thread for a bit more info.

I would be interested in how it goes for you and would consider replacing my RPi. However, y current NodeRed solution on RPi (same link to the last one) has a few nice things built in that might be be slightly harder to get via raw esphome I think (like reset, some light message preprocessing, handling negative values, mapping values, etc.) which would have to be on HA side or done via ESPHome magic (which is not terrible, but also not that straightforward). If you try rewriting it, I recommend checking my current nodered configs to make it easier for you.

@jant90
Copy link

jant90 commented Apr 15, 2022

@hnykda cool, thanks for replying so quickly! Another way around using the RPi might be to run a "serial server" using ESPHome on the ESP32 and connect that to the modbus interface, not sure if that's possible but modbus is just a serial interface right? Next, Node-RED should be able to connect to the serial server over TCP (instead of using a local serial device).

@hnykda
Copy link

hnykda commented Apr 22, 2022

Yep, that could work nicely. I tried that as well and it didn't work. But please, let me know if it does work for you.

@KjellVerb
Copy link

Hi, how are all the jumpers configured for you? I don't have a plus print but nothing gets my ModBus working

@renssies
Copy link

renssies commented Oct 8, 2022

On my system no jumpers were changed, it was installed out of the box and I then connected to the Modbus port on the plus print and enabled brink bus (it is Modbus if you don’t have the plus print).

@KjellVerb
Copy link

Thanks for your reply. Unfortunately while debugging I have already tried many permutations of placing and removing the jumpers and now I don't remember their original out-of-the-box position.

@GreyEarl
Copy link

What does this mean? I try to get in the correct mode to adjust/set speed:

pi@brink-flair300:~ $ mbpoll -a 20 -b 19200 -r 8000 -t 4 -v -m rtu -d 8 -p even -1 -0 /dev/ttyUSB0 2
debug enabled
Set mode=rtu
Set rtu data bits=8
Set device=/dev/ttyUSB0
1 write data have been found
Set data=2
Word[0]=0x2
mbpoll 1.4-12 - FieldTalk(tm) Modbus(R) Master Simulator
Copyright © 2015-2019 Pascal JEAN, https://github.com/epsilonrt/mbpoll
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions; type 'mbpoll -w' for details.

Opening /dev/ttyUSB0 at 19200 bauds (E, 8, 1)
Set response timeout to 1 sec, 0 us
Protocol configuration: Modbus RTU
Slave configuration...: address = [20]
                        start reference = 8000, count = 1
Communication.........: /dev/ttyUSB0,      19200-8E1
                        t/o 1.00 s, poll rate 1000 ms
Data type.............: 16-bit register, output (holding) register table

[14][06][1F][40][00][02][0C][CE]
Waiting for a confirmation...
ERROR Connection timed out: select
Write output (holding) register failed: Connection timed out

@GreyEarl
Copy link

GreyEarl commented Dec 18, 2022

Answering my own question. There was an interference, because I left the physical (wired) switched connected. I disconnected, now there are no issues. Afterwards I saw there is a warning message on the page of sirjackal (translated) regarding the use of modbus in combination of the physical switch.

Also note, in the samples of cliftonc there is a mentioning of outside temperature in the Node Red flows. However, the flow reads a different register, which is exhauste temperate. This is (obviously) not the same.

Exhaust air temperature (degrees C, divide by 10):
mbpoll -a 20 -b 19200 -r 4046 -t 3 -c 1 -v -m rtu -d 8 -p even -1 -0 /dev/ttyUSB0

Outdoor temperature (degrees C, divide by 10):
 mbpoll -a 20 -b 19200 -r 4081 -t 3 -c 1 -v -m rtu -d 8 -p even -1 -0 /dev/ttyUSB0

@GreyEarl
Copy link

GreyEarl commented Feb 7, 2023

I notice sometimes getting weird temperature reading. @hnykda do you have an idea?
image

@hnykda
Copy link

hnykda commented Feb 7, 2023

@GreyEarl Yep, I am fairly sure you live in an area where temperatures drop under zero celsius, am I right :-) ? The unit sends negative values as if it started from the end of the UINT16 range (so 65535 is -1, 65534 is -2, 65524 is -10 etc). You have to convert them.

If you look in my nodered graph, you can find I process it so it actually shows sane values:

It seems that negative values are being sent as positive integers starting at 2^16 (Uint16).

@GreyEarl
Copy link

@hnykda Thanks for your reply! For some reason I did not get a notification!

That's correct! I live in NL, and indeed it was always below 0 when the issue occured. I now see your Node-Red flows, I used to look at the one from cliftonc, but I never noticed yours! Thanks for that! :))

@Garia666
Copy link

Man I want something like this for homebridge.

@icecoldfire
Copy link

I use directly the modbus interface between my server and brink flair. This is my current configuration file: https://gist.github.com/icecoldfire/7a0ba51154ae707cc8cd6c19b3ffc63e

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment