This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

Art-Net DMX
#1
Hi Just asking if anyone has any information using Art-Net DMX  IP.
We are looking to use the C-Bus NAC, (Similar to LM) 
Art-Net is a protocol to control DMX lighting over IP Networks. 

Any example on how to implement would be grateful, say like send channel 1 to 255 or channel 2 to 125 on Module IP 192.168.2.43
Reply
#2
here is the Artnet integration script:

Code:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
require("socket") artnet_id = "Art-net" .. string.char(0)  --8 byte op_codes = {  ARTNET_POLL = 0x2000,  ARTNET_REPLY = 0x2100,  ARTNET_DMX = 0x5000,  ARTNET_ADDRESS = 0x6000,  ARTNET_INPUT = 0x7000,  ARTNET_TODREQUEST = 0x8000,  ARTNET_TODDATA = 0x8100,  ARTNET_TODCONTROL = 0x8200,  ARTNET_RDM = 0x8300,  ARTNET_VIDEOSTEUP = 0xa010,  ARTNET_VIDEOPALETTE = 0xa020,  ARTNET_VIDEODATA = 0xa040,  ARTNET_MACMASTER = 0xf000,  ARTNET_MACSLAVE = 0xf100,  ARTNET_FIRMWAREMASTER = 0xf200,  ARTNET_FIRMWAREREPLY = 0xf300,  ARTNET_IPPROG = 0xf800,  ARTNET_IPREPLY = 0xf900,  ARTNET_MEDIA = 0x9000,  ARTNET_MEDIAPATCH = 0x9200,  ARTNET_MEDIACONTROLREPLY = 0x9300 } protocol_version = {  high = 0,  low = 14 } priority_codes = {  DpLow = 0x10,  DpMed = 0x40,  DpHigh = 0x80,  DpCritical = 0xE0,  DpVolatile = 0xF0 } function op_code(name)  local code = op_codes[name]  local low = bit.band(code, 0x00FF)  local high = bit.rshift(code, 8)  return low, high end function artnet_poll(address, port, talk_to_me, priority)  local command = {}  local opCodeLow, opCodeHigh = op_code("ARTNET_POLL")  table.insert(command, opCodeLow)  table.insert(command, opCodeHigh)  table.insert(command, protocol_version.low)  table.insert(command, protocol_version.high)  table.insert(command, talk_to_me)  table.insert(command, priority_codes[priority])  command = artnet_id .. string.char(unpack(command))  local s = socket.udp()  s:sendto(command, address, port)  s:close() end function artnet_dmx(address, port, sequence, in_port_physical, in_port_address, data)  local command = {}  local opCodeLow, opCodeHigh = op_code("ARTNET_DMX")  table.insert(command, opCodeLow)  table.insert(command, opCodeHigh)  table.insert(command, protocol_version.low)  table.insert(command, protocol_version.high) --[[ The sequence number is used to ensure that ArtDmx packets are used in the correct order. When Art-Net is carried over a medium such as the Internet, it is possible that ArtDmx packets will reach the receiver out of order. This field is incremented in the range 0x01 to 0xff to allow the receiving node to resequence packets. The Sequence field is set to 0x00 to disable this feature ]]  sequence = bit.band(sequence, 0xFF)  table.insert(command, sequence) --[[ The physical input port from which DMX512 data was input. This field is for information only. Use Universe for data routing. ]]  table.insert(command, in_port_physical) --15 bit Port-Address (universe)  local address_hi = bit.rshift(in_port_address, 8)  local address_low = bit.band(in_port_address, 0x00FF)  table.insert(command, address_hi)  table.insert(command, address_low)  --Length  local l = #data --2 byte  local LengthHi = bit.rshift(l, 8)  local LengthLow = bit.band(l, 0x00FF)  table.insert(command, LengthHi)  table.insert(command, LengthLow)  command = artnet_id .. string.char(unpack(command)) .. string.char(unpack(data))  local s = socket.udp()  s:sendto(command, address, port)  s:close() end artnet_dmx(ip, port, 0, 0x12, 0x93, {0xA, 0x12, 0xFF})
Reply
#3
WOW Smile
Thank you, will give it go now.

Sorry for my ignorance, new to LUA, could you explain how to deploy this.
I have a group 0/56/0 to control channel 1 on DMX at 192.168.2.43
Where do I put the script, I assume it goes in the User Library and is then called from an event based script.
Where do I put the DMX Channel No, IP Address, and Port Number.

Again, Sorry if I am being completely stupid.
Reply
#4
Here's a simplified function. Change universe number as needed, sequence and physical parameters can probably be zero. Value is the output channel value ranging from 0 to 255.

Code:
123456789101112131415161718192021222324252627282930
function artnet_dmx(ip, sequence, physical, universe, data)  local cmd = string.char(    -- null byte    0,    -- ARTNET_DMX opcope    0, 0x50,    -- protocol version 14    0, 14,    -- sequence number is used to ensure that ArtDmx packets are used in the correct order    bit.band(sequence, 0xFF),    -- physical input port from which DMX512 data was input.    bit.band(physical, 0xFF),    -- 15 bit Port-Address (universe)    bit.band(universe, 0xFF), bit.rshift(universe, 8),    -- Length    bit.rshift(#data, 8), bit.band(#data, 0xFF)  )  local req = "Art-Net" .. cmd .. string.char(unpack(data))  local sck = require("socket").udp()  sck:sendto(req, ip, 0x1936)  sck:close() end ip = '192.168.2.43' universe = 123 value = 255 artnet_dmx(ip, 0, 0, universe, { value })
Reply
#5
Hi,

Nice (: 

Is there also a handler script so we can use it as a server?

BR,

Erwin
Reply
#6
(14.05.2018, 06:28)admin Wrote: Here's a simplified function. Change universe number as needed, sequence and physical parameters can probably be zero. Value is the output channel value ranging from 0 to 255.

Code:
123456789101112131415161718192021222324252627282930
function artnet_dmx(ip, sequence, physical, universe, data)  local cmd = string.char(    -- null byte    0,    -- ARTNET_DMX opcope    0, 0x50,    -- protocol version 14    0, 14,    -- sequence number is used to ensure that ArtDmx packets are used in the correct order    bit.band(sequence, 0xFF),    -- physical input port from which DMX512 data was input.    bit.band(physical, 0xFF),    -- 15 bit Port-Address (universe)    bit.band(universe, 0xFF), bit.rshift(universe, 8),    -- Length    bit.rshift(#data, 8), bit.band(#data, 0xFF)  )  local req = "Art-Net" .. cmd .. string.char(unpack(data))  local sck = require("socket").udp()  sck:sendto(req, ip, 0x1936)  sck:close() end ip = '192.168.2.43' universe = 123 value = 255 artnet_dmx(ip, 0, 0, universe, { value })
Many thanks for the details. Works great. What do I need to change to control each DMX 512 Channels

(14.05.2018, 10:26)sjfp Wrote:
(14.05.2018, 06:28)admin Wrote: Here's a simplified function. Change universe number as needed, sequence and physical parameters can probably be zero. Value is the output channel value ranging from 0 to 255.

Code:
123456789101112131415161718192021222324252627282930
function artnet_dmx(ip, sequence, physical, universe, data)  local cmd = string.char(    -- null byte    0,    -- ARTNET_DMX opcope    0, 0x50,    -- protocol version 14    0, 14,    -- sequence number is used to ensure that ArtDmx packets are used in the correct order    bit.band(sequence, 0xFF),    -- physical input port from which DMX512 data was input.    bit.band(physical, 0xFF),    -- 15 bit Port-Address (universe)    bit.band(universe, 0xFF), bit.rshift(universe, 8),    -- Length    bit.rshift(#data, 8), bit.band(#data, 0xFF)  )  local req = "Art-Net" .. cmd .. string.char(unpack(data))  local sck = require("socket").udp()  sck:sendto(req, ip, 0x1936)  sck:close() end ip = '192.168.2.43' universe = 123 value = 255 artnet_dmx(ip, 0, 0, universe, { value })
Many thanks for the details. Works great. What do I need to change to control each DMX 512 Channels
Found how. {value1,value2,value3) 1= Ch1 2=Ch2 3=Ch3 etc etc.
But how can I can change channels independently without affecting other channels that may of been set
Reply
#7
You can only write full dmx-512 value array starting with the first address. So for several channels you need a script attached to all controlling groups which will write all required values.

@Erwin, it's possible to implement a simple server which only handles DMX commands. Do you have real use cases for that?
Reply
#8
Hi Admin,

We get the request for Art-Net a few times a year, often for theather and cinema projects.

In most cases there is a main DMX control in the area where they want to control the KNX lights from there main system. In theater it’s mostly the stage cleaning and blue lights and backstage area ligths and often screens for windows between public area and stage. They want to control it often from the control room’s dmx panels.

At this moment we refer to dedicated bidirectional DMX IP gateways to do this task but if we can do it by scripting i would like to run some tests to see how it performs.

PS: All new Dolby Atmos theathers in the NL are controlled by Wiser for KNX at this moment and they control quite some DMX loops for user experience in coridors (:

BR,

Erwin
Reply
#9
Here's a rudimentary server example (resident script, sleep time = 0). Only DMX command is supported, sequences are not supported, universe number is ignored.

Code:
12345678910111213141516171819202122232425262728293031323334
if not server then  mapping = { '32/1/1', '32/1/2', '32/1/3' }  cache = {}  header = 'Art-Net' .. string.char(0, 0, 0x50, 0, 14)  function parse(data)    if #data < 19 then      return    end    if data:sub(1, #header) ~= header then      return    end    local values = data:sub(19)    for i = 1, #values do      local value = values:byte(i)      if mapping[ i ] and cache[ i ] ~= value then        cache[ i ] = value        grp.write(mapping[ i ], value, dt.uint8)      end    end  end  server = require('socket').udp()  server:settimeout(3)  server:setsockname('*', 0x1936) end data, err = server:receive() if data then  parse(data) end
Reply
#10
(14.05.2018, 11:02)admin Wrote: You can only write full dmx-512 value array starting with the first address. So for several channels you need a script attached to all controlling groups which will write all required values.

@Erwin, it's possible to implement a simple server which only handles DMX commands. Do you have real use cases for that?

Thanks for the advise.
I have been looking on how to write that script you mentioned, I have it working in a fashion, but would appreciate to some advise on the best way to achieve logging all the groups.
I have started off by creating a table to store each groups current val, but wondering how to extract all the data that's contained in the table.

I have it working by bring back individual stored values, but cant find the correct way to bring back all stored in a format like
{200,50,0,255,127,100,255,0,0,0,255,0} etc. etc.

any help would be appreciated.
Reply
#11
If you have many groups to control you should use a resident script which monitors all bus traffic and sends update once any group changes. I'll provide an example later on.
Reply
#12
(22.05.2018, 06:00)admin Wrote: If you have many groups to control you should use a resident script which monitors all bus traffic and sends update once any group changes. I'll provide an example later on.

Thank you. Looking forward to seeing your example. Really appreciate your help.
Reply
#13
Resident script, sleep time = 0. Might require some change to work with C-BUS objects.

Code:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
if not client then   ip = '192.168.2.43'   mapping = { '1/1/1', '1/1/2', '1/1/3', '2/1/1', '2/1/2', '2/1/3' }   universe = 1   physical = 0   udpsock = require('socket').udp()   addrtoid = {}   values = {}   for id, addr in ipairs(mapping) do     obj = grp.find(addr)     addrtoid[ addr ] = id     if obj then       value = obj.value or 0       if obj.datatype == dt.scale then         value = math.floor(value * 2.55)       end     else       value = 0     end     values[ id ] = value   end   function send()     local cmd = string.char(       -- null byte; ARTNET_DMX opcope; protocol version 14, sequence not defined       0, 0, 0x50, 0, 14, 0,       -- physical input port from which DMX512 data was input       bit.band(physical, 0xFF),       -- 15 bit Port-Address (universe)       bit.band(universe, 0xFF), bit.rshift(universe, 8),       -- Length       bit.rshift(#values, 8), bit.band(#values, 0xFF)     )     local req = 'Art-Net' .. cmd .. string.char(unpack(values))     udpsock:sendto(req, ip, 0x1936)   end   function eventhandler(event)     local id = addrtoid[ event.dst ]     if id then       values[ id ] = busdatatype.decode(event.datahex, dt.uint8, event.dstraw)       send()     end   end   client = require('localbus').new()   client:sethandler('groupwrite', eventhandler)   send() end client:step()
Reply
#14
(23.05.2018, 08:35)admin Wrote: Resident script, sleep time = 0. Might require some change to work with C-BUS objects.

Code:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
if not client then  ip = '192.168.2.43'  mapping = { '1/1/1', '1/1/2', '1/1/3', '2/1/1', '2/1/2', '2/1/3' }  universe = 1  physical = 0  udpsock = require('socket').udp()  addrtoid = {}  values = {}  for id, addr in ipairs(mapping) do    obj = grp.find(addr)    addrtoid[ addr ] = id    if obj then      value = obj.value or 0      if obj.datatype == dt.scale then        value = math.floor(value * 2.55)      end    else      value = 0    end    values[ id ] = value  end  function send()    local cmd = string.char(      -- null byte; ARTNET_DMX opcope; protocol version 14, sequence not defined      0, 0, 0x50, 0, 14, 0,      -- physical input port from which DMX512 data was input      bit.band(physical, 0xFF),      -- 15 bit Port-Address (universe)      bit.band(universe, 0xFF), bit.rshift(universe, 8),      -- Length      bit.rshift(#values, 8), bit.band(#values, 0xFF)    )    local req = 'Art-Net' .. cmd .. string.char(unpack(values))    udpsock:sendto(req, ip, 0x1936)  end  function eventhandler(event)    local id = addrtoid[ event.dst ]    if id then      values[ id ] = busdatatype.decode(event.datahex, dt.uint8, event.dstraw)      send()    end  end  client = require('localbus').new()  client:sethandler('groupwrite', eventhandler)  send() end client:step()
Thank you for your assistance. Will have a go when i get back to the office tomorrow evening.
Reply
#15
(23.05.2018, 09:49)sjfp Wrote:
(23.05.2018, 08:35)admin Wrote: Resident script, sleep time = 0. Might require some change to work with C-BUS objects.

Code:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
if not client then  ip = '192.168.2.43'  mapping = { '1/1/1', '1/1/2', '1/1/3', '2/1/1', '2/1/2', '2/1/3' }  universe = 1  physical = 0  udpsock = require('socket').udp()  addrtoid = {}  values = {}  for id, addr in ipairs(mapping) do    obj = grp.find(addr)    addrtoid[ addr ] = id    if obj then      value = obj.value or 0      if obj.datatype == dt.scale then        value = math.floor(value * 2.55)      end    else      value = 0    end    values[ id ] = value  end  function send()    local cmd = string.char(      -- null byte; ARTNET_DMX opcope; protocol version 14, sequence not defined      0, 0, 0x50, 0, 14, 0,      -- physical input port from which DMX512 data was input      bit.band(physical, 0xFF),      -- 15 bit Port-Address (universe)      bit.band(universe, 0xFF), bit.rshift(universe, 8),      -- Length      bit.rshift(#values, 8), bit.band(#values, 0xFF)    )    local req = 'Art-Net' .. cmd .. string.char(unpack(values))    udpsock:sendto(req, ip, 0x1936)  end  function eventhandler(event)    local id = addrtoid[ event.dst ]    if id then      values[ id ] = busdatatype.decode(event.datahex, dt.uint8, event.dstraw)      send()    end  end  client = require('localbus').new()  client:sethandler('groupwrite', eventhandler)  send() end client:step()
Thank you for your assistance. Will have a go when i get back to the office tomorrow evening.

Did you get this solution to work. I have a similar requirement. I have a client with a DMX slider controller that they want to control Cbus controlled lights with. Where did you purchase a art-net interface from?

thanks
Reply
#16
This script sends Art-Net commands but you need a different script which receives Art-Net commands. This is doable but first you need a DMX > Art-Net converter. I think something like this should work: https://dmxking.com/artnetsacn/edmx1-pro
Reply
#17
(03.02.2020, 11:41)admin Wrote: This script sends Art-Net commands but you need a different script which receives Art-Net commands. This is doable but first you need a DMX > Art-Net converter. I think something like this should work: https://dmxking.com/artnetsacn/edmx1-pro

thanks for the info I will get intouch with them, how hard is it to write an art-net receiver ?
Reply
#18
An example of Art-Net receiver. It ignores sequence and universe numbers which are not needed for general use. Edit mapping table to select which groups addresses are mapped to DMX channel numbers starting from 1. Output value for each channel is 1 byte (0..255).

Code:
1234567891011121314151617181920212223242526272829303132333435363738394041
if not server then   mapping = {     [1] = '32/1/1',     [2] = '32/1/2',     [3] = '32/1/3',   }   server = require('socket').udp()   server:settimeout(1)   server:setsockname('*', 0x1936)   header = 'Art-Net' .. string.char(0, 0, 0x50)   values = {}   function parse(data)     local count, value     if data:sub(1, #header) ~= header then       return     end     count = #data - 18     for i = 1, count do       value = data:byte(i + 18)       if value ~= values[ i ] then         values[ i ] = value         if mapping[ i ] then           grp.update(mapping[ i ], value)         end       end     end   end end data = server:receive() if data then   parse(data) end
Reply
#19
(03.02.2020, 12:51)admin Wrote: An example of Art-Net receiver. It ignores sequence and universe numbers which are not needed for general use. Edit mapping table to select which groups addresses are mapped to DMX channel numbers starting from 1. Output value for each channel is 1 byte (0..255).

Code:
1234567891011121314151617181920212223242526272829303132333435363738394041
if not server then   mapping = {     [1] = '32/1/1',     [2] = '32/1/2',     [3] = '32/1/3',   }   server = require('socket').udp()   server:settimeout(1)   server:setsockname('*', 0x1936)   header = 'Art-Net' .. string.char(0, 0, 0x50)   values = {}   function parse(data)     local count, value     if data:sub(1, #header) ~= header then       return     end     count = #data - 18     for i = 1, count do       value = data:byte(i + 18)       if value ~= values[ i ] then         values[ i ] = value         if mapping[ i ] then           grp.update(mapping[ i ], value)         end       end     end   end end data = server:receive() if data then   parse(data) end
Thanks for the info, I’ve tried testing it with a couple of software controller, as I’m not on site, but the wiser only shows a connection to the controller (eg when I log server it shows art-net) but the data is alway nil. 

can I put the art-net transmit on the wiser and test that way?
Reply
#20
You can run the sending script on the same device. Just set IP to 127.0.0.1
Reply


Forum Jump: