MODBUS and System air

Hi, Attached below is an overview of the pcb in my unit. I have a RJ45 for my modbus connection and a RJ11 for the control panel. The only place on the terminals I can find A and B is for the control panel connection which I find weird. I think someone commented in this thread that this connection could not be used for modbus, but maybe worth a try? I am lifting the unit off the wall today to paint the room. I can have a closer look at my PCB then. But I would definitely look for terminals marked with modbus or “A” and “B” in your case. VR400 is supported for modbus, so there must be a way to connect it.

Had it open today, but there is no real way of telling how the RJ45 is connected on the PCB without removing it. I suggest contacting systemair and ask them how to connect to modbus on your unit. They have been helpful with me in the past, so I think they will help you out.

I thought I’d update here with my current configuration including modbus cfg, items, rules and sitemap. Maybe someone can make use of it and also come up with some other ideas I haven’t thought of :slight_smile:

modbus.cfg:

serial.vvfan.connection=/dev/ttyUSB0:19200:8:none:1:rtu:35:5000
serial.vvfan.id=1
serial.vvfan.start=100
serial.vvfan.length=38
serial.vvfan.type=holding
serial.vvfan.valuetype=int16

serial.vvhc.connection=/dev/ttyUSB0:19200:8:none:1:rtu:35:5000
serial.vvhc.id=1
serial.vvhc.start=200
serial.vvhc.length=21
serial.vvhc.type=holding
serial.vvhc.valuetype=int16

serial.vvrot.connection=/dev/ttyUSB0:19200:8:none:1:rtu:35:5000
serial.vvrot.id=1
serial.vvrot.start=350
serial.vvrot.length=2
serial.vvrot.type=holding
serial.vvrot.valuetype=int16

serial.vvsys.connection=/dev/ttyUSB0:19200:8:none:1:rtu:35:5000
serial.vvsys.id=1
serial.vvsys.start=500
serial.vvsys.length=7
serial.vvsys.type=holding
serial.vvsys.valuetype=int16

serial.vvnvm.connection=/dev/ttyUSB0:19200:8:none:1:rtu:35:5000
serial.vvnvm.id=1
serial.vvnvm.start=548
serial.vvnvm.length=1
serial.vvnvm.type=holding
serial.vvnvm.valuetype=int16

serial.vvclock.connection=/dev/ttyUSB0:19200:8:none:1:rtu:35:5000
serial.vvclock.id=1
serial.vvclock.start=550
serial.vvclock.length=7
serial.vvclock.type=holding
serial.vvclock.valuetype=int16

serial.vvfilter.connection=/dev/ttyUSB0:19200:8:none:1:rtu:35:5000
serial.vvfilter.id=1
serial.vvfilter.start=600
serial.vvfilter.length=2
serial.vvfilter.type=holding
serial.vvfilter.valuetype=int16

serial.vvdefr.connection=/dev/ttyUSB0:19200:8:none:1:rtu:35:5000
serial.vvdefr.id=1
serial.vvdefr.start=670
serial.vvdefr.length=2
serial.vvdefr.type=holding
serial.vvdefr.valuetype=int16

serial.vvdig.connection=/dev/ttyUSB0:19200:8:none:1:rtu:35:5000
serial.vvdig.id=1
serial.vvdig.start=700
serial.vvdig.length=9
serial.vvdig.type=holding
serial.vvdig.valuetype=int16

serial.vvtsf.connection=/dev/ttyUSB0:19200:8:none:1:rtu:35:5000
serial.vvtsf.id=1
serial.vvtsf.start=3488
serial.vvtsf.length=5
serial.vvtsf.type=coil
serial.vvtsf.valuetype=bit

serial.vvdi.connection=/dev/ttyUSB0:19200:8:none:1:rtu:35:5000
serial.vvdi.id=1
serial.vvdi.start=11200
serial.vvdi.length=7
serial.vvdi.type=coil
serial.vvdi.valuetype=bit

serial.vvpcu.connection=/dev/ttyUSB0:19200:8:none:1:rtu:35:5000
serial.vvpcu.id=1
serial.vvpcu.start=12000
serial.vvpcu.length=3
serial.vvpcu.type=coil
serial.vvpcu.valuetype=bit

serial.vval.connection=/dev/ttyUSB0:19200:8:none:1:rtu:35:5000
serial.vval.id=1
serial.vval.start=12800
serial.vval.length=9
serial.vval.type=coil
serial.vval.valuetype=bit

items:

Group Villavent

Group VillaventTemperatureGroup
Number VillaventTemperatureGroupGraph

Number VillaventNvm "Store NVM" (Villavent) { modbus="vvnvm:0" }

Number VillaventFan "Fan speed" <fan> (Villavent, Persist) { modbus="vvfan:0" }
Number VillaventSupplyFanRpm "Supply fan RPM [%d]" <fan> (Villavent, Persist) { modbus="vvfan:10" }
Number VillaventSupplyFanGraph (PersistAndRestore)
Number VillaventExtractFanRpm "Extract fan RPM [%d]" <fan> (Villavent, Persist) { modbus="vvfan:11" }
Number VillaventExtractFanGraph (PersistAndRestore)

Number VillaventFrostProtection "Frost protection level [%d]" <temperature> (Villavent, Persist) { modbus="vvhc:5" }
Number VillaventTempLvl "Temperature set point level [%d]" <heating> (Villavent, Persist) { modbus="vvhc:6" }
Number VillaventHeatSetpoint "Temperature set point (read only) [%d]" <heating> (Villavent, Persist) { modbus="vvhc:7" }

Number VillaventSupplyAirTemp "Supply air temperature [%.1f °C]" <temperature> (Villavent, VillaventTemperatureGroup, Temperature)
Number VillaventSupplyAirTemp_in "Supply air temperature [%d]" (Villavent) { modbus="vvhc:13" }
Number VillaventSupplyAirGraph (PersistAndRestore)

Number VillaventExtractAirTemp "Extract air temperature [%.1f °C]" <temperature> (Villavent, VillaventTemperatureGroup, Temperature)
Number VillaventExtractAirTemp_in "Extract air temperature [%d]" (Villavent) { modbus="vvhc:14" }
Number VillaventExtractAirGraph (PersistAndRestore)

Number VillaventExhaustAirTemp "Exhaust air temperature [%.1f °C]" <temperature> (Villavent)
Number VillaventExhaustAirTemp_in "Exhaust air temperature [%d]" (Villavent) { modbus="vvhc:15" }
Number VillaventExhaustAirGraph (PersistAndRestore)

Number VillaventProtectAirTemp "Overheating/Frosprotection temperature [%.1f °C]" <temperature> (Villavent, VillaventTemperatureGroup, Temperature)
Number VillaventProtectAirTemp_in "Overheating/Frosprotection temperature [%d]" (Villavent) { modbus="vvhc:16" }
Number VillaventProtectAirGraph (PersistAndRestore)

Number VillaventIntakeAirTemp "Intake air temperature [%.1f °C]" <temperature> (Villavent, VillaventTemperatureGroup, Temperature)
Number VillaventIntakeAirTemp_in "Intake air temperature [%d]" (Villavent) { modbus="vvhc:17" }
Number VillaventIntakeAirGraph (PersistAndRestore)

Number VillaventHeaterType "Heater type" <smoke> (Villavent, Persist) { modbus="vvhc:0" }
Number VillaventCoolerType "Cooler type" <climate> (Villavent, Persist) { modbus="vvhc:1" }
Number VillaventPreHeaterType "Pre-heater type" <smoke> (Villavent, Persist) { modbus="vvhc:19" }

Number VillaventFlowUnits "Fan speed flow unit" <pressure> (Villavent, Persist) { modbus="vvfan:7" }
Number VillaventFanControlType "Fan control" <flow> (Villavent, Persist) { modbus="vvfan:37" }
Number VillaventAllowFanStop "Control panel fan stop allowed" <switch> (Villavent, Persist) { modbus="vvfan:13" }

Number VillaventSupplyFanSystemCurve "Supply fan system curve [%d]" <line> (Villavent, Persist) { modbus="vvfan:35" }
Number VillaventSupplyFanSpeedLow "Supply fan speed for low [%d]" <fan> (Villavent, Persist) { modbus="vvfan:1" }
Number VillaventSupplyFanSpeedNom "Supply fan speed for nominal [%d]" <fan> (Villavent, Persist) { modbus="vvfan:3" }
Number VillaventSupplyFanSpeedHigh "Supply fan speed for high [%d]" <fan> (Villavent, Persist) { modbus="vvfan:5" }

Number VillaventExtractFanSystemCurve "Extract fan system curve [%d]" <line> (Villavent, Persist) { modbus="vvfan:36" }
Number VillaventExtractFanSpeedLow "Extract fan speed for low [%d]" <fan> (Villavent, Persist) { modbus="vvfan:2" }
Number VillaventExtractFanSpeedNom "Extract fan speed for nominal [%d]" <fan> (Villavent, Persist) { modbus="vvfan:4" }
Number VillaventExtractFanSpeedHigh "Extract fan speed for high [%d]" <fan> (Villavent, Persist) { modbus="vvfan:5" }

Number VillaventRotorState "Rotor state [MAP(vvrotstate.map):%s]" <sun> (Villavent, Persist) { modbus="vvrot:0" }
Switch VillaventRotorRelayActive "Rotor relay [%s]" <switch> (Villavent, VillaventTemperatureGroup, Persist) { modbus="vvrot:1" }

Number VillaventDefrostState "Defrosting state [MAP(vvdefrstate.map):%s]" <rain> (Villavent, Persist) { modbus="vvdefr:0" }
Number VillaventDefrostMode "Defrosting mode [%d]" <qualityofservice> (Villavent, Persist) { modbus="vvdefr:1" }

Number VillaventFilterPeriod "Filter replace time [%d months]" <calendar> (Villavent, Persist) { modbus="vvfilter:0" }
Number VillaventFilterDays "Filter days since last replacement [%d days]" <time> (Villavent, Persist) { modbus="vvfilter:1" }

Number VillaventSystemType "System type [MAP(vvsystype.map):%s]" <contact> (Villavent, Persist) { modbus="vvsys:0" }

String VillaventProgVersion "Program version [%s]" <shield> (Villavent, Persist)
Number VillaventProgVH (Villavent) { modbus="vvsys:1" }
Number VillaventProgVM (Villavent) { modbus="vvsys:2" }
Number VillaventProgVL (Villavent) { modbus="vvsys:3" }
String VillaventBootVersion "Boot version [%s]" <shield> (Villavent, Persist)
Number VillaventBootVH (Villavent) { modbus="vvsys:4" }
Number VillaventBootVM (Villavent) { modbus="vvsys:5" }
Number VillaventBootVL (Villavent) { modbus="vvsys:6" }

DateTime VillaventClock "Clock [%s]" <time> (Villavent)
Number VillaventClockS "Clock seconds [%d]" (Villavent) { modbus="vvclock:0" }
Number VillaventClockM "Clock minutes [%d]" (Villavent) { modbus="vvclock:1" }
Number VillaventClockH "Clock hours [%d]" (Villavent) { modbus="vvclock:2" }
Number VillaventClockD "Clock day of month [%d]" (Villavent) { modbus="vvclock:3" }
Number VillaventClockMNTH "Clock month [%d]" (Villavent) { modbus="vvclock:4" }
Number VillaventClockY "Clock year [%d]" (Villavent) { modbus="vvclock:5" }

Switch VillaventSupplyAirTempFault "Supply air temperature sensor fault [%s]" <siren> (Villavent, Persist) { modbus="vvtsf:0" }
Switch VillaventExtractAirTempFault "Extract air temperature sensor fault [%s]" <siren> (Villavent, Persist) { modbus="vvtsf:0" }
Switch VillaventExhaustAirTempFault "Exhaust air temperature sensor fault [%s]" <siren> (Villavent, Persist) { modbus="vvtsf:0" }
Switch VillaventProtectAirTempFault "Overheating/Frostprotection temperature sensor fault [%s]" <siren> (Villavent, Persist) { modbus="vvtsf:0" }
Switch VillaventIntakeAirTempFault "Intake air temperature sensor fault [%s]" <siren> (Villavent, Persist) { modbus="vvtsf:0" }

Switch VillaventDigital1 "Digital input 1 [%s]" <switch> (Villavent, Persist) { modbus="vvdi:0" }
Number VillaventDigital1SF "Supply fan speed at DI1 [%s]" <fan> (Villavent, Persist) { modbus="vvdig:3" }
Number VillaventDigital1EF "Extract fan speed at DI1 [%s]" <fan> (Villavent, Persist) { modbus="vvdig:4" }
Switch VillaventDigital2 "Digital input 2 [%s]" <switch> (Villavent, Persist) { modbus="vvdi:1" }
Number VillaventDigital2SF "Supply fan speed at DI2 [%s]" <fan> (Villavent, Persist) { modbus="vvdig:5" }
Number VillaventDigital2EF "Extract fan speed at DI2 [%s]" <fan> (Villavent, Persist) { modbus="vvdig:6" }
Switch VillaventDigital3 "Digital input 3 [%s]" <switch> (Villavent, Persist) { modbus="vvdi:2" }
Number VillaventDigital3SF "Supply fan speed at DI3 [%s]" <fan> (Villavent, Persist) { modbus="vvdig:7" }
Number VillaventDigital3EF "Extract fan speed at DI3 [%s]" <fan> (Villavent, Persist) { modbus="vvdig:8" }
Switch VillaventDigital4 "Stop heat [%s]" <switch> (Villavent, Persist) { modbus="vvdi:3" }
Switch VillaventDigital5 "Extended run [%s]" <switch> (Villavent, Persist) { modbus="vvdi:4" }
Number VillaventDigital5Min "Extended running time [%d]" <time> (Villavent, Persist) { modbus="vvdig:1" }
Number VillaventDigital5Speed "Extended running speed [%d]" <fan> (Villavent, Persist) { modbus="vvdig:2" }
Switch VillaventDigital6 "Rotor top sensor [%s]" <switch> (Villavent, Persist) { modbus="vvdi:5" }
Switch VillaventDigital7 "Away [%s]" <switch> (Villavent, Persist) { modbus="vvdi:6" }

Switch VillaventFilterAlarm "Filter alarm [%s]" <siren> (Villavent, Persist) { modbus="vval:0" }
Switch VillaventFanAlarm "Fan alarm [%s]" <siren> (Villavent, Persist) { modbus="vval:1" }
Switch VillaventRotorAlarm "Rotor alarm [%s]" <siren> (Villavent, Persist) { modbus="vval:3" }
Switch VillaventFrostAlarm "Frost alarm [%s]" <siren> (Villavent, Persist) { modbus="vval:4" }
Switch VillaventPcuPbAlarm "PCU-PB alarm [%s]" <siren> (Villavent, Persist) { modbus="vval:5" }
Switch VillaventTempSensorAlarm "Temperature sensor alarm [%s]" <siren> (Villavent, Persist) { modbus="vval:6" }
Switch VillaventThermostatAlarm "Emergency thermostat alarm [%s]" <siren> (Villavent, Persist) { modbus="vval:7" }
Switch VillaventDamperAlarm "Damper alarm [%s]" <siren> (Villavent, Persist) { modbus="vval:8" }

Switch VillaventPreHeaterRelay "Preheater relay [%s]" <switch> (Villavent, Persist) { modbus="vvpcu:0" }
Switch VillaventHeaterRelay "Heater relay [%s]" <switch> (Villavent, Persist) { modbus="vvpcu:1" }
Switch VillaventCommonHeaterRelay "Common relay [%s]" <switch> (Villavent, Persist) { modbus="vvpcu:2" }

rules:

import java.math.BigDecimal
import java.util.Calendar

rule "Villavent home"
when
    Item PresenceState changed to HOME
then
    VillaventFan.sendCommand(2)
    VillaventDigital7.sendCommand(OFF)
end

rule "Villavent away"
when
    Item PresenceState changed to AWAY
then
    VillaventFan.sendCommand(1)
    VillaventDigital7.sendCommand(ON)
end

rule "Correct supply air temp"
when
    Item VillaventSupplyAirTemp_in changed
then
    val BigDecimal ret = (VillaventSupplyAirTemp_in.state as DecimalType).toBigDecimal.movePointLeft(1)
    VillaventSupplyAirTemp.postUpdate(new DecimalType(ret))
end

rule "Correct extract air temp"
when
    Item VillaventExtractAirTemp_in changed
then
    val BigDecimal ret = (VillaventExtractAirTemp_in.state as DecimalType).toBigDecimal.movePointLeft(1)
    VillaventExtractAirTemp.postUpdate(new DecimalType(ret))
end

rule "Correct exhaust air temp"
when
    Item VillaventExhaustAirTemp_in changed
then
    val BigDecimal ret = (VillaventExhaustAirTemp_in.state as DecimalType).toBigDecimal.movePointLeft(1)
    VillaventExhaustAirTemp.postUpdate(new DecimalType(ret))
end

rule "Correct protect air temp"
when
    Item VillaventProtectAirTemp_in changed
then
    val BigDecimal ret = (VillaventProtectAirTemp_in.state as DecimalType).toBigDecimal.movePointLeft(1)
    VillaventProtectAirTemp.postUpdate(new DecimalType(ret))
end

rule "Correct intake air temp"
when
    Item VillaventIntakeAirTemp_in changed
then
    val BigDecimal ret = (VillaventIntakeAirTemp_in.state as DecimalType).toBigDecimal.movePointLeft(1)
    VillaventIntakeAirTemp.postUpdate(new DecimalType(ret))
end

rule "Filter alert"
when
    Item VillaventFilterDays changed
then
    if (((VillaventFilterDays.state as DecimalType).intValue % Math::round(((VillaventFilterPeriod.state as DecimalType).intValue * 30.5) - 30)) == 0) {
        pushover("WARN: Villavent filter replacement is due")
    } else {
        if ((VillaventFilterDays.state as DecimalType).intValue >= (VillaventFilterPeriod.state as DecimalType).intValue * 30.5) {
            pushover("ERROR: Villavent filter replacement overdue")
        }
    }
end

rule "VV read clock"
when
    Item VillaventClockS changed
then
    var Calendar cal = Calendar::getInstance()
    cal.set(Calendar::HOUR_OF_DAY, (VillaventClockH.state as DecimalType).intValue)
    cal.set(Calendar::MINUTE, (VillaventClockM.state as DecimalType).intValue)
    cal.set(Calendar::SECOND, (VillaventClockS.state as DecimalType).intValue)
    cal.set(Calendar::DAY_OF_MONTH, (VillaventClockD.state as DecimalType).intValue)
    cal.set(Calendar::MONTH, (VillaventClockMNTH.state as DecimalType).intValue)
    cal.set(Calendar::YEAR, (VillaventClockY.state as DecimalType).intValue+2000)
    cal.clear(Calendar::MILLISECOND)
    VillaventClock.postUpdate(new DateTimeType(cal))
end

rule "VV write clock"
when
    System started or
    Time cron "0 0 6 * * ?"
then
    var Calendar cal = Calendar::getInstance()
    VillaventClockH.sendCommand(cal.get(Calendar::HOUR_OF_DAY))
    VillaventClockM.sendCommand(cal.get(Calendar::MINUTE))
    VillaventClockS.sendCommand(cal.get(Calendar::SECOND))

    VillaventClockD.sendCommand(cal.get(Calendar::DAY_OF_MONTH))
    VillaventClockMNTH.sendCommand(cal.get(Calendar::MONTH))
    VillaventClockY.sendCommand(cal.get(Calendar::YEAR)-2000)
end

rule "Write NVM"
when
    Time is midnight
then
    VillaventNvm.sendCommand(165)
end

rule "Villavent Program Version"
when
    System started or
    Item VillaventProgVH changed or
    Item VillaventProgVM changed or
    Item VillaventProgVL changed or
    Time is midnight
then
    VillaventProgVersion.postUpdate(String::format("%1$d.%2$02d.%3$02d",
        (VillaventProgVH.state as DecimalType).intValue,
        (VillaventProgVM.state as DecimalType).intValue,
        (VillaventProgVL.state as DecimalType).intValue
    ))
end

rule "Villavent Boot Version"
when
    System started or
    Item VillaventBootVH changed or
    Item VillaventBootVM changed or
    Item VillaventBootVL changed or
    Time is midnight
then
    VillaventBootVersion.postUpdate(String::format("%1$d.%2$02d.%3$02d",
        (VillaventBootVH.state as DecimalType).intValue,
        (VillaventBootVM.state as DecimalType).intValue,
        (VillaventBootVL.state as DecimalType).intValue
    ))
end

rule "Villavent filter alarm"
when
    Item VillaventFilterAlarm changed from OFF to ON
then
    pushover("ERROR: Villavent filter")
end

rule "Villavent filter ok"
when
    Item VillaventFilterAlarm changed from ON to OFF
then
    pushover("OK: Villavent filter")
end

rule "Villavent fan alarm"
when
    Item VillaventFanAlarm changed from OFF to ON
then
    pushover("ERROR: Villavent fan")
end

rule "Villavent fan ok"
when
    Item VillaventFanAlarm changed from ON to OFF
then
    pushover("OK: Villavent fan")
end

rule "Villavent rotor alarm"
when
    Item VillaventRotorAlarm changed from OFF to ON
then
    pushover("ERROR: Villavent rotor")
end

rule "Villavent rotor ok"
when
    Item VillaventRotorAlarm changed from ON to OFF
then
    pushover("OK: Villavent rotor")
end

rule "Villavent frost alarm"
when
    Item VillaventFrostAlarm changed from OFF to ON
then
    pushover("ERROR: Villavent frost")
end

rule "Villavent frost ok"
when
    Item VillaventFrostAlarm changed from ON to OFF
then
    pushover("OK: Villavent frost")
end

rule "Villavent PCU-PB alarm"
when
    Item VillaventPcuPbAlarm changed from OFF to ON
then
    pushover("ERROR: Villavent PCU-PB")
end

rule "Villavent PCU-PB ok"
when
    Item VillaventPcuPbAlarm changed from ON to OFF
then
    pushover("OK: Villavent PCU-PB")
end

rule "Villavent temp sensor alarm"
when
    Item VillaventTempSensorAlarm changed from OFF to ON
then
    pushover("ERROR: Villavent temp sensor")
end

rule "Villavent temp sensor ok"
when
    Item VillaventTempSensorAlarm changed from ON to OFF
then
    pushover("OK: Villavent temp sensor")
end

rule "Villavent thermostat alarm"
when
    Item VillaventThermostatAlarm changed from OFF to ON
then
    pushover("ERROR: Villavent emergency thermostat")
end

rule "Villavent thermostat ok"
when
    Item VillaventThermostatAlarm changed from ON to OFF
then
    pushover("OK: Villavent emergency thermostat")
end

rule "Villavent damper alarm"
when
    Item VillaventDamperAlarm changed from OFF to ON
then
    pushover("ERROR: Villavent damper")
end

rule "Villavent damper ok"
when
    Item VillaventDamperAlarm changed from ON to OFF
then
    pushover("OK: Villavent damper")
end

sitemap:

sitemap villavent label="Villavent" {
	Selection item=VillaventTempLvl mappings=[11="22 °C", 10="21 °C", 9="20 °C", 8="19 °C", 7="18 °C", 6="17 °C", 5="16 °C", 4="15 °C", 3="14 °C", 2="13 °C", 1="12° C", 0="Summer mode"]
	Selection item=VillaventFan icon=qualityofservice mappings=[4="Auto", 3="Max", 2="Normal", 1="Low", 0="Off"]
	Text item=VillaventSupplyAirTemp {
		Switch item=VillaventSupplyAirGraph label="Graph period" mappings=[1="Hour", 2="Day", 3="Week", 4="Month", 5="Year"]
		Chart item=VillaventSupplyAirTemp period=h refresh=30000 visibility=[VillaventSupplyAirGraph==1]
		Chart item=VillaventSupplyAirTemp period=D refresh=900000 visibility=[VillaventSupplyAirGraph==2]
		Chart item=VillaventSupplyAirTemp period=W refresh=3600000 visibility=[VillaventSupplyAirGraph==3]
		Chart item=VillaventSupplyAirTemp period=M refresh=3600000 visibility=[VillaventSupplyAirGraph==4]
		Chart item=VillaventSupplyAirTemp period=Y refresh=3600000 visibility=[VillaventSupplyAirGraph==5]
	}
	Text item=VillaventSupplyFanRpm {
		Switch item=VillaventSupplyFanGraph label="Graph period" mappings=[1="Hour", 2="Day", 3="Week", 4="Month", 5="Year"]
		Chart item=VillaventSupplyFanRpm period=h refresh=30000 visibility=[VillaventSupplyFanGraph==1]
		Chart item=VillaventSupplyFanRpm period=D refresh=900000 visibility=[VillaventSupplyFanGraph==2]
		Chart item=VillaventSupplyFanRpm period=W refresh=3600000 visibility=[VillaventSupplyFanGraph==3]
		Chart item=VillaventSupplyFanRpm period=M refresh=3600000 visibility=[VillaventSupplyFanGraph==4]
		Chart item=VillaventSupplyFanRpm period=Y refresh=3600000 visibility=[VillaventSupplyFanGraph==5]
	}
	Text item=VillaventExtractAirTemp {
		Switch item=VillaventExtractAirGraph label="Graph period" mappings=[1="Hour", 2="Day", 3="Week", 4="Month", 5="Year"]
		Chart item=VillaventExtractAirTemp period=h refresh=30000 visibility=[VillaventExtractAirGraph==1]
		Chart item=VillaventExtractAirTemp period=D refresh=900000 visibility=[VillaventExtractAirGraph==2]
		Chart item=VillaventExtractAirTemp period=W refresh=3600000 visibility=[VillaventExtractAirGraph==3]
		Chart item=VillaventExtractAirTemp period=M refresh=3600000 visibility=[VillaventExtractAirGraph==4]
		Chart item=VillaventExtractAirTemp period=Y refresh=3600000 visibility=[VillaventExtractAirGraph==5]
	}
	Text item=VillaventExtractFanRpm {
		Switch item=VillaventExtractFanGraph label="Graph period" mappings=[1="Hour", 2="Day", 3="Week", 4="Month", 5="Year"]
		Chart item=VillaventExtractFanRpm period=h refresh=30000 visibility=[VillaventExtractFanGraph==1]
		Chart item=VillaventExtractFanRpm period=D refresh=900000 visibility=[VillaventExtractFanGraph==2]
		Chart item=VillaventExtractFanRpm period=W refresh=3600000 visibility=[VillaventExtractFanGraph==3]
		Chart item=VillaventExtractFanRpm period=M refresh=3600000 visibility=[VillaventExtractFanGraph==4]
		Chart item=VillaventExtractFanRpm period=Y refresh=3600000 visibility=[VillaventExtractFanGraph==5]
	}
	Text item=VillaventIntakeAirTemp {
		Switch item=VillaventIntakeAirGraph label="Graph period" mappings=[1="Hour", 2="Day", 3="Week", 4="Month", 5="Year"]
		Chart item=VillaventIntakeAirTemp period=h refresh=30000 visibility=[VillaventIntakeAirGraph==1]
		Chart item=VillaventIntakeAirTemp period=D refresh=900000 visibility=[VillaventIntakeAirGraph==2]
		Chart item=VillaventIntakeAirTemp period=W refresh=3600000 visibility=[VillaventIntakeAirGraph==3]
		Chart item=VillaventIntakeAirTemp period=M refresh=3600000 visibility=[VillaventIntakeAirGraph==4]
		Chart item=VillaventIntakeAirTemp period=Y refresh=3600000 visibility=[VillaventIntakeAirGraph==5]
	}
	Text item=Villavent label="Configuration" icon=settings {
		Frame label="Information" {
			Text item=VillaventSystemType
			Text item=VillaventClock
			Text item=VillaventProgVersion
			Text item=VillaventBootVersion
		}
		Frame label="Fan" {
			Selection item=VillaventFanControlType mappings=[0="Air flow", 1="Speed"]
			Selection item=VillaventFlowUnits mappings=[0="l/s", 1="m3/h"]
			Setpoint item=VillaventSupplyFanSystemCurve minValue=0 maxValue=20 step=1
			Setpoint item=VillaventExtractFanSystemCurve minValue=0 maxValue=10 step=1
			Setpoint item=VillaventSupplyFanSpeedLow minValue=10 maxValue=100 step=1
			Setpoint item=VillaventExtractFanSpeedLow minValue=10 maxValue=100 step=1
			Setpoint item=VillaventSupplyFanSpeedNom minValue=10 maxValue=100 step=1
			Setpoint item=VillaventExtractFanSpeedNom minValue=10 maxValue=100 step=1
			Setpoint item=VillaventSupplyFanSpeedHigh minValue=10 maxValue=100 step=1
			Setpoint item=VillaventExtractFanSpeedHigh minValue=10 maxValue=100 step=1
			Switch item=VillaventAllowFanStop
		}
		Frame label="Filter" {
			Setpoint item=VillaventFilterPeriod minValue=6 maxValue=12 step=1 label="Replace interval"
			Text item=VillaventFilterDays label="Days since last replace"
		}
		Frame label="Heater and cooler" {
			Selection item=VillaventHeaterType mappings=[3="Contactor", 2="Electrical", 1="Water", 0="None"]
			Text item=VillaventHeaterRelay
			Selection item=VillaventPreHeaterType mappings=[1="Electrical", 0="None"]
			Text item=VillaventPreHeaterRelay
			Selection item=VillaventCoolerType mappings=[1="Water", 0="None"]
			Text item=VillaventCommonHeaterRelay
			Selection item=VillaventFrostProtection mappings=[120="12° C", 110="11° C", 100="10° C", 90="9° C", 80="8° C", 70="7° C"]
			Switch item=VillaventDigital4
			Text item=VillaventHeatSetpoint
		}
		Frame label="Rotor" {
			Text item=VillaventRotorState label="State"
			Text item=VillaventRotorRelayActive label="Relay"
			Text item=VillaventDigital6 label="Rotation sensor"
		}
		Frame label="Defrosting" {
			Text item=VillaventDefrostState label="State"
			Selection item=VillaventDefrostMode mappings=[0="0", 1="1", 2="2", 3="3", 4="4", 5="5"] label="Mode"
		}
		Frame label="Digital inputs" {
			Switch item=VillaventDigital7
		}
		Frame label="" {
			Switch item=VillaventDigital1
			Selection item=VillaventDigital1SF mappings=[3="Max", 2="Normal", 1="Low", 0="Off"]
			Selection item=VillaventDigital1EF mappings=[3="Max", 2="Normal", 1="Low", 0="Off"]
		}
		Frame label="" {
			Switch item=VillaventDigital2
			Selection item=VillaventDigital2SF mappings=[3="Max", 2="Normal", 1="Low", 0="Off"]
			Selection item=VillaventDigital2EF mappings=[3="Max", 2="Normal", 1="Low", 0="Off"]
		}
		Frame label="" {
			Switch item=VillaventDigital3
			Selection item=VillaventDigital3SF mappings=[3="Max", 2="Normal", 1="Low", 0="Off"]
			Selection item=VillaventDigital3EF mappings=[3="Max", 2="Normal", 1="Low", 0="Off"]
		}
		Frame label="" {
			Switch item=VillaventDigital5
			Setpoint item=VillaventDigital5Min minValue=1 maxValue=60 step=1
			Selection item=VillaventDigital5Speed mappings=[3="Max", 2="Normal", 1="Low", 0="Off"]
		}
		Frame label="Temperature sensors" {
			Text item=VillaventSupplyAirTemp
			Text item=VillaventSupplyAirTempFault
			Text item=VillaventExtractAirTemp
			Text item=VillaventExtractAirTempFault
			Text item=VillaventExhaustAirTemp
			Text item=VillaventExhaustAirTempFault
			Text item=VillaventProtectAirTemp
			Text item=VillaventProtectAirTempFault
			Text item=VillaventIntakeAirTemp
			Text item=VillaventIntakeAirTempFault
		}
		Frame label="Alarms" {
			Text item=VillaventFilterAlarm
			Text item=VillaventFanAlarm
			Text item=VillaventRotorAlarm
			Text item=VillaventFrostAlarm
			Text item=VillaventPcuPbAlarm
			Text item=VillaventTempSensorAlarm
			Text item=VillaventThermostatAlarm
			Text item=VillaventDamperAlarm
		}
	}
	Text item=VillaventTemperatureGroup label="Temperature graph" icon=line {
		Switch item=VillaventTemperatureGroupGraph label="Graph period" mappings=[1="Hour", 2="Day", 3="Week", 4="Month", 5="Year"]
		Chart item=VillaventTemperatureGroup period=h refresh=30000 visibility=[VillaventTemperatureGroupGraph==1]
		Chart item=VillaventTemperatureGroup period=D refresh=900000 visibility=[VillaventTemperatureGroupGraph==2]
		Chart item=VillaventTemperatureGroup period=W refresh=3600000 visibility=[VillaventTemperatureGroupGraph==3]
		Chart item=VillaventTemperatureGroup period=M refresh=3600000 visibility=[VillaventTemperatureGroupGraph==4]
		Chart item=VillaventTemperatureGroup period=Y refresh=3600000 visibility=[VillaventTemperatureGroupGraph==5]
	}
}
1 Like

This is such a great write up, do you reckon you could turn it into a tutorial and also add some screenshots of the setup(both sitemaps and physical wiring). Great work Gunnar!

Thanks, I’ll see what I can do over a cold beer one weekend :slight_smile:

Hi, please can you give some screenshots of wiring on Systemair ventilation?

My recommendation, buy something like a FTDI USB-RS485-PCBA and connect like attached image. Solid blue to B and striped blue to A from a standard T568 color coded RJ45 cable.

vv_rs485

1 Like

I have also edited the configuration in my previous comment to match the config I use today.

OK so that it, just plug one side of the cable RJ-45 to board on Systemair HRV unit and other side with two wires like you mentioned to FTDI USB and then plug it to Raspberry pi…

Please send picture how does it look on OH dashboard…

Correct. According to the modbus document from Systemair, the following units use this registry set:
VR400, VR700, VR700DK, VR400DE, VTC300, VTC700, VTR150K, VTR200B, VSR300, VSR500, VSR150, VTR300, VTR500, VSR300DE, VTC200

There may be more that also use the same registry set, but these are at least mentioned in the document.

I have tested this on a Asus Tinkerboard, and the system totally crash when I run openhab with this setup. Without the RS485 USB connected it runs fine. I’m not sure if this is some issue isolated to Tinkerboard and the chipset it uses or if this would apply for Raspberry Pi as well. As far as I could tell when debugging it was related to chipset and IO. I did not use much time to try and fix it as it wasn’t important to me.

An alternative would be to find a modbus rs485 to tcp/ip adapter, but I have no suggestion on what hardware to use or how well it works.

Following is some screenshots from my openhab basicui:

Configuration menu:



Temperature graph:

I guess it would be nice to set up a usb rule, so that in case of shutdown and a new number get assigned it will still work. I am can not rember how to do this now, but it is explained somewhere on the forum here. Just a small tip. It looks awesome @gskjold, and something I will definitely implement over Xmas, are you running it on a RPI?

Do you have link to your dongle?

So which temperatures do you recommend to use? Some models have an optional heating element to preheat the incoming air(supply).

I have the followig in my /etc/udev/rules.d/99-usb-serial.rules

SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", OWNER="openhab" SYMLINK="modbus"

This creates /dev/modbus as a device, but openhab does not work with this symlink for some reason. I have not chased this issue, so the solution may be simple. On reboot the device stays the same. Only replugging the device will change the identifier.

Not tried this setup on rpi yet. I use a HP G7 micro server.

My dongle is a FTDI USB-RS485-PCBA which you can find on ebay.

As for set temperatures I have 22° set in the winter. I use the electric heater in mine. I just feel this makes for more comfortable air in my house. My rules sets the away flag in the unit which switches off this heater when I am away. You can see it in the temperature graph. Looking at the blue and red line (which overlaps), you can see the ripples in the supply air when I am home, and how it is switched off when I am away. I also set the fan to low when I am away.

Temp graph:
vv_openhab_graph

Fan graph:
vv_openhab_graph2

To your info I got it to work now. I will make some rules and keep you posted on the progress, thanks for the great work…

Some additional information to you.

  1. I am on openhabian, so I just issued system tweaks to get the serial device to work.
  2. Get last connected USB device
    dmesg | awk '/tty/ && /USB/ {print "/dev/"$10}'|tail -1```
    
  3. Get the device number
    lsusb
  4. Edit usb rules: sudo nano /etc/udev/rules.d/99-usb-serial.rules
    SUBSYSTEM=="tty", ATTRS{idVendor}=="XXX", ATTRS{idProduct}=="XXXX", OWNER="openhab" SYMLINK="modbus" however I could not get this to work either…
  5. Go to your fan panel and make sure the modbus settings are correct:
    Baudrate:19200, startIndex =1 Parity:None

So lets now move on to the rules:
So what I want to do with the fan is:

  1. Increase the speed if you go to the bathroom
  2. Switch it off when you are not home
  3. Adjust it automatically depending on CO2 level in the flat

The question is if we should adjust the rpm or do we only have the option to set fan speed to 1,2,3,4, what do you think @gskjold, can these be set dynamically:

Number VillaventSupplyFanRpm 
Number VillaventExtractFanRpm 

So my rule idea is shown below

val Functions.Function2 hvac= [ NumberItem setPoint,NumberItem co2Level ||

if manualOverride.state = OFF {
if((co2Level.state as Number)-100.0<(setPoint.state as Number) ) {

   VillaventFan.sendCommand( VillaventFan.state as Number -1)
}
if((co2Level.state as Number)+100.0>(setPoint.state as Number) ) {

   VillaventFan.sendCommand( VillaventFan.state as Number +1)
}
}

The bathroom rule:

rule "Turn up fan"
when
	Item bathroomLight changed  to ON
then

        VillaventFan.sendCommand(4)
	createTimer(now.plusSeconds(600))[|
	sendCommand(manualOverride, ON)
]
 sendCommand(manualOverride, OFF)
end

Thanks for all the details in here, I will build something similar to replace my existing Z-wave unit attached to my VSR-300 (not working in OH2.4, and also with very limited feature set compared to the solution here).

My OpenHAB server is two floors away from the ventilation unit, so a direct connection is not possible. A Raspberry PI is a candidate, any recommendation on how to connect them? USB over TCP/IP, serial over TCP/IP (maybe the same thing?), or possibly write a MQTT service on the Pi?

There are off-the-shelf solutions like this.
openHAB talks to it as though it were a Modbus-TCP device, and it converts to/from serial Modbus-RTU
https://www.usriot.com/products/modbus-rtu-to-wifi-converter.html

Thanks @gskjold for the detailed modbus configuration file and items file (the map-file is missing though).

I have it running with a one-dollar usb-485 adapter on a raspberry pi zero, which is running socat to my OpenHAB server in the basement.

I have already installed four temp+rh sensors in each of the chambers, and I compute efficiency factors continously . Some preliminary testing after some hours of operation reveals that there is additional effiency gain from ensuring that the heating set point is so high that the rotor is always on, but the heater is off. It also took me some time to figure out that I could not control the setpoint through the item VillaventHeatSetpoint, but through VillaventTempLvl (and the former can be subsequently read).

Has anyone converted the setup to the OpenHAB 2 Modbus binding? I think it is then possible to have different polling periods for different items. The VillaventDigital6 (Rotor Top Sensor) should be polled quite often (many times pr. second), it seems, to catch the event that the rotor passes the top, while most others items can be polled with many seconds apart.

I see that I’ve missed some posts here, sorry about that…

@skatun great to hear that you got it working! I love the idea of adjusting it for the fan speed based on CO2. What do you use for CO2 measuring?
Switching it off while you are not home is something I would be careful with due to condensation in the intake and exhaust. There is a reason why VillaventAllowFanStop is off as a default.

@berland did you find a good solution for physical connection between the unit and openhab? Personally I try to run everything over cable if I can.
How is the load on your rpi with this connected? I tried openhab on a Asus Tinker Board once, but it crashed as soon as I put the dongle in.
Do you have a device id for your dongle? You can find it using lsusb command in the terminal.
Sorry about my misconfiguration of the VillaventTempLvl adjustment in my setup. I noticed it as well this summer. I must have switched those two items in my sitemap at some point since I have used that dropdown in the sitemap before and seen it work.
Is the four sensors standalone, or have you added the fourth sensor to the unit? At least mine seems to be missing the exhaust air temperature sensor and shows no value on this register.
I have also seen that the efficiency factor is highest in this condition through the units menu. I’m annoyed that the unit does not report this value on the register.

I have recently converted my setup to 2x binding. I think it took me 3hrs to do so… In short, I’ve done:
Added modbus serial slave. Then added poller for each range of registers I need to read. Then added a thing for each item I needed to read from. You can set the poll interval for each poller. So for the digital input coil, you can set something lower.

As a side note, I think the modbus 2x binding has a really strange configuration structure, and I would be surprised if this doesn’t change in the future.

The most recent rules for my villavent is to set the fan to max while the fire alarm is going, and then set it back to normal when the fire alarm goes back to normal state. Any thoughts on how the ventilation should be set if there is a fire?

I wouldn’t hold your breath for that :slight_smile:
It was developed based on real OH users wrestling with weird and wonderful Modbus devices. The key thing is flexibility to cope with some maker’s truly weird interpretations of modbus ‘standard’.
This does of course make it look over-complicated for simple tasks.

What we do keep bandying about is a method to add-on a module or template from a library for known devices. This would hide complexity. To make it happen would require enough someones with device X, and skills and inclination.

In reality we each solve our own problem, and move on. Helpful folk can of course publish their solutions here, to give latecomers a head start :slight_smile:

Thanks @rossko57 for the input, but wouldn’t it be a lot easier to have the register values as dynamic channels on the poll thing? This would to me make a lot more sense and reduce the number of things drastically.

KNX 2x binding is made in that way:

I don’t really understand the question, not familiar with KNX.
But let me make an example. We want to read registers 5 and 7 say, but there is no register 6. Some devices let you read 5,6,7 in one efficient poll (in our terms, one poller thing with two data channels). Other devices insist you poll 5 and 7 separately (in our terms, two poller things with a data channel each).

There’s no way for the system to tell which is the correct solution, it must be manually configured by a user with understanding.