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. local socket = require('socket')
  2. local crc16 = require('encdec').crc16
  3. local ifinfo = require('ifinfo')
  4.  
  5. HDL = {
  6. -- destination ip
  7. dstip = '192.168.0.2',
  8. -- packet constant data
  9. magic = 'HDLMIRACLE',
  10. lcode = string.char(0xAA, 0xAA),
  11. -- source device settings
  12. srcsubnet = 1,
  13. srcdevice = 254,
  14. devicetype = 0xFFFE,
  15. -- command types
  16. cmd = {
  17. chanreg = 0x0031, -- single channel regulate
  18. chanregreply = 0x0032, -- single channel regulate answerback
  19. chanstat = 0x0033, -- read status of single channel targets
  20. chanstatreply = 0x0034, -- single channel targets status answerback
  21. }
  22. }
  23.  
  24. HDL.init = function()
  25. local info, chunk, chunks
  26.  
  27. info = ifinfo()
  28.  
  29. if not info or not info.eth0 then
  30. error('cannot get interface info')
  31. end
  32.  
  33. -- ip header
  34. HDL.iphdr = ''
  35. -- broadcast address
  36. HDL.bcast = info.eth0.bcast
  37.  
  38. -- split ip address into chunks
  39. chunks = info.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) ..
  103. HDL.word(HDL.devicetype) ..
  104. HDL.word(cmd) ..
  105. string.char(dstsubnet, dstdevice)
  106.  
  107. -- add extra data parameters
  108. if type(extra) == 'string' then
  109. data = data .. extra
  110. end
  111.  
  112. -- calculate length and crc
  113. len = string.char(data:len() + 3)
  114. crc = crc16(len .. data)
  115.  
  116. table.insert(packet, len)
  117. table.insert(packet, data)
  118. table.insert(packet, HDL.word(crc))
  119.  
  120. return table.concat(packet)
  121. end
  122.  
  123. HDL.send = function(packet)
  124. local client = socket.udp()
  125. client:sendto(packet, HDL.dstip, 6000)
  126. end
  127.  
  128. HDL.chanreg = function(dstsubnet, dstdevice, chan, value, delay)
  129. local extra, packet
  130. delay = delay or 0
  131.  
  132. if type(value) == 'boolean' then
  133. value = value and 100 or 0
  134. end
  135.  
  136. extra = string.char(chan, value) .. HDL.word(delay)
  137. packet = HDL.encode(HDL.cmd.chanreg, dstsubnet, dstdevice, extra)
  138.  
  139. HDL.send(packet)
  140. 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.