Example: HDL integration with KNX through Logic Machine 2

Task

Control HDL protocol-based dimmer and relay according to KNX objects value change.

Note! Using Logic Machine 2 you can create also HDL to Modbus, or HDL to BACnet, or EnOcean to HDL gateway etc.

Add HDL function in LM2

Add the following script in Scripting -> Tools -> User function library

Source code    
  1. HDL = {
  2. -- destination ip
  3. dstip = '192.168.1.7',
  4. -- packet constant data
  5. magic = 'HDLMIRACLE',
  6. lcode = string.char(0xAA, 0xAA),
  7. -- source device settings
  8. srcsubnet = 1,
  9. srcdevice = 254,
  10. devicetype = 0xFFFE,
  11. -- command types
  12. cmd = {
  13. chanreg = 0x0031, -- single channel regulate
  14. chanregreply = 0x0032, -- single channel regulate answerback
  15. chanstat = 0x0033, -- read status of single channel targets
  16. chanstatreply = 0x0034, -- single channel targets status answerback
  17. }
  18. }
  19.  
  20. HDL.init = function()
  21. require('json')
  22. require('crc16')
  23. require('socket')
  24.  
  25. local ip, chunk, chunks, data
  26. -- read interface data
  27. data = json.pdecode(io.readproc('if-json'))
  28.  
  29. if not data or not data.eth0 then
  30. error('cannot get interface data')
  31. end
  32.  
  33. -- ip header
  34. HDL.iphdr = ''
  35. -- broadcast address
  36. HDL.bcast = data.eth0.bcast
  37.  
  38. -- split ip address into chunks
  39. chunks = data.eth0.inetaddr:split('.')
  40.  
  41. -- add ip address chunks
  42. for i = 1, 4 do
  43. chunk = tonumber(chunks[ i ])
  44. HDL.iphdr = HDL.iphdr .. string.char(chunk)
  45. end
  46. end
  47.  
  48. HDL.decode = function(packet)
  49. local len, data, src, crc
  50.  
  51. -- primary header
  52. if packet:sub(5, 14) ~= HDL.magic then
  53. return nil, 'magic'
  54. end
  55.  
  56. -- leading code
  57. if packet:sub(15, 16) ~= HDL.lcode then
  58. return nil, 'lcode'
  59. end
  60.  
  61. -- get data length and check against
  62. len = packet:byte(17)
  63. if len and len + 16 ~= packet:len() then
  64. return nil, 'len'
  65. end
  66.  
  67. -- get packet data and check crc
  68. data = packet:sub(17, len + 14)
  69. crc = packet:byte(len + 15) * 0x100 + packet:byte(len + 16)
  70. if crc16(data) ~= crc then
  71. return nil, 'crc'
  72. end
  73.  
  74. -- return parsed packet
  75. return {
  76. srcip = string.format('%d.%d.%d.%d', packet:byte(1, 4)),
  77. srcsubnet = packet:byte(18),
  78. srcdevice = packet:byte(19),
  79. devicetype = (packet:byte(20) * 0x100 + packet:byte(21)),
  80. opcode = (packet:byte(22) * 0x100 + packet:byte(23)),
  81. dstsubnet = packet:byte(24),
  82. dstdevice = packet:byte(25),
  83. additional = packet:sub(26, len + 14)
  84. }
  85. end
  86.  
  87. HDL.word = function(v)
  88. return string.char(bit.band(bit.rshift(v, 8), 0xFF), bit.band(v, 0xFF))
  89. end
  90.  
  91. HDL.encode = function(cmd, dstsubnet, dstdevice, extra)
  92. local packet, len, crc, data
  93.  
  94. -- perform init if required
  95. if not HDL.iphdr then
  96. HDL.init()
  97. end
  98.  
  99. -- start packet: ip, magic and leading code
  100. packet = { HDL.iphdr, HDL.magic, HDL.lcode }
  101. -- base data
  102. data = string.char(HDL.srcsubnet, HDL.srcdevice) .. HDL.word(HDL.devicetype) .. HDL.word(cmd) .. string.char(dstsubnet, dstdevice)
  103.  
  104. -- add extra data parameters
  105. if type(extra) == 'string' then
  106. data = data .. extra
  107. end
  108.  
  109. -- calculate length and crc
  110. len = string.char(data:len() + 3)
  111. crc = crc16(len .. data)
  112.  
  113. table.insert(packet, len)
  114. table.insert(packet, data)
  115. table.insert(packet, HDL.word(crc))
  116.  
  117. return table.concat(packet)
  118. end
  119.  
  120. HDL.send = function(packet)
  121. local client = socket.udp()
  122. client:sendto(packet, HDL.dstip, 6000)
  123. end
  124.  
  125. HDL.chanreg = function(dstsubnet, dstdevice, chan, value, delay)
  126. local extra, packet
  127. delay = delay or 0
  128.  
  129. if type(value) == 'boolean' then
  130. value = value and 100 or 0
  131. end
  132.  
  133. extra = string.char(chan, value) .. HDL.word(delay)
  134. packet = HDL.encode(HDL.cmd.chanreg, dstsubnet, dstdevice, extra)
  135.  
  136. HDL.send(packet)
  137. end

Change HDL parameters in the function to correct ones

By default IP of HDL controller is 192.168.1.7 (dstip), the address of Logic Machine in HDL network is 1.254 (srcsubnet and srcdevice).

Change HDL dimmer value on each KNX object value change

  • Add new object in Object menu
  • Add Event-based script which will monitor newly created object
  • In Scripting Editor specify the following code for this script
  • Source code    
    1. local value = dpt.decode(event.datahex, dt.scale)
    2. HDL.chanreg(1, 12, 1, value, 1)

    Function description to control dimmer or relay module:

    Source code    
    1. HDL.chanreg(dstsubnet, dstdevice, chan, value, delay)

    Parameters:

    • dstsubnet – device subnet
    • dstdevice – device address
    • chan – channel number (1..n)
    • value – value (0..100, or true / false)
    • delay – transition time or delay in seconds (0..65535), by default is 0

 

Test Dimmer program: if you change the value for object 4/1/1 in Objects menu with Set Value, it will automatically change dimmer state in HDL network

Change HDL network’s relay state on each KNX object value change

  • Add new object in Object menu
  • Add Event-based script which will monitor newly created object
  • In Scripting Editor specify the following code for this script
  • Source code    
    1. local value = dpt.decode(event.datahex, dt.bool)
    2. HDL.chanreg(1, 11, 1, value)

 

Test the Relay program: if you change the value for object 4/1/2 in Objects menu with Set Value, it will automatically change the relay state in HDL network.