VRV system control from KNX bus with CoolMaster
Example: VRV system control from KNX bus with CoolMaster
Task
If there is a necessity to control VRV systems from your fieldbus system you can use/integrate one of the following control methods:
- InfraRed
- ModBus over RS-485 or LAN
- RS-232
Using the above methods you can do quite cost-effective integration of VRV systems in your KNX, BACnet, EnOcean etc. field-buses and common visualization.
The following script will show how to interconnect CoolMaster VRV controller to LogicMachine3 over RS-232 serial port. The following VRV systems can be integrated: Daikin, Fujitsu, Gree, Hitachi, LG, Mitsubishi, Mitsubishi Heavy, Panasonic, Samsung, Sanyo, Toshiba.
RS-232 integration
If your LogicMachine does not have RS-232, you can integrate it through USB to RS232 converter based on CP210x, FT232, PL2303, MCT U232 chips. For example, this.
Objects
For correct work objects should be named correctly:
- 101 on/off – binary
- 101 temp – 2 byte floating point
- 101 mode – 1 byte (0 = cool, 1 = dry, 2 = heat)
- 101 fspeed – 1 byte (0 = low, 1 = medium, 2 = high, 3 = auto)
- all on/off – binary object to turn all blocks on and off at once
For status objects we add ‘status’ after the name, example:
- 101 on/off status
- 101 temp status
For all objects you should set tag = VRV
Resident script
Add the following resident script with 0 interval. Replace ‘/dev/RS232’ with the respective name of the port of your LogicMachine.
- if not cm then
- require('serial')
- require('genohm-scada.eibdgm')
-
- -- knx and coolmaster mapping
- knxtocm = {}
- cmtoknx = {}
-
- -- value mapping
- modetotext = { [0] = 'cool', [1] = 'dry', [2] = 'heat' }
- texttomode = { cool = 0, dry = 1, heat = 2 }
- speedtotext = { [0] = 'l', [1] = 'm', [2] = 'h', [3] = 'a' }
- texttospeed = { low = 0, med = 1, high = 2, auto = 3 }
-
- -- get all tagged objects and set mapping
- for _, object in ipairs(grp.tag('VRV')) do
- local address, fn, stat = unpack(object.name:split(' '))
-
- if address ~= 'all' then
- address = tonumber(address)
- end
-
- -- status object
- if stat then
- if not cmtoknx[ address ] then
- cmtoknx[ address ] = {}
- end
-
- cmtoknx[ address ][ fn ] = {
- id = object.id,
- value = grp.getvalue(object.id),
- }
- -- control object
- else
- knxtocm[ object.id ] = {
- address = address,
- fn = fn,
- }
- end
- end
-
- function updateknx(address, fn, value, datatype)
- local object = cmtoknx[ address ] and cmtoknx[ address ][ fn ] or nil
-
- -- object not found
- if not object then
- return
- end
-
- -- no value or same value, no update required
- if value == nil or object.value == value then
- return
- end
-
- -- save new value and write to knx
- object.value = value
- grp.write(object.id, value, datatype)
- end
-
- function parseline(line)
- local address, status, setpoint, temp, speed, mode
-
- address = line:sub(1, 3)
- address = tonumber(address)
-
- -- address is not a number, cannot parse line
- if not address then
- return
- end
-
- -- on/off status
- status = line:sub(5, 6):lower() == 'on'
- updateknx(address, 'on/off', status, dt.bool)
-
- -- setpoint is integer
- setpoint = line:sub(9, 10)
- setpoint = tonumber(setpoint)
- updateknx(address, 'temp', setpoint, dt.float16)
-
- -- room temp is float, separated by comma
- temp = line:sub(13, 17):gsub(',', '.')
- temp = tonumber(temp)
- updateknx(address, 'temp_room', temp, dt.float16)
-
- -- speed: low, med, high, auto
- speed = line:sub(20, 23):lower():trim()
- updateknx(address, 'fspeed', texttospeed[ speed ], dt.uint8)
-
- -- mode: cool, heat, fan, dry, auto
- mode = line:sub(25, 28):lower():trim()
- updateknx(address, 'mode', texttomode[ mode ], dt.uint8)
- end
-
- -- read single line from serial
- function readline()
- local line, timeout, char, err
-
- line = {}
- timeout = 10
-
- -- read until timeout or full line
- while timeout > 0 do
- -- get single char from serial
- char, err = cm:read(1, 0.1)
-
- -- read timeout
- if not char then
- timeout = timeout - 1
- -- end-of-line
- elseif char == '\n' then
- break
- -- ignore carriage return
- elseif char ~= '\r' then
- table.insert(line, char)
- end
- end
-
- -- read ok
- if timeout > 0 then
- return table.concat(line)
- end
- end
-
- -- read current status
- function readstat()
- local line
- cm:flush()
- cm:write('stat\r\n')
-
- -- read until error or end of stat
- while true do
- line = readline()
-
- -- timeout or end occured
- if not line or line == 'OK' then
- break
- end
-
- parseline(line)
- end
- end
-
- -- send single cmd to coolmaster
- function writecmd(cmd)
- local result
-
- cm:flush()
- cm:write(cmd .. '\r\n')
-
- readline() -- command echo
- result = readline() -- command result
- readline() -- new line prompt
-
- return result
- end
-
- -- handle group writes
- function eventhandler(event)
- local object, cmd, value, param
-
- object = knxtocm[ event.dstraw ]
- -- knx object not mapped, ignore
- if not object then
- return
- end
-
- -- on/off - boolean
- if object.fn == 'on/off' then
- value = knxdatatype.decode(event.datahex, dt.bool)
- cmd = value and 'on' or 'off'
- -- setpoint - floating point
- elseif object.fn == 'temp' then
- value = knxdatatype.decode(event.datahex, dt.float16)
- param = string.format('%.2f', value)
- cmd = 'temp'
- -- mode (fan, dry, auto)
- elseif object.fn == 'mode' then
- value = knxdatatype.decode(event.datahex, dt.uint8)
- cmd = modetotext[ value ]
- -- speed (low, medium, high, auto)
- elseif object.fn == 'fspeed' then
- value = knxdatatype.decode(event.datahex, dt.uint8)
- param = speedtotext[ value ]
-
- if param then
- cmd = 'fspeed'
- end
- end
-
- -- got valid command
- if cmd then
- -- all allows only on/off
- if object.address == 'all' then
- cmd = 'all' .. cmd
- -- append address to command
- else
- cmd = cmd .. ' ' .. object.address
- end
-
- -- append additional parameter if set
- if param then
- cmd = cmd .. ' ' .. param
- end
-
- writecmd(cmd, true)
- end
- end
-
- -- coolmaster serial connection
- cm = serial.open('/dev/RS232', { baudrate = 9600 })
-
- -- knx connection
- client = eibdgm:new()
- client:sethandler('groupwrite', eventhandler)
-
- -- start-up time
- sec, usec = os.microtime()
- end
-
- -- handle knx
- client:step()
-
- -- read stats every 3 seconds
- diff = os.udifftime(sec, usec)
- if diff < 0 or diff >= 3 then
- readstat()
- sec, usec = os.microtime()
- end