Tag Archives: Logic Machine

LogicMachine telnet user script

The following code enables you to open a telnet connection and send commands. I use it to connect to my Pioneer sound system. Add it to a user script.

telnet-lm

-- send Telnet command

function telnet(host, port, command)
local socket = require("socket")
local tcp = assert(socket.tcp())
res, err = tcp:connect(host, port)
res, err = tcp:send(command)

while true do
local s, status, threebytes = tcp:receive(3)
if status == "closed" then break end
end
tcp:close()
end

Usage

All you need to do now is call the function from your KNX-Object script. Example:

require('user.telnet')
telnet('192.168.0.24', 8102, 'PF\r\n\r\nclose\r\n\r\n')

The PF command tells my Pioneer to turn off. I’m not sure if a close command is necessary, but all the returns are. The IP address and port have to be changed to what you need.

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

Logic Machine re:actor videos

I bought a Logic Machine from openrb.com a few weeks ago. At about 1.100€ it is not very cheap, but it contains quite a few features that, if bought separately, would have been a lot more expensive. I am going to use it in our flat to do the following:

  1. as a KNX gateway (separate for at least 400€)

  2. DMX or DALI controller (separate for at least 600€)

  3. Home automation visualization (PC and Touch, go for about 1.700€)

  4. Logging

  5. Logic

  6. Digital/Analog inputs and outputs

Nice to have is the enocean chip. If for any reason I need to add wireless sensors or actuators, this will help a lot.

Right now it is not connected to the KNX bus, but I imported a few objects from the ETS4 software (OPC export). Then I used Sweet Home 3D to create a nice 3D view of the flat. Using GIMP I created some nice transparent lighting elements.

To see what I then did with the re:actor check out the following two videos: