Posts: 99
Threads: 39
Joined: Apr 2018
Reputation:
0
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
Posts: 429
Threads: 100
Joined: Jun 2015
Reputation:
45
here is the Artnet integration script:
Code: 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})
Posts: 99
Threads: 39
Joined: Apr 2018
Reputation:
0
11.05.2018, 13:20
(This post was last modified: 11.05.2018, 14:16 by sjfp.)
WOW
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.
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
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: 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 })
Posts: 1764
Threads: 6
Joined: Jul 2015
Reputation:
117
Hi,
Nice (:
Is there also a handler script so we can use it as a server?
BR,
Erwin
Posts: 99
Threads: 39
Joined: Apr 2018
Reputation:
0
14.05.2018, 10:26
(This post was last modified: 14.05.2018, 10:45 by sjfp.)
(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: 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: 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
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
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?
Posts: 1764
Threads: 6
Joined: Jul 2015
Reputation:
117
14.05.2018, 19:56
(This post was last modified: 14.05.2018, 20:03 by Erwin van der Zwart.)
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
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
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: 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
Posts: 99
Threads: 39
Joined: Apr 2018
Reputation:
0
(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.
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
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.
Posts: 99
Threads: 39
Joined: Apr 2018
Reputation:
0
(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.
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
Resident script, sleep time = 0. Might require some change to work with C-BUS objects.
Code: 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()
Posts: 99
Threads: 39
Joined: Apr 2018
Reputation:
0
(23.05.2018, 08:35)admin Wrote: Resident script, sleep time = 0. Might require some change to work with C-BUS objects.
Code: 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.
Posts: 16
Threads: 1
Joined: Feb 2019
Reputation:
0
03.02.2020, 03:20
(This post was last modified: 03.02.2020, 03:23 by lcrowhurst.
Edit Reason: Was blank
)
(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: 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
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
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
Posts: 16
Threads: 1
Joined: Feb 2019
Reputation:
0
(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 ?
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
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: 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
Posts: 16
Threads: 1
Joined: Feb 2019
Reputation:
0
(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: 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?
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
You can run the sending script on the same device. Just set IP to 127.0.0.1
|