Tag Archives: DMX

DMX LogicMachine Lights user library

I have been optimizing my DMX scripts for the LogicMachine. The following is a set of functions that will enable you to turn a set of DMX channels on/off.


-- set of variables for zones in the apartment
 alllights = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24}

 bathroomlights = {7,8,20}
 livinglights = {5,6,9,10,11,12,14,15,16,17,18,19}
 kitchenlights = {13}
 bedroomlights = {1,2}
 entrancelights = {4}

-- turns DMX stripe to full on or off based on two groups
function dmxtoggle(channel, state)
 -- group that contains the value for ON
 on = grp.getvalue('5/7/17')
 -- group that contains the value for OFF
 off = grp.getvalue('5/7/18')
 if (state == true) then
  DMX.set(channel, on)
 else
  DMX.set(channel, off)
 end
end

-- loops through all lights
-- lights is an array of the channel(s) to be set
-- state is the state of the group
function dmxlooptoggle(lights, state)
 for index, value in ipairs(lights) do
  dmxtoggle(value, state)
 end
end

--Loops through an array of channels and sets them to a value
function dmxloopset(lights, value)
 for index, channel in ipairs(lights) do
  DMX.set(channel, value)
 end
end

To use it, create a user library, I called it mydmx. Then copy/paste the above code and change the variables to the light groups you want to switch together.

To then run it use the following code. The first line enables the above user library. Then the dmxlooptoggle is called to change the bathroom light to the value of the group (on/off). the last code updates a group that holds the status object (on/off).


require('user.mydmx')
dmxlooptoggle(bathroomlights, event.getvalue())
grp.write('5/7/24', event.getvalue())

DMX dimming using the Logic Machine

The following script enables you to dim all channels monitoring a KNX group. The code assumes you have created dummy objects (starting 10/0/1) in the Logic Machine for each DMX dimmer channel. That way we can visualize the status of the dimmer and recall the values from other scripts.

Depending on the group value of the dimming group the DMX full on value gets edited accordingly.

I have commented out the logging of each dimming step, but if you are debugging, that might be helpful.

-- This software may be used and distributed according to the terms of the -- GNU General Public License version 3, incorporated herein by reference. -- config variables dmxchan = 12 -- number of max DMX channels to change dimprefix = '10/0/' -- for dummy groups dimgroup = '1/0/1' -- group that dims all dmxon = 255 dmxoff = 0 -- end config variables -- percentage up/down values of the 03.007 KNX value local percentages = {1,1,0.5,0.25,0.12,0.06,0.03,0.01,1,1,0.5,0.25,0.12,0.06,0.03,0.01} -- read value as of moment of change value = grp.getvalue(dimgroup) -- define the up down as a percentage of the full on value dimstep = math.floor(dmxon * percentages[value]) while (value >= 9 and value < =15) do -- Dimming UP for i = 1, dmxchan, 1 do -- I have created dummy objects in the LM to store the dimming value of each channel. 10/0/x DMXvalue = grp.getvalue(dimprefix .. i) if (DMXvalue >= 0 and DMXvalue < = 255) then DMXvalue = DMXvalue + dimstep if (DMXvalue > 255) then DMXvalue = dmxon end else DMXvalue = dmxon end DMX.set(i, DMXvalue) -- update the dimmer status group for specific channel grp.update(dimprefix .. i, DMXvalue) end -- update the Status group for all Dimmers, helps for visualizations grp.update(dimprefix .. 0, DMXvalue) -- give the device enough time to process os.sleep(0.1) value = grp.getvalue(dimgroup) --log('Dimm up ' .. DMXvalue) end while (value >= 1 and value < = 7) do -- Dimming Down for i = 1, dmxchan, 1 do DMXvalue = grp.getvalue(dimprefix .. i) if (DMXvalue >= 0 and DMXvalue < = 255) then DMXvalue = DMXvalue - dimstep if (DMXvalue < 0) then DMXvalue = dmxoff end else DMXvalue = dmxoff end DMX.set(i, DMXvalue) -- update the dimmer status group for specific channel grp.update(dimprefix .. i, DMXvalue) end -- update the Status group for all Dimmers grp.update(dimprefix .. 0, DMXvalue) -- give the device enough time to process os.sleep(0.1) value = grp.getvalue(dimgroup) --log('Dimm down ' .. DMXvalue) end [/prettify]

The screenshot shows you how I added the status into my visualization.
Logic Machine DMX Dimming visualization object

Here you see the dummy objects I created.
Logic Machine DMX Dimming dummy objects

CONTROL DIMMING (4-bit)
9 10 11 12 13 14 15 8 0 7 6 5 4 3 2 1
100% Increase 50% Increase 25% Increase 12% Increase 6% Increase 3% Increase 1% Increase Stop Stop 1% Decrease 3% Increase 6% Increase 12% Increase 25% Increase 50% Increase 100% Increase

Turn all DMX LED lights on/off using the Logic Machine reactor

We have quite a few DMX based LED lighting sources. I use the eldoled lineardrive 720D to do the controling part. The following script enables me to turn the LEDs on/off, all at once.



-- This software may be used and distributed according to the terms of the
-- GNU General Public License version 3, incorporated herein by reference.

-- define group address
dmxchan = 12
dmxon = 255
dmxoff = 0
-- address of all on/off group
groupvalue = '1/2/0'

-- addresses of groups that should be switched including the all on/off group
lights = {'1/2/0', '1/1/0', '1/1/2', '1/1/3', '1/1/4', '1/1/5', '1/1/6', '1/1/7', '1/1/8'}

-- get value of object with group address
value = grp.getvalue(groupvalue)

if (value == false) then
  -- channel value set to 255
  for i = 1, dmxchan, 1 do
    DMX.set(i, dmxon)
  end
  -- change groups to true
  for index, value in ipairs(lights) do
    grp.write(value, true)
  end

  log('Light on')
else
  -- channel 1 value set to 0
  for i = 1, dmxchan, 1 do
    DMX.set(i, dmxoff)
  end
  
  -- change groups to false
  for index, value in ipairs(lights) do
    grp.write(value, false)
  end
     
  log('Light off')
end

I created an “event based script” in the Logic Machine to add this code. The address I monitor is 1/1/1 which is the group that turns all lights on/off in the KNX push buttons.

Logic Machine - group address for DMX

DMX on the Logic Machine

To get the DMX control of the Logic Machine one needs to get two scripts running. The first is a “handler” that needs to be added as a resident script:

Logic Machine - DMX handler resident script



-- This software may be used and distributed according to the terms of the
-- GNU General Public License version 3, incorporated herein by reference.

if not d then
  d = DMX:init({
    channels = 16,
    transition = 1,
  })
  log('DMX initialized')

-- reset custom DMX RGB color settings to "off" (0), comment out if not needed
  storage.set('DMXrgbred', 0)
  storage.set('DMXrgbgreen', 0)
  storage.set('DMXrgbblue', 0)
  log('Reset RGB colors')
end

d:run()

and the second are the common functions, that you can just add to the end of the current common functions.



-- This code is copyright of openrb.com
-- Open Source but != free 

DMX = {
  -- default params
  defaults = {
    -- storage key
    skey = 'dmx_chan_',
    -- RS-485 port
    port = '/dev/ttyAPP2',
    -- number of calls per second
    resolution = 20,
    -- total number of channels to use
    channels = 16,
    -- transition time in seconds, does not include DMX transfer time
    transition = 2,
  },
  -- value setter
  set = function(i, v)
    -- validate channel number
    if type(i) == 'number' and i >= 1 and i < = 512 then
      -- validate channel value
      if type(v) == 'number' and v >= 0 and v < = 255 then
        storage.set(DMX.defaults.skey .. i, v)
      end
    end
  end
}

-- DMX init, returns new DMX object
function DMX:init(params)
  log('starting DMX')
  require('luadmx')

  local n = setmetatable({}, { __index = DMX })
  local k, v

  -- set user parameters
  n.params = params

  -- copy parameters that are set by user
  for k, v in pairs(DMX.defaults) do
    if n.params&#91; k &#93; == nil then
      n.params&#91; k &#93; = v
    end
  end

  n:reset()

  return n
end

function DMX:reset()
  local err, chan
  log('resetting DMX')
  self.dm, err = luadmx.open(self.params.port)

    -- error while opening
  if err then
    os.sleep(1)
    error(err)
  end

  -- set channel count
  self.dm:setcount(self.params.channels)

  -- number of transaction ticks
  self.ticks = math.max(1, self.params.transition * self.params.resolution)

  -- calculate sleep time
  self.sleep = 1 / self.params.resolution

  -- reset channel map
  self.channels = {}

  -- fill channel map
  for chan = 1, self.params.channels do
    self.channels&#91; chan &#93; = { current = 0, target = 0, ticks = 0 }

    -- turn off by default
    storage.set(self.params.skey .. chan, 0)
    self.dm:setchannel(chan, 0)
  end
end

-- get new values
function DMX:getvalues()
  local chan, val
  --log('getting DMX values')
      
  -- check for new values for each channel
  for chan = 1, self.params.channels do
    val = storage.get(self.params.skey .. chan)

    -- target value differs, set transcation
    if val ~= self.channels&#91; chan &#93;.target then
      self.channels&#91; chan &#93;.target = val
      self.channels&#91; chan &#93;.delta = (self.channels&#91; chan &#93;.target - self.channels&#91; chan &#93;.current) / self.ticks
      self.channels&#91; chan &#93;.ticks = self.ticks
    end
  end
end

-- main loop handler
function DMX:run()
  local i, bs, bm, as, am, delta
  local res = self.params.resolution

  if not self.calibrated then
    bs, bm = os.microtime()
  end
    
  self:getvalues()

  -- transition loop
  for i = 1, res do
    self:step()
    self.dm:send()

    -- wait until next step
    os.sleep(self.sleep)
  end

  -- calibrate delay loop to match 1 second
  if not self.calibrated then
    as, am = os.microtime()
    delta = (as - bs) + (am - bm) / 1000000

    if delta > 1.05 then
      self.sleep = self.sleep - math.max(10, self.sleep / res)
    else
      self.calibrated = true
    end
  end
end

-- single transition step
function DMX:step()
  local chan, t

  -- transition for each channel
  for chan = 1, self.params.channels do
    t = self.channels[ chan ].ticks

    -- transition is active
    if t > 0 then
      t = t - 1

      self.channels[ chan ].current = self.channels[ chan ].target - self.channels[ chan ].delta * t
      self.channels[ chan ].ticks = t

      self.dm:setchannel(chan, self.channels[ chan ].current)
    end
  end
end